diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index f42eb167a..76371f860 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1232,34 +1232,43 @@ void CheckUnusedVar::checkStructMemberUsage() structname.clear(); if (!structname.empty() && Token::Match(tok, "[{;]")) { - // declaring a POD variable? + // declaring a POD member variable? if (!tok->next()->isStandardType()) continue; - // Declaring struct variable.. - const std::string* varname; + // Declaring struct member variable.. + const std::string* memberVarName; if (Token::Match(tok->next(), "%type% %name% [;[]")) - varname = &tok->strAt(2); + memberVarName = &tok->strAt(2); else if (Token::Match(tok->next(), "%type% %type%|* %name% [;[]")) - varname = &tok->strAt(3); + memberVarName = &tok->strAt(3); else if (Token::Match(tok->next(), "%type% %type% * %name% [;[]")) - varname = &tok->strAt(4); + memberVarName = &tok->strAt(4); else continue; - // Check if the struct variable is used anywhere in the file - const std::string usagePattern(". " + *varname); + // Check if the struct member variable is used anywhere in the file + const std::string usagePattern(". " + *memberVarName); bool used = false; - for (const Token *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next()) { - if (Token::simpleMatch(tok2, usagePattern.c_str())) { + const Token* usageTok = _tokenizer->tokens(); + while ((usageTok = Token::findsimplematch(usageTok->next(), usagePattern.c_str())) != nullptr) { + // Locate the containing struct variable and ensure it's of the same type, not a random struct + const Token* structVarTok = usageTok->previous(); + // Walk backwards over array accesses + while (structVarTok && structVarTok->link()) + structVarTok = structVarTok->link()->previous(); + if (!structVarTok) + continue; + const Variable* structVar = structVarTok->variable(); + if (structVar && structVar->type() && structVar->type()->name() == structname) { used = true; break; } } if (!used) { - unusedStructMemberError(tok->next(), structname, *varname, tok->scope()->type == Scope::eUnion); + unusedStructMemberError(tok->next(), structname, *memberVarName, tok->scope()->type == Scope::eUnion); } } } diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 96d1fcb54..b6c5fefc0 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -222,6 +222,34 @@ private: "[test.cpp:4]: (style) union member 'abc::b' is never used.\n" "[test.cpp:5]: (style) union member 'abc::c' is never used.\n", errout.str()); + checkStructMemberUsage("struct A\n" + "{\n" + " int a;\n" + "};\n" + "struct B\n" + "{\n" + " int a;\n" + "};\n" + "void foo()\n" + "{\n" + " A a;\n" + " a.a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) struct member 'B::a' is never used.\n", errout.str()); + + checkStructMemberUsage("struct A\n" + "{\n" + " int a;\n" + "};\n" + "struct B\n" + "{\n" + " int a;\n" + "};\n" + "void foo(A* a)\n" + "{\n" + " a->a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) struct member 'B::a' is never used.\n", errout.str()); } void structmember2() {