diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 755b77517c7..6d100837c12 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1534,7 +1534,14 @@ void CheckUnusedVar::checkStructMemberUsage() if (bailout) continue; + // Keep track of number of non-static members encountered so far + int nummembs = 0; + for (const Variable &var : scope.varlist) { + if (!var.isStatic()) { + nummembs++; + } + // only warn for variables without side effects if (!var.typeStartToken()->isStandardType() && !var.isPointer() && !astIsContainer(var.nameToken()) && !isRecordTypeWithoutSideEffects(var.type())) continue; @@ -1544,6 +1551,22 @@ void CheckUnusedVar::checkStructMemberUsage() // Check if the struct member variable is used anywhere in the file bool use = false; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name% {") && (!tok->previous() || !Token::Match(tok->previous(), "class|struct")) && tok->str() == scope.className) { + const Token *inner = tok->next()->astOperand2(); + // Find number of initialized members in braced initalizer + int initmembs = 0; + while (inner) { + initmembs++; + if (!inner->isInitComma()) { + break; + } + inner = inner->astOperand1(); + } + if (initmembs >= nummembs) { + use = true; + break; + } + } if (Token::Match(tok, ". %name%") && !tok->next()->variable() && !tok->next()->function() && tok->strAt(1) == var.name()) { // not known => assume variable is used use = true; diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 3d57b789c02..82d37d70d6d 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -70,6 +70,9 @@ class TestUnusedVar : public TestFixture { TEST_CASE(structmember23); TEST_CASE(structmember24); // #10847 TEST_CASE(structmember25); + TEST_CASE(structmember26); // #10305 + TEST_CASE(structmember27); + TEST_CASE(structmember28); TEST_CASE(structmember_macro); TEST_CASE(classmember); @@ -1962,6 +1965,54 @@ class TestUnusedVar : public TestFixture { errout_str()); } + void structmember26() { // #10305 + checkStructMemberUsage("struct S {\n" + " const int* p{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() const {\n" + " return S{ &i };\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void structmember27() { + checkStructMemberUsage("struct S {\n" + " const int* p{};\n" + " static int* q;\n" + " const int* r{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " int j{};\n" + " S f() const {\n" + " return S{ &i, &j };\n" + " }\n" + "};\n" + "int *S::q = 0;\n"); + ASSERT_EQUALS("", errout_str()); + } + + void structmember28() { + checkStructMemberUsage("struct S {\n" + " const int* p{};\n" + " static int* q;\n" + " const int* r{};\n" + " const int* s{};" + "};\n" + "struct C {\n" + " int i{};\n" + " int j{};\n" + " S f() const {\n" + " return S{ &i, &j };\n" + " }\n" + "};\n" + "int *S::q = 0;\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) struct member 'S::s' is never used.\n", errout_str()); + } + void structmember_macro() { checkStructMemberUsageP("#define S(n) struct n { int a, b, c; };\n" "S(unused);\n");