Skip to content

Commit c0ebd17

Browse files
autoantwortclaude
andcommitted
Fix #15084 Wrong varid for lambda in constructor initializer list
The lambda body in an initializer list was taken as the start of the constructor body, so following members got the parameter varid, lambda parameters leaked into the rest of the initializer list and the parameter varid leaked into later functions (FP uninitMemberVar, selfInitialization, functionStatic, constParameterPointer). The lambda is now parsed like a lambda in executable code. findTypeEnd() also finds the end of unlinked template brackets so that findLambdaEndScope() works for trailing return types like std::vector<int> before createLinks2(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent abef304 commit c0ebd17

3 files changed

Lines changed: 98 additions & 1 deletion

File tree

‎lib/token.cpp‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2683,8 +2683,10 @@ bool Token::Impl::getCppcheckAttribute(CppcheckAttributesType attrType, MathLib:
26832683
Token* findTypeEnd(Token* tok)
26842684
{
26852685
while (Token::Match(tok, "%name%|.|::|*|&|&&|<|(|template|decltype|sizeof")) {
2686-
if (Token::Match(tok, "(|<"))
2686+
if (tok->str() == "(")
26872687
tok = tok->link();
2688+
else if (tok->str() == "<") // template brackets are not linked before Tokenizer::createLinks2()
2689+
tok = tok->link() ? tok->link() : tok->findClosingBracket();
26882690
if (!tok)
26892691
return nullptr;
26902692
tok = tok->next();

‎lib/tokenize.cpp‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4763,6 +4763,24 @@ static const std::unordered_set<std::string> notstart_cpp = { NOTSTART_C,
47634763
"delete", "friend", "new", "throw", "using", "virtual", "explicit", "const_cast", "dynamic_cast", "reinterpret_cast", "static_cast", "template"
47644764
};
47654765

4766+
// Returns the end of the lambda that starts at tok in a constructor initializer list, or nullptr
4767+
static const Token* findInitListLambdaEnd(const Token* tok)
4768+
{
4769+
if (!Token::simpleMatch(tok, "[") || Token::Match(tok->previous(), "%name%|)|]|>"))
4770+
return nullptr; // array subscript or array size of a new expression
4771+
// array size of a new expression with pointer or reference type: new T*[n]{...}
4772+
for (const Token* prev = tok->previous(); Token::Match(prev, "*|&|&&|::|%name%|>"); prev = prev->previous()) {
4773+
if (prev->str() == "new")
4774+
return nullptr;
4775+
if (prev->str() == ">") {
4776+
prev = prev->findOpeningBracket();
4777+
if (!prev)
4778+
break;
4779+
}
4780+
}
4781+
return findLambdaEndScope(tok);
4782+
}
4783+
47664784
void Tokenizer::setVarIdPass1()
47674785
{
47684786
const bool cpp = isCPP();
@@ -4778,6 +4796,7 @@ void Tokenizer::setVarIdPass1()
47784796
std::stack<const Token *> functionDeclEndStack;
47794797
const Token *functionDeclEndToken = nullptr;
47804798
bool initlist = false;
4799+
std::stack<const Token *> initlistLambdaEnds; // ends of lambdas in constructor initializer lists
47814800
bool inlineFunction = false;
47824801
for (Token *tok = list.front(); tok; tok = tok->next()) {
47834802
if (tok->isOp())
@@ -4813,6 +4832,13 @@ void Tokenizer::setVarIdPass1()
48134832
variableMap.enterScope();
48144833
}
48154834
}
4835+
} else if (const Token* lambdaEnd = initlist ? findInitListLambdaEnd(tok) : nullptr) {
4836+
// lambda in initializer list: parse it like a lambda in executable code, the
4837+
// extra scope holds its parameters and is left at the end of the lambda
4838+
initlistLambdaEnds.push(lambdaEnd);
4839+
scopeStack.emplace(/*isExecutable=*/ true, /*isStructInit=*/ false, /*isEnum=*/ false, variableMap.getVarId());
4840+
variableMap.enterScope();
4841+
initlist = false;
48164842
} else if (!initlist && tok->str()=="(") {
48174843
const Token * newFunctionDeclEnd = nullptr;
48184844
if (!scopeStack.top().isExecutable)
@@ -4903,6 +4929,14 @@ void Tokenizer::setVarIdPass1()
49034929
}
49044930
}
49054931
}
4932+
4933+
if (!initlistLambdaEnds.empty() && initlistLambdaEnds.top() == tok) {
4934+
// end of lambda in initializer list
4935+
initlistLambdaEnds.pop();
4936+
scopeStack.pop();
4937+
variableMap.leaveScope();
4938+
initlist = true;
4939+
}
49064940
}
49074941

49084942
if ((!scopeStack.top().isStructInit &&

‎test/testvarid.cpp‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class TestVarID : public TestFixture {
161161
TEST_CASE(varid_initList);
162162
TEST_CASE(varid_initListWithBaseTemplate);
163163
TEST_CASE(varid_initListWithScope);
164+
TEST_CASE(varid_initListWithLambda);
164165
TEST_CASE(varid_operator);
165166
TEST_CASE(varid_throw);
166167
TEST_CASE(varid_unknown_macro); // #2638 - unknown macro is not type
@@ -2789,6 +2790,66 @@ class TestVarID : public TestFixture {
27892790
tokenize(code1));
27902791
}
27912792

2793+
void varid_initListWithLambda() {
2794+
const char code1[] = "struct S {\n"
2795+
" int x;\n"
2796+
" int* p;\n"
2797+
" S(int* p) : x([p] { return *p; }()), p(p) {}\n"
2798+
" S(int* p, int* q) : x([p](int* q) { return *p + *q; }(q)), p(q) {}\n"
2799+
" S(int* p, char) : x([p]() noexcept { int v = *p; return v; }()), p{p} {}\n"
2800+
"};\n"
2801+
"struct T {\n"
2802+
" int* p;\n"
2803+
" int g();\n"
2804+
"};\n"
2805+
"int T::g() { return *p; }\n";
2806+
ASSERT_EQUALS("1: struct S {\n"
2807+
"2: int x@1 ;\n"
2808+
"3: int * p@2 ;\n"
2809+
"4: S ( int * p@3 ) : x@1 ( [ p@3 ] { return * p@3 ; } ( ) ) , p@2 ( p@3 ) { }\n"
2810+
"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"
2811+
"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"
2812+
"7: } ;\n"
2813+
"8: struct T {\n"
2814+
"9: int * p@9 ;\n"
2815+
"10: int g ( ) ;\n"
2816+
"11: } ;\n"
2817+
"12: int T :: g ( ) { return * p@9 ; }\n",
2818+
tokenize(code1));
2819+
2820+
const char code2[] = "struct S {\n"
2821+
" int x;\n"
2822+
" int* p;\n"
2823+
" S(int* p) : x([p]() -> std::map<int, int> { return {{*p, 1}}; }().size()), p(p) {}\n"
2824+
"};\n";
2825+
ASSERT_EQUALS("1: struct S {\n"
2826+
"2: int x@1 ;\n"
2827+
"3: int * p@2 ;\n"
2828+
"4: S ( int * p@3 ) : x@1 ( [ p@3 ] ( ) . std :: map < int , int > { return { { * p@3 , 1 } } ; } ( ) . size ( ) ) , p@2 ( p@3 ) { }\n"
2829+
"5: } ;\n",
2830+
tokenize(code2));
2831+
2832+
const char code3[] = "enum { N = 2 };\n" // no lambda
2833+
"struct S {\n"
2834+
" int* q;\n"
2835+
" S(int b) : q(new int[2]{ N * b, 1 }) {}\n"
2836+
"};\n"
2837+
"struct T {\n"
2838+
" std::vector<int>** r;\n"
2839+
" T(std::vector<int>* c) : r(new std::vector<int>*[2]{ N * c, c }) {}\n"
2840+
"};\n";
2841+
ASSERT_EQUALS("1: enum Anonymous0 { N = 2 } ;\n"
2842+
"2: struct S {\n"
2843+
"3: int * q@1 ;\n"
2844+
"4: S ( int b@2 ) : q@1 ( new int [ 2 ] { N * b@2 , 1 } ) { }\n"
2845+
"5: } ;\n"
2846+
"6: struct T {\n"
2847+
"7: std :: vector < int > * * r@3 ;\n"
2848+
"8: T ( std :: vector < int > * c@4 ) : r@3 ( new std :: vector < int > * [ 2 ] { N * c@4 , c@4 } ) { }\n"
2849+
"9: } ;\n",
2850+
tokenize(code3));
2851+
}
2852+
27922853
void varid_operator() {
27932854
{
27942855
const std::string actual = tokenize(

0 commit comments

Comments
 (0)