From ced7b0ef5a988088c7a757700133239649392fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sun, 1 Mar 2026 20:55:27 +0100 Subject: [PATCH 1/3] Fix #14471 --- lib/checkother.cpp | 58 +++++++++++++++++++++++++++++----------------- lib/checkother.h | 2 +- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 32dedf8f374..61305302b34 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -4144,18 +4144,21 @@ void CheckOther::checkShadowVariables() const Scope *functionScope = &scope; while (functionScope && functionScope->type != ScopeType::eFunction && functionScope->type != ScopeType::eLambda) functionScope = functionScope->nestedIn; - for (const Variable &var : scope.varlist) { - if (var.nameToken() && var.nameToken()->isExpandedMacro()) // #8903 - continue; + const auto checkVar = [&](const Variable &var) { + if (!var.nameToken()) + return; - if (functionScope && functionScope->type == ScopeType::eFunction && functionScope->function) { + if (var.nameToken()->isExpandedMacro()) // #8903 + return; + + if (!var.isArgument() && functionScope && functionScope->type == ScopeType::eFunction && functionScope->function) { const auto & argList = functionScope->function->argumentList; auto it = std::find_if(argList.cbegin(), argList.cend(), [&](const Variable& arg) { return arg.nameToken() && var.name() == arg.name(); }); if (it != argList.end()) { - shadowError(var.nameToken(), it->nameToken(), "argument"); - continue; + shadowError(var.nameToken(), "local variable", it->nameToken(), "argument"); + return; } } @@ -4163,27 +4166,39 @@ void CheckOther::checkShadowVariables() if (!shadowed) shadowed = findShadowed(scope.functionOf, var, var.nameToken()->linenr()); if (!shadowed) - continue; + return; if (scope.type == ScopeType::eFunction && scope.className == var.name()) - continue; + return; if (functionScope->functionOf && functionScope->functionOf->isClassOrStructOrUnion() && functionScope->function && (functionScope->function->isStatic() || functionScope->function->isFriend()) && shadowed->variable() && !shadowed->variable()->isLocal()) - continue; - shadowError(var.nameToken(), shadowed, (shadowed->varId() != 0) ? "variable" : "function"); - } + return; + if (var.scope() && var.scope()->function && var.scope()->function->isConstructor() + && shadowed->variable() && shadowed->variable()->isMember()) + return; + shadowError(var.nameToken(), var.isArgument() ? "argument" : "local variable", + shadowed, (shadowed->varId() != 0) ? + (shadowed->variable()->isMember() ? "member" : "variable") : "function"); + }; + for (const Variable &var : scope.varlist) + checkVar(var); + if (functionScope && functionScope->type == ScopeType::eFunction && functionScope->function) + for (const Variable &arg: functionScope->function->argumentList) + checkVar(arg); } } -void CheckOther::shadowError(const Token *var, const Token *shadowed, const std::string& type) +void CheckOther::shadowError(const Token *shadows, const std::string &shadowsType, + const Token *shadowed, const std::string &shadowedType) { ErrorPath errorPath; - errorPath.emplace_back(shadowed, "Shadowed declaration"); - errorPath.emplace_back(var, "Shadow variable"); - const std::string &varname = var ? var->str() : type; - const std::string Type = char(std::toupper(type[0])) + type.substr(1); - const std::string id = "shadow" + Type; - const std::string message = "$symbol:" + varname + "\nLocal variable \'$symbol\' shadows outer " + type; + errorPath.emplace_back(shadowed, "Shadowed " + shadowedType); + errorPath.emplace_back(shadows, "Shadow " + shadowsType); + const std::string &varname = shadows ? shadows->str() : shadowsType; + const std::string ShadowsType = char(std::toupper(shadowsType[0])) + shadowsType.substr(1); + const std::string ShadowedType = char(std::toupper(shadowedType[0])) + shadowedType.substr(1); + const std::string id = "shadow" + ShadowedType; + const std::string message = "$symbol:" + varname + "\n" + ShadowsType + " \'$symbol\' shadows outer " + shadowedType; reportError(std::move(errorPath), Severity::style, id.c_str(), message, CWE398, Certainty::normal); } @@ -4865,9 +4880,10 @@ void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *sett c.accessMovedError(nullptr, "v", nullptr, false); c.funcArgNamesDifferent("function", 1, nullptr, nullptr); c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); - c.shadowError(nullptr, nullptr, "variable"); - c.shadowError(nullptr, nullptr, "function"); - c.shadowError(nullptr, nullptr, "argument"); + c.shadowError(nullptr, "local variable", nullptr, "variable"); + c.shadowError(nullptr, "local variable", nullptr, "argument"); + c.shadowError(nullptr, "local variable", nullptr, "function"); + c.shadowError(nullptr, "local variable", nullptr, "member"); c.knownArgumentError(nullptr, nullptr, nullptr, "x", false); c.knownPointerToBoolError(nullptr, nullptr); c.comparePointersError(nullptr, nullptr, nullptr); diff --git a/lib/checkother.h b/lib/checkother.h index 28ee25cfa76..f779141f499 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -258,7 +258,7 @@ class CPPCHECKLIB CheckOther : public Check { void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); void funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition); void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); - void shadowError(const Token *var, const Token *shadowed, const std::string& type); + void shadowError(const Token *shadows, const std::string &shadowsType, const Token *shadowed, const std::string &shadowedType); void knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden); void knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value); void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2); From 33fb42eb2b76cd4e20985bab6fb1f2492cd5d5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sun, 1 Mar 2026 20:55:36 +0100 Subject: [PATCH 2/3] Update tests --- test/testother.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/testother.cpp b/test/testother.cpp index 53f14c2a31d..13c8488d76d 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -6479,9 +6479,9 @@ class TestOther : public TestFixture { check("class Foo {\n" " int var;\n" - " void func(int var);\n" + " Foo(int var);\n" "};\n" - "void Foo::func(int var) {\n" + "void Foo(int var) {\n" " this->var = var;\n" "}"); ASSERT_EQUALS("", errout_str()); @@ -12791,14 +12791,14 @@ class TestOther : public TestFixture { " int i{};\n" " void f() { int i; }\n" "};\n"); - ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:3:20]: (style) Local variable 'i' shadows outer variable [shadowVariable]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:3:20]: (style) Local variable 'i' shadows outer member [shadowMember]\n", errout_str()); check("struct S {\n" " int i{};\n" " std::vector v;\n" " void f() const { for (const int& i : v) {} }\n" "};\n"); - ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:4:38]: (style) Local variable 'i' shadows outer variable [shadowVariable]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:4:38]: (style) Local variable 'i' shadows outer member [shadowMember]\n", errout_str()); check("struct S {\n" // #10405 " F* f{};\n" @@ -12808,7 +12808,7 @@ class TestOther : public TestFixture { "void S::f() const {\n" " for (const F& f : fl) {}\n" "};\n"); - ASSERT_EQUALS("[test.cpp:2:8] -> [test.cpp:7:19]: (style) Local variable 'f' shadows outer variable [shadowVariable]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:2:8] -> [test.cpp:7:19]: (style) Local variable 'f' shadows outer member [shadowMember]\n", errout_str()); check("extern int a;\n" "int a;\n" From fca1db483346e0b6d724133b25c22f34d66bd8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 6 Mar 2026 12:58:35 +0100 Subject: [PATCH 3/3] Add tests --- test/testother.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index 13c8488d76d..8d885c983a4 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -12830,6 +12830,26 @@ class TestOther : public TestFixture { " friend int f() { int i = 5; return i; }\n" "};\n"); ASSERT_EQUALS("", errout_str()); + + check("int x;\n" + "void f(int x) {}\n"); + ASSERT_EQUALS("[test.cpp:1:5] -> [test.cpp:2:12]: (style) Argument 'x' shadows outer variable [shadowVariable]\n", errout_str()); + + check("void x() {}\n" + "void f(int x) {}\n"); + ASSERT_EQUALS("[test.cpp:1:6] -> [test.cpp:2:12]: (style) Argument 'x' shadows outer function [shadowFunction]\n", errout_str()); + + check("struct S { int v; void func(int v); };\n" + "void S::func(int v) { this->v = v; }\n"); + ASSERT_EQUALS("[test.cpp:1:16] -> [test.cpp:2:18]: (style) Argument 'v' shadows outer member [shadowMember]\n", errout_str()); + + check("struct S { int v; void func(void); };\n" + "void S::func() { int v = 0; }\n"); + ASSERT_EQUALS("[test.cpp:1:16] -> [test.cpp:2:22]: (style) Local variable 'v' shadows outer member [shadowMember]\n", errout_str()); + + check("struct S { int v; explicit S(int v); };\n" + "S::S(int v) : v(v) {}\n"); + ASSERT_EQUALS("", errout_str()); } void knownArgument() {