Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/token.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
34 changes: 34 additions & 0 deletions lib/tokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4763,6 +4763,24 @@ static const std::unordered_set<std::string> 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();
Expand All @@ -4778,6 +4796,7 @@ void Tokenizer::setVarIdPass1()
std::stack<const Token *> functionDeclEndStack;
const Token *functionDeclEndToken = nullptr;
bool initlist = false;
std::stack<const Token *> initlistLambdaEnds; // ends of lambdas in constructor initializer lists
bool inlineFunction = false;
for (Token *tok = list.front(); tok; tok = tok->next()) {
if (tok->isOp())
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 &&
Expand Down
61 changes: 61 additions & 0 deletions test/testvarid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<int, int> { 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<int>** r;\n"
" T(std::vector<int>* c) : r(new std::vector<int>*[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(
Expand Down
Loading