From 35a46dfd004bbc6b96fd20723d88a8c030d907a2 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Tue, 18 Apr 2023 22:30:08 +0200 Subject: [PATCH] Fix FN unusedStructMember with member functions, inheritance (#4978) * Fix #551 Detect unused Private member variables * Fix FN unusedStructMember when there are member functions * Unused member * Warn for unused private variables in base class * Warn for private inheritance, add test --- lib/checkstl.cpp | 2 +- lib/checkunusedvar.cpp | 13 +++++-------- test/testunusedvar.cpp | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 77041883e..481364053 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -919,7 +919,7 @@ struct InvalidContainerAnalyzer { const Token* ftok; }; std::unordered_map expressions; - ErrorPath errorPath; + void add(const std::vector& refs) { for (const Reference& r : refs) { add(r); diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 3d9adf7fa..5a47ba8d6 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1455,25 +1455,20 @@ void CheckUnusedVar::checkStructMemberUsage() continue; } - // Bail out if struct/union contains any functions - if (!scope.functionList.empty()) - continue; - // Bail out for template struct, members might be used in non-matching instantiations if (scope.className.find('<') != std::string::npos) continue; // bail out if struct is inherited - bool bailout = std::any_of(symbolDatabase->scopeList.cbegin(), symbolDatabase->scopeList.cend(), [&](const Scope& derivedScope) { + const bool isInherited = std::any_of(symbolDatabase->scopeList.cbegin(), symbolDatabase->scopeList.cend(), [&](const Scope& derivedScope) { const Type* dType = derivedScope.definedType; return dType && std::any_of(dType->derivedFrom.cbegin(), dType->derivedFrom.cend(), [&](const Type::BaseInfo& derivedFrom) { - return derivedFrom.type == scope.definedType; + return derivedFrom.type == scope.definedType && derivedFrom.access != AccessControl::Private; }); }); - if (bailout) - continue; // bail out for extern/global struct + bool bailout = false; for (const Variable* var : symbolDatabase->variableList()) { if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope.className) { bailout = true; @@ -1510,6 +1505,8 @@ void CheckUnusedVar::checkStructMemberUsage() // only warn for variables without side effects if (!var.typeStartToken()->isStandardType() && !var.isPointer() && !astIsContainer(var.nameToken()) && !isRecordTypeWithoutSideEffects(var.type())) continue; + if (isInherited && !var.isPrivate()) + continue; // Check if the struct member variable is used anywhere in the file bool use = false; diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 4ff19d347..d19c2eef3 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -1876,6 +1876,34 @@ private: " int i{};\n" "};\n"); ASSERT_EQUALS("[test.cpp:2]: (style) class member 'C::i' is never used.\n", errout.str()); + + checkStructMemberUsage("class C {\n" + " int i{}, j{};\n" + "public:\n" + " int& get() { return i; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) class member 'C::j' is never used.\n", errout.str()); + + checkStructMemberUsage("class C {\n" + "private:\n" + " int i;\n" + "};\n" + "class D : public C {};\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) class member 'C::i' is never used.\n", errout.str()); + + checkStructMemberUsage("class C {\n" + "public:\n" + " int i;\n" + "};\n" + "class D : C {};\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) class member 'C::i' is never used.\n", errout.str()); + + checkStructMemberUsage("class C {\n" + "public:\n" + " int i;\n" + "};\n" + "class D : public C {};\n"); + ASSERT_EQUALS("", errout.str()); } void functionVariableUsage_(const char* file, int line, const char code[], const char filename[] = "test.cpp") {