Parse lambdas as functions (#1955)

* Parse lambdas as functions

* Fix issue with missing paren

* Fix error when parsing non-existent args

* Remove unused function variable
This commit is contained in:
Paul Fultz II 2019-07-05 05:30:42 -05:00 committed by Daniel Marjamäki
parent 2a17e624d9
commit e0ced1c415
5 changed files with 42 additions and 22 deletions

View File

@ -666,9 +666,9 @@ void SymbolDatabase::createSymbolDatabaseFindAllScopes()
tok = tok->linkAt(1);
} else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) {
const Token *lambdaStartToken = lambdaEndToken->link();
scopeList.emplace_back(this, tok, scope, Scope::eLambda, lambdaStartToken);
scope->nestedList.push_back(&scopeList.back());
scope = &scopeList.back();
const Token * argStart = lambdaStartToken->astParent();
const Token * funcStart = Token::simpleMatch(argStart, "[") ? argStart : argStart->astParent();
addGlobalFunction(scope, tok, argStart, funcStart);
tok = lambdaStartToken;
} else if (tok->str() == "{") {
if (isExecutableScope(tok)) {
@ -1732,6 +1732,10 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *s
type = Function::eOperatorEqual;
}
else if (tokenDef->str() == "[") {
type = Function::eLambda;
}
// class constructor/destructor
else if (tokenDef->str() == scope->className) {
// destructor
@ -1778,7 +1782,7 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *s
}
// find the return type
if (!isConstructor() && !isDestructor()) {
if (!isConstructor() && !isDestructor() && !isLambda()) {
// @todo auto type deduction should be checked
// @todo attributes and exception specification can also precede trailing return type
if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type
@ -2054,13 +2058,16 @@ const Token * Function::constructorMemberInitialization() const
Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart)
{
Function* function = nullptr;
for (std::multimap<std::string, const Function *>::iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) {
const Function *f = i->second;
if (f->hasBody())
continue;
if (Function::argsMatch(scope, f->argDef, argStart, emptyString, 0)) {
function = const_cast<Function *>(i->second);
break;
// Lambda functions are always unique
if (tok->str() != "[") {
for (std::multimap<std::string, const Function *>::iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) {
const Function *f = i->second;
if (f->hasBody())
continue;
if (Function::argsMatch(scope, f->argDef, argStart, emptyString, 0)) {
function = const_cast<Function *>(i->second);
break;
}
}
}
@ -3038,6 +3045,8 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *s
{
// check for non-empty argument list "( ... )"
const Token * start = arg ? arg : argDef;
if (!Token::simpleMatch(start, "("))
return;
if (!(start && start->link() != start->next() && !Token::simpleMatch(start, "( void )")))
return;
@ -3296,6 +3305,8 @@ Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *
enumClass = true;
nameTok = nameTok->next();
}
} else if (classDef->str() == "[") {
type = Scope::eLambda;
} else {
type = Scope::eFunction;
}

View File

@ -705,7 +705,7 @@ class CPPCHECKLIB Function {
}
public:
enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction };
enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction, eLambda };
Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef);
@ -731,6 +731,10 @@ public:
/** @brief get function in base class that is overridden */
const Function *getOverriddenFunction(bool *foundAllBaseClasses = nullptr) const;
bool isLambda() const {
return type==eLambda;
}
bool isConstructor() const {
return type==eConstructor ||
type==eCopyConstructor ||

View File

@ -955,6 +955,17 @@ const Token *Token::findmatch(const Token * const startTok, const char pattern[]
return nullptr;
}
void Token::function(const Function *f) {
mImpl->mFunction = f;
if (f) {
if (f->isLambda())
tokType(eLambda);
else
tokType(eFunction);
} else if (mTokType == eFunction)
tokType(eName);
}
void Token::insertToken(const std::string &tokenStr, const std::string &originalNameStr, bool prepend)
{
Token *newToken;

View File

@ -146,6 +146,7 @@ public:
eNumber, eString, eChar, eBoolean, eLiteral, eEnumerator, // Literals: Number, String, Character, Boolean, User defined literal (C++11), Enumerator
eArithmeticalOp, eComparisonOp, eAssignmentOp, eLogicalOp, eBitOp, eIncDecOp, eExtendedOp, // Operators: Arithmetical, Comparison, Assignment, Logical, Bitwise, ++/--, Extended
eBracket, // {, }, <, >: < and > only if link() is set. Otherwise they are comparison operators.
eLambda, // A function without a name
eOther,
eNone
};
@ -765,19 +766,13 @@ public:
* Associate this token with given function
* @param f Function to be associated
*/
void function(const Function *f) {
mImpl->mFunction = f;
if (f)
tokType(eFunction);
else if (mTokType == eFunction)
tokType(eName);
}
void function(const Function *f);
/**
* @return a pointer to the Function associated with this token.
*/
const Function *function() const {
return mTokType == eFunction ? mImpl->mFunction : nullptr;
return mTokType == eFunction || mTokType == eLambda ? mImpl->mFunction : nullptr;
}
/**

View File

@ -1438,14 +1438,13 @@ private:
"}\n");
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str());
// TODO: Variable is not set correctly for this case
check("auto f(int b) {\n"
" return [=](int a){\n"
" a += b;\n"
" return [&](){ return a; };\n"
" };\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", "", errout.str());
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str());
check("auto g(int& a) {\n"
" return [&](){ return a; };\n"