diff --git a/lib/token.cpp b/lib/token.cpp index eac69decee7..a224915b1fe 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -2683,8 +2683,10 @@ bool Token::Impl::getCppcheckAttribute(CppcheckAttributesType attrType, MathLib: Token* findTypeEnd(Token* tok) { while (Token::Match(tok, "%name%|.|::|*|&|&&|<|(|template|decltype|sizeof")) { - if (Token::Match(tok, "(|<")) + if (tok->str() == "(") tok = tok->link(); + else if (tok->str() == "<") // template brackets are not linked before Tokenizer::createLinks2() + tok = tok->link() ? tok->link() : tok->findClosingBracket(); if (!tok) return nullptr; tok = tok->next(); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 7f92d66eb78..0b4de780642 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4763,6 +4763,24 @@ static const std::unordered_set notstart_cpp = { NOTSTART_C, "delete", "friend", "new", "throw", "using", "virtual", "explicit", "const_cast", "dynamic_cast", "reinterpret_cast", "static_cast", "template" }; +// Returns the end of the lambda that starts at tok in a constructor initializer list, or nullptr +static const Token* findInitListLambdaEnd(const Token* tok) +{ + if (!Token::simpleMatch(tok, "[") || Token::Match(tok->previous(), "%name%|)|]|>")) + return nullptr; // array subscript or array size of a new expression + // array size of a new expression with pointer or reference type: new T*[n]{...} + for (const Token* prev = tok->previous(); Token::Match(prev, "*|&|&&|::|%name%|>"); prev = prev->previous()) { + if (prev->str() == "new") + return nullptr; + if (prev->str() == ">") { + prev = prev->findOpeningBracket(); + if (!prev) + break; + } + } + return findLambdaEndScope(tok); +} + void Tokenizer::setVarIdPass1() { const bool cpp = isCPP(); @@ -4778,6 +4796,7 @@ void Tokenizer::setVarIdPass1() std::stack functionDeclEndStack; const Token *functionDeclEndToken = nullptr; bool initlist = false; + std::stack initlistLambdaEnds; // ends of lambdas in constructor initializer lists bool inlineFunction = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isOp()) @@ -4813,6 +4832,13 @@ void Tokenizer::setVarIdPass1() variableMap.enterScope(); } } + } else if (const Token* lambdaEnd = initlist ? findInitListLambdaEnd(tok) : nullptr) { + // lambda in initializer list: parse it like a lambda in executable code, the + // extra scope holds its parameters and is left at the end of the lambda + initlistLambdaEnds.push(lambdaEnd); + scopeStack.emplace(/*isExecutable=*/ true, /*isStructInit=*/ false, /*isEnum=*/ false, variableMap.getVarId()); + variableMap.enterScope(); + initlist = false; } else if (!initlist && tok->str()=="(") { const Token * newFunctionDeclEnd = nullptr; if (!scopeStack.top().isExecutable) @@ -4903,6 +4929,14 @@ void Tokenizer::setVarIdPass1() } } } + + if (!initlistLambdaEnds.empty() && initlistLambdaEnds.top() == tok) { + // end of lambda in initializer list + initlistLambdaEnds.pop(); + scopeStack.pop(); + variableMap.leaveScope(); + initlist = true; + } } if ((!scopeStack.top().isStructInit && diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 4186917cc35..dbff0ed160a 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -161,6 +161,7 @@ class TestVarID : public TestFixture { TEST_CASE(varid_initList); TEST_CASE(varid_initListWithBaseTemplate); TEST_CASE(varid_initListWithScope); + TEST_CASE(varid_initListWithLambda); TEST_CASE(varid_operator); TEST_CASE(varid_throw); TEST_CASE(varid_unknown_macro); // #2638 - unknown macro is not type @@ -2789,6 +2790,66 @@ class TestVarID : public TestFixture { tokenize(code1)); } + void varid_initListWithLambda() { + const char code1[] = "struct S {\n" + " int x;\n" + " int* p;\n" + " S(int* p) : x([p] { return *p; }()), p(p) {}\n" + " S(int* p, int* q) : x([p](int* q) { return *p + *q; }(q)), p(q) {}\n" + " S(int* p, char) : x([p]() noexcept { int v = *p; return v; }()), p{p} {}\n" + "};\n" + "struct T {\n" + " int* p;\n" + " int g();\n" + "};\n" + "int T::g() { return *p; }\n"; + ASSERT_EQUALS("1: struct S {\n" + "2: int x@1 ;\n" + "3: int * p@2 ;\n" + "4: S ( int * p@3 ) : x@1 ( [ p@3 ] { return * p@3 ; } ( ) ) , p@2 ( p@3 ) { }\n" + "5: S ( int * p@4 , int * q@5 ) : x@1 ( [ p@4 ] ( int * q@6 ) { return * p@4 + * q@6 ; } ( q@5 ) ) , p@2 ( q@5 ) { }\n" + "6: S ( int * p@7 , char ) : x@1 ( [ p@7 ] ( ) noexcept ( true ) { int v@8 ; v@8 = * p@7 ; return v@8 ; } ( ) ) , p@2 { p@7 } { }\n" + "7: } ;\n" + "8: struct T {\n" + "9: int * p@9 ;\n" + "10: int g ( ) ;\n" + "11: } ;\n" + "12: int T :: g ( ) { return * p@9 ; }\n", + tokenize(code1)); + + const char code2[] = "struct S {\n" + " int x;\n" + " int* p;\n" + " S(int* p) : x([p]() -> std::map { return {{*p, 1}}; }().size()), p(p) {}\n" + "};\n"; + ASSERT_EQUALS("1: struct S {\n" + "2: int x@1 ;\n" + "3: int * p@2 ;\n" + "4: S ( int * p@3 ) : x@1 ( [ p@3 ] ( ) . std :: map < int , int > { return { { * p@3 , 1 } } ; } ( ) . size ( ) ) , p@2 ( p@3 ) { }\n" + "5: } ;\n", + tokenize(code2)); + + const char code3[] = "enum { N = 2 };\n" // no lambda + "struct S {\n" + " int* q;\n" + " S(int b) : q(new int[2]{ N * b, 1 }) {}\n" + "};\n" + "struct T {\n" + " std::vector** r;\n" + " T(std::vector* c) : r(new std::vector*[2]{ N * c, c }) {}\n" + "};\n"; + ASSERT_EQUALS("1: enum Anonymous0 { N = 2 } ;\n" + "2: struct S {\n" + "3: int * q@1 ;\n" + "4: S ( int b@2 ) : q@1 ( new int [ 2 ] { N * b@2 , 1 } ) { }\n" + "5: } ;\n" + "6: struct T {\n" + "7: std :: vector < int > * * r@3 ;\n" + "8: T ( std :: vector < int > * c@4 ) : r@3 ( new std :: vector < int > * [ 2 ] { N * c@4 , c@4 } ) { }\n" + "9: } ;\n", + tokenize(code3)); + } + void varid_operator() { { const std::string actual = tokenize(