Skip to content

Commit 2a9e809

Browse files
authored
Fix #15084 Wrong varid for lambda in constructor initializer list (#8917)
1 parent 79ac5ad commit 2a9e809

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

‎lib/tokenize.cpp‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4763,6 +4763,26 @@ 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]{...}, new (p) 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->link();
4777+
else if (prev->str() == ">") {
4778+
prev = prev->findOpeningBracket();
4779+
if (!prev)
4780+
break;
4781+
}
4782+
}
4783+
return findLambdaEndScope(tok);
4784+
}
4785+
47664786
void Tokenizer::setVarIdPass1()
47674787
{
47684788
const bool cpp = isCPP();
@@ -4778,6 +4798,7 @@ void Tokenizer::setVarIdPass1()
47784798
std::stack<const Token *> functionDeclEndStack;
47794799
const Token *functionDeclEndToken = nullptr;
47804800
bool initlist = false;
4801+
std::stack<const Token *> initlistLambdaEnds; // ends of lambdas in constructor initializer lists
47814802
bool inlineFunction = false;
47824803
for (Token *tok = list.front(); tok; tok = tok->next()) {
47834804
if (tok->isOp())
@@ -4813,6 +4834,13 @@ void Tokenizer::setVarIdPass1()
48134834
variableMap.enterScope();
48144835
}
48154836
}
4837+
} else if (const Token* lambdaEnd = initlist ? findInitListLambdaEnd(tok) : nullptr) {
4838+
// lambda in initializer list: parse it like a lambda in executable code, the
4839+
// extra scope holds its parameters and is left at the end of the lambda
4840+
initlistLambdaEnds.push(lambdaEnd);
4841+
scopeStack.emplace(/*isExecutable=*/ true, /*isStructInit=*/ false, /*isEnum=*/ false, variableMap.getVarId());
4842+
variableMap.enterScope();
4843+
initlist = false;
48164844
} else if (!initlist && tok->str()=="(") {
48174845
const Token * newFunctionDeclEnd = nullptr;
48184846
if (!scopeStack.top().isExecutable)
@@ -4903,6 +4931,15 @@ void Tokenizer::setVarIdPass1()
49034931
}
49044932
}
49054933
}
4934+
4935+
if (!initlistLambdaEnds.empty() && initlistLambdaEnds.top() == tok) {
4936+
// end of lambda in initializer list
4937+
initlistLambdaEnds.pop();
4938+
if (scopeStack.size() > 1)
4939+
scopeStack.pop();
4940+
variableMap.leaveScope();
4941+
initlist = true;
4942+
}
49064943
}
49074944

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

‎test/testvarid.cpp‎

Lines changed: 77 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
@@ -2813,6 +2814,82 @@ class TestVarID : public TestFixture {
28132814
tokenize(code1));
28142815
}
28152816

2817+
void varid_initListWithLambda() {
2818+
const char code1[] = "struct S {\n"
2819+
" int x;\n"
2820+
" int* p;\n"
2821+
" S(int* p) : x([p] { return *p; }()), p(p) {}\n"
2822+
" S(int* p, int* q) : x([p](int* q) { return *p + *q; }(q)), p(q) {}\n"
2823+
" S(int* p, char) : x([p]() noexcept { int v = *p; return v; }()), p{p} {}\n"
2824+
"};\n"
2825+
"struct T {\n"
2826+
" int* p;\n"
2827+
" int g();\n"
2828+
"};\n"
2829+
"int T::g() { return *p; }\n";
2830+
ASSERT_EQUALS("1: struct S {\n"
2831+
"2: int x@1 ;\n"
2832+
"3: int * p@2 ;\n"
2833+
"4: S ( int * p@3 ) : x@1 ( [ p@3 ] { return * p@3 ; } ( ) ) , p@2 ( p@3 ) { }\n"
2834+
"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"
2835+
"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"
2836+
"7: } ;\n"
2837+
"8: struct T {\n"
2838+
"9: int * p@9 ;\n"
2839+
"10: int g ( ) ;\n"
2840+
"11: } ;\n"
2841+
"12: int T :: g ( ) { return * p@9 ; }\n",
2842+
tokenize(code1));
2843+
2844+
const char code2[] = "struct S {\n"
2845+
" int x;\n"
2846+
" int* p;\n"
2847+
" S(int* p) : x(*[p]() -> int* { return p; }()), p(p) {}\n"
2848+
"};\n";
2849+
ASSERT_EQUALS("1: struct S {\n"
2850+
"2: int x@1 ;\n"
2851+
"3: int * p@2 ;\n"
2852+
"4: S ( int * p@3 ) : x@1 ( * [ p@3 ] ( ) . int * { return p@3 ; } ( ) ) , p@2 ( p@3 ) { }\n"
2853+
"5: } ;\n",
2854+
tokenize(code2));
2855+
2856+
const char code3[] = "enum { N = 2 };\n" // no lambda
2857+
"struct S {\n"
2858+
" int* q;\n"
2859+
" S(int b) : q(new int[2]{ N * b, 1 }) {}\n"
2860+
"};\n"
2861+
"struct T {\n"
2862+
" std::vector<int>** r;\n"
2863+
" T(std::vector<int>* c) : r(new std::vector<int>*[2]{ N * c, c }) {}\n"
2864+
"};\n"
2865+
"struct U {\n"
2866+
" int** r;\n"
2867+
" U(int* c) : r(new (std::nothrow) int*[2]{ N * c, c }) {}\n"
2868+
"};\n"
2869+
"struct V {\n"
2870+
" int** r;\n"
2871+
" V(int* c) : r(new decltype(c)*[2]{ N * c, c }) {}\n"
2872+
"};\n";
2873+
ASSERT_EQUALS("1: enum Anonymous0 { N = 2 } ;\n"
2874+
"2: struct S {\n"
2875+
"3: int * q@1 ;\n"
2876+
"4: S ( int b@2 ) : q@1 ( new int [ 2 ] { N * b@2 , 1 } ) { }\n"
2877+
"5: } ;\n"
2878+
"6: struct T {\n"
2879+
"7: std :: vector < int > * * r@3 ;\n"
2880+
"8: T ( std :: vector < int > * c@4 ) : r@3 ( new std :: vector < int > * [ 2 ] { N * c@4 , c@4 } ) { }\n"
2881+
"9: } ;\n"
2882+
"10: struct U {\n"
2883+
"11: int * * r@5 ;\n"
2884+
"12: U ( int * c@6 ) : r@5 ( new ( std :: nothrow ) int * [ 2 ] { N * c@6 , c@6 } ) { }\n"
2885+
"13: } ;\n"
2886+
"14: struct V {\n"
2887+
"15: int * * r@7 ;\n"
2888+
"16: V ( int * c@8 ) : r@7 ( new decltype ( c@8 ) * [ 2 ] { N * c@8 , c@8 } ) { }\n"
2889+
"17: } ;\n",
2890+
tokenize(code3));
2891+
}
2892+
28162893
void varid_operator() {
28172894
{
28182895
const std::string actual = tokenize(

0 commit comments

Comments
 (0)