From a67696d4ffed58047c5b455d830cab0c4b51bd3d Mon Sep 17 00:00:00 2001 From: Gerbo Engels Date: Fri, 4 Nov 2022 15:10:24 +0100 Subject: [PATCH 1/2] Don't assume const or static member functions can initialize member variables --- lib/checkclass.cpp | 12 ++++++------ lib/checkclass.h | 2 +- test/testconstructors.cpp | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 18cbf3b79b8..146d177c348 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -694,7 +694,7 @@ void CheckClass::clearAllVar(std::vector &usageList) } } -bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope) +bool CheckClass::isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope) { // Iterate through each base class... for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { @@ -705,11 +705,11 @@ bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope) const std::list& functionList = derivedFrom->classScope->functionList; if (std::any_of(functionList.begin(), functionList.end(), [&](const Function& func) { - return func.tokenDef->str() == tok->str(); + return func.tokenDef->str() == tok->str() && !func.isStatic() && !func.isConst(); })) return true; - if (isBaseClassFunc(tok, derivedFrom->classScope)) + if (isBaseClassMutableMemberFunc(tok, derivedFrom->classScope)) return true; } @@ -949,8 +949,8 @@ void CheckClass::initializeVarList(const Function &func, std::listisConst() && !member->isStatic()) { assignAllVar(usage); } } @@ -958,7 +958,7 @@ void CheckClass::initializeVarList(const Function &func, std::list Date: Fri, 4 Nov 2022 15:31:22 +0100 Subject: [PATCH 2/2] Let base class call to operator= only assign members in that base class --- lib/checkclass.cpp | 21 +++++++++++++++++++++ lib/checkclass.h | 7 +++++++ test/testconstructors.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 146d177c348..937c2874f43 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -686,6 +686,21 @@ void CheckClass::assignAllVar(std::vector &usageList) i.assign = true; } +void CheckClass::assignAllVarsVisibleFromScope(std::vector& usageList, const Scope* scope) +{ + for (Usage& usage : usageList) { + if (usage.var->scope() == scope) + usage.assign = true; + } + + // Iterate through each base class... + for (const Type::BaseInfo& i : scope->definedType->derivedFrom) { + const Type *derivedFrom = i.type; + + assignAllVarsVisibleFromScope(usageList, derivedFrom->classScope); + } +} + void CheckClass::clearAllVar(std::vector &usageList) { for (Usage & i : usageList) { @@ -894,6 +909,12 @@ void CheckClass::initializeVarList(const Function &func, std::liststr() == ftok->str() && isBaseClassMutableMemberFunc(ftok, scope)) { + if (member->nestedIn) + assignAllVarsVisibleFromScope(usage, member->nestedIn->definedType->classScope); + } + // there is a called member function, but it has no implementation, so we assume it initializes everything else { assignAllVar(usage); diff --git a/lib/checkclass.h b/lib/checkclass.h index cce9c93e1ab..a526c52bf05 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -372,6 +372,13 @@ class CPPCHECKLIB CheckClass : public Check { */ static void assignAllVar(std::vector &usageList); + /** + * @brief set all variable in list assigned, if visible from given scope + * @param usageList reference to usage vector + * @param scope scope from which usages must be visible + */ + static void assignAllVarsVisibleFromScope(std::vector &usageList, const Scope *scope); + /** * @brief set all variables in list not assigned and not initialized * @param usageList reference to usage vector diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 409447db51f..b6ec94996d8 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -116,6 +116,7 @@ class TestConstructors : public TestFixture { TEST_CASE(initvar_operator_eq5); // ticket #4119 TEST_CASE(initvar_operator_eq6); TEST_CASE(initvar_operator_eq7); + TEST_CASE(initvar_operator_eq8); TEST_CASE(initvar_same_classname); // BUG 2208157 TEST_CASE(initvar_chained_assign); // BUG 2270433 TEST_CASE(initvar_2constructors); // BUG 2270353 @@ -981,6 +982,30 @@ class TestConstructors : public TestFixture { ASSERT_EQUALS("", errout.str()); } + void initvar_operator_eq8() { + check("struct B {\n" + " int b;\n" + "};\n" + "struct D1 : B {\n" + " D1& operator=(const D1& src);\n" + " int d1;\n" + "};\n" + "struct D2 : D1 {\n" + " D2& operator=(const D2& src);\n" + " int d2;\n" + "};\n" + "struct D3 : D2 {\n" + " D3& operator=(const D3& src) {\n" + " D1::operator=(src);\n" + " d3_1 = src.d3_1;\n" + " }\n" + " int d3_1;\n" + " int d3_2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'D3::d3_2' is not assigned a value in 'D3::operator='.\n" + "[test.cpp:13]: (warning) Member variable 'D3::d2' is not assigned a value in 'D3::operator='.\n", errout.str()); + } + void initvar_same_classname() { // Bug 2208157 - False positive: Uninitialized variable, same class name