Skip to content

Fix #15062 Wrong varid for shadowed member in initializer list after braced init list argument (FP uninitMemberVar, selfInitialization, functionStatic) - #8885

Open
autoantwort wants to merge 1 commit into
cppcheck-opensource:mainfrom
autoantwort:fix-varid-initlist-braced-arg
Open

autoantwort wants to merge 1 commit into
cppcheck-opensource:mainfrom
autoantwort:fix-varid-initlist-braced-arg

Conversation

@autoantwort

Copy link
Copy Markdown
Contributor

In setVarIdPass1 a { inside a constructor initializer list was only treated as part of an argument when preceded by %name%|>|>>|(. For a braced init list that is not the first argument (x(f(0, {0}))) or a nested one (x(f(0, {{1}, 2}))), the { was taken as the start of the constructor body. The parameter scope was then not left correctly: a following p(p) got the parameter's varid for the member, and the parameter varid leaked into later functions such as int T::g() { return *p; }.

This caused false positives uninitMemberVar, selfInitialization and functionStatic.

The fix also accepts , and { before the {, and } after the matching }.

🤖 Generated with Claude Code

…braced init list argument (FP uninitMemberVar, selfInitialization, functionStatic)

In setVarIdPass1 a '{' inside a constructor initializer list was only
recognized as part of an argument if it was preceded by a name, '>',
'>>' or '('. A braced init list that is not the first argument, e.g.
x(f(0, {0})), or a nested one, e.g. x(f(0, {{1}, 2})), was treated as
the start of the constructor body. As a result the parameter scope was
not left correctly, so the member in a following p(p) got the varid of
the parameter, and the parameter varid leaked into later functions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread lib/tokenize.cpp
// parse anonymous namespaces as part of the current scope
if (!Token::Match(startToken->previous(), "union|struct|enum|namespace {") &&
!(initlist && Token::Match(startToken->previous(), "%name%|>|>>|(") && Token::Match(startToken->link(), "} ,|{|)|..."))) {
!(initlist && Token::Match(startToken->previous(), "%name%|>|>>|(|,|{") && Token::Match(startToken->link(), "} ,|{|)|}|..."))) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an AI review. Take it with a grain of salt and feel free to reject it by resolving the comment.

The change looks good to me. I built it and checked some more nested forms: x{{1, 2}}, x(A{{1, 2}}), v{1, 2}, v(std::vector<int>{1, {2}}), and a braced local in the body. The member/parameter varids are now correct, and the leaked varid in the following T::g() is gone.

FYI, a closely related case that still gives the same false positives on both main and this PR is a lambda in the initializer list:

struct S {
    int* p;
    int x;
    S(int* p) : x([p]{ return *p; }()), p(p) {}
};
struct T { int* p; int g(); };
int T::g() { return *p; }

This gives uninitMemberVar + selfInitialization on line 4 and functionStatic on line 7. It may be out of scope for this PR, but maybe it's worth a ticket, or a TODO test next to code14.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants