Fixed lots of bugs in the AST:

- Support new and delete
- Properly handle "..."
- References and rValue references as function parameters
- Destructor definitions
This commit is contained in:
PKEuS 2014-06-04 18:08:51 +02:00
parent 26e09ae4a4
commit 2455b76abd
2 changed files with 86 additions and 15 deletions

View File

@ -411,7 +411,7 @@ static bool iscast(const Token *tok)
tok2 = tok2->link()->next();
if (tok2->str() == ")")
return type || tok2->previous()->str() == "*" ||
return type || tok2->strAt(-1) == "*" ||
(Token::Match(tok2, ") %any%") &&
(tok2->strAt(1) == "&" || (!tok2->next()->isOp() && !Token::Match(tok2->next(), "[[]);,?:.]"))));
if (!Token::Match(tok2, "%var%|*|&|::"))
@ -476,7 +476,7 @@ static void compileTerm(Token *& tok, std::stack<Token*> &op, unsigned int depth
} else if (tok->str() == "return") {
compileUnaryOp(tok, compileExpression, op, depth);
op.pop();
} else if (tok->isName()) {
} else if (tok->isName() && (!Token::isCPP() || !Token::Match(tok, "new|delete")) && tok->str() != "case") {
while (tok->next() && tok->next()->isName())
tok = tok->next();
op.push(tok);
@ -494,10 +494,17 @@ static void compileScope(Token *&tok, std::stack<Token*> &op, unsigned int depth
compileTerm(tok,op, depth);
while (tok) {
if (tok->str() == "::") {
if (tok->previous() && tok->previous()->isName())
compileBinOp(tok, compileTerm, op, depth);
Token *binop = tok;
tok = tok->next();
if (tok && tok->str() == "~") // Jump over ~ of destructor definition
tok = tok->next();
if (tok)
compileTerm(tok, op, depth);
if (binop->previous() && binop->previous()->isName())
compileBinOp(binop, nullptr, op, depth);
else
compileUnaryOp(tok, compileTerm, op, depth);
compileUnaryOp(binop, nullptr, op, depth);
} else break;
}
}
@ -505,7 +512,7 @@ static void compileScope(Token *&tok, std::stack<Token*> &op, unsigned int depth
static bool isPrefixUnary(const Token* tok)
{
if (!tok->previous()
|| (Token::Match(tok->previous(), "(|[|{|%op%|;|}|?|:|,|.|return|throw")
|| ((Token::Match(tok->previous(), "(|[|{|%op%|;|}|?|:|,|.|return|::") || (Token::isCPP() && tok->strAt(-1) == "throw"))
&& (tok->previous()->type() != Token::eIncDecOp || tok->type() == Token::eIncDecOp)))
return true;
@ -519,7 +526,12 @@ static void compilePrecedence2(Token *&tok, std::stack<Token*> &op, unsigned int
if (tok->type() == Token::eIncDecOp && !isPrefixUnary(tok)) {
compileUnaryOp(tok, compileScope, op, depth);
} else if (tok->str() == "." && tok->strAt(1) != "*") {
compileBinOp(tok, compileScope, op, depth);
if (tok->strAt(1) == ".") {
op.push(tok);
tok = tok->tokAt(3);
break;
} else
compileBinOp(tok, compileScope, op, depth);
} else if (tok->str() == "[") {
if (isPrefixUnary(tok) && tok->link()->strAt(1) == "(") { // Lambda
// What we do here:
@ -536,21 +548,26 @@ static void compilePrecedence2(Token *&tok, std::stack<Token*> &op, unsigned int
tok = curlyBracket->link()->next();
} else {
Token* tok2 = tok;
compileBinOp(tok, compileExpression, op, depth);
if (tok->strAt(1) != "]")
compileBinOp(tok, compileExpression, op, depth);
else
compileUnaryOp(tok, compileExpression, op, depth);
tok = tok2->link()->next();
}
} else if (tok->str() == "(" && (!iscast(tok) || Token::Match(tok->previous(), "if|while|for|switch|catch"))) {
Token* tok2 = tok;
tok = tok->next();
bool opPrevTopSquare = !op.empty() && op.top() && op.top()->str() == "[";
std::size_t oldOpSize = op.size();
compileExpression(tok, op, depth);
bool operandInside = oldOpSize < op.size();
tok = tok2;
if ((tok->previous() && tok->previous()->isName() && !Token::Match(tok->previous(), "return|throw"))
|| tok->strAt(-1) == "]"
if ((tok->previous() && tok->previous()->isName() && (tok->strAt(-1) != "return" && (!Token::isCPP() || !Token::Match(tok->previous(), "throw|delete"))))
|| (tok->strAt(-1) == "]" && (!Token::isCPP() || !Token::Match(tok->linkAt(-1)->previous(), "new|delete")))
|| (tok->strAt(-1) == ">" && tok->linkAt(-1))
|| (tok->strAt(-1) == ")" && !iscast(tok->linkAt(-1))) // Don't treat brackets to clarify precedence as function calls
|| (tok->strAt(-1) == "}" && opPrevTopSquare)) {
if (tok->strAt(1) != ")")
if (operandInside)
compileBinOp(tok, 0, op, depth);
else
compileUnaryOp(tok, 0, op, depth);
@ -566,15 +583,40 @@ static void compilePrecedence3(Token *&tok, std::stack<Token*> &op, unsigned int
while (tok) {
if ((Token::Match(tok, "[+-!~*&]") || tok->type() == Token::eIncDecOp) &&
isPrefixUnary(tok)) {
if (Token::Match(tok, "* [*,)]")) {
Token* tok2 = tok;
while (tok2->next() && tok2->str() == "*")
tok2 = tok2->next();
if (Token::Match(tok2, "[>),]")) {
tok = tok2;
continue;
}
}
compileUnaryOp(tok, compilePrecedence3, op, depth);
} else if (tok->str() == "(" && iscast(tok)) {
Token* tok2 = tok;
tok = tok->link()->next();
compilePrecedence3(tok, op, depth);
compileUnaryOp(tok2, 0, op, depth);
} else if (Token::isCPP() && tok->str() == "new") {
Token* tok2 = tok;
tok = tok->next();
op.push(tok);
while (Token::Match(tok, "%var%|*|&|<|[")) {
if (tok->link())
tok = tok->link();
tok = tok->next();
}
compileUnaryOp(tok2, 0, op, depth);
} else if (Token::isCPP() && tok->str() == "delete") {
Token* tok2 = tok;
tok = tok->next();
if (tok->str() == "[")
tok = tok->link()->next();
compilePrecedence3(tok, op, depth);
compileUnaryOp(tok2, 0, op, depth);
}
// TODO: Handle sizeof, new and delete
// TODO: Handle sizeof
else break;
}
}
@ -598,7 +640,8 @@ static void compileMulDiv(Token *&tok, std::stack<Token*> &op, unsigned int dept
Token* tok2 = tok;
while (tok2->next() && tok2->str() == "*")
tok2 = tok2->next();
if (Token::Match(tok2, "[,)]")) {
if (Token::Match(tok2, "[>),]")) {
tok = tok2;
break;
}
}
@ -631,7 +674,7 @@ static void compileRelComp(Token *&tok, std::stack<Token*> &op, unsigned int dep
{
compileShift(tok, op, depth);
while (tok) {
if (Token::Match(tok, "<|<=|>=|>")) {
if (Token::Match(tok, "<|<=|>=|>") && !tok->link()) {
compileBinOp(tok, compileShift, op, depth);
} else break;
}
@ -652,6 +695,13 @@ static void compileAnd(Token *&tok, std::stack<Token*> &op, unsigned int depth)
compileEqComp(tok, op, depth);
while (tok) {
if (tok->str() == "&" && !tok->astOperand1()) {
Token* tok2 = tok->next();
if (tok2->str() == "&")
tok2 = tok2->next();
if (Token::isCPP() && (tok2->str() == "," || tok2->str() == ")")) {
tok = tok2;
break; // rValue reference
}
compileBinOp(tok, compileEqComp, op, depth);
} else break;
}

View File

@ -582,6 +582,7 @@ private:
// AST data
TEST_CASE(astexpr);
TEST_CASE(astpar);
TEST_CASE(astnewdelete);
TEST_CASE(astbrackets);
TEST_CASE(astunaryop);
TEST_CASE(astfunction);
@ -10500,6 +10501,23 @@ private:
ASSERT_EQUALS("abc.1:?1+bd.1:?+=", testAst("a =(b.c ? : 1) + 1 + (b.d ? : 1);"));
ASSERT_EQUALS("catch.(", testAst("try {} catch (...) {}"));
ASSERT_EQUALS("FooBar(", testAst("void Foo(Bar&);"));
ASSERT_EQUALS("FooBar(", testAst("void Foo(Bar& &);")); // Rvalue reference - simplified from && to & & by real tokenizer
ASSERT_EQUALS("DerivedDerived::(", testAst("Derived::~Derived() {}"));
}
void astnewdelete() const {
ASSERT_EQUALS("aintnew=", testAst("a = new int;"));
ASSERT_EQUALS("aintnew=", testAst("a = new int[4];"));
ASSERT_EQUALS("aFoonew=", testAst("a = new Foo(bar);"));
ASSERT_EQUALS("aFoonew=", testAst("a = new Foo<bar>();"));
ASSERT_EQUALS("adelete", testAst("delete a;"));
ASSERT_EQUALS("adelete", testAst("delete (a);"));
ASSERT_EQUALS("adelete", testAst("delete[] a;"));
ASSERT_EQUALS("ab.3c-(delete", testAst("delete[] a.b(3 - c);"));
}
void astpar() const { // parentheses
@ -10550,6 +10568,9 @@ private:
ASSERT_EQUALS("QT_WA{{,( QT_WA{{,( x1=",
testAst("QT_WA({},{x=0;});" // don't hang
"QT_WA({x=1;},{x=2;});"));
// function pointer
TODO_ASSERT_EQUALS("todo", "va_argapvoid((,(*0=", testAst("*va_arg(ap, void(**) ()) = 0;"));
}
void astbrackets() const { // []