diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 6b028d417..897155c8b 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2037,12 +2037,28 @@ bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const } } while (again); - for (const Variable &var : scope->varlist) { + for (const Variable& var : scope->varlist) { if (var.name() == tok->str()) { - if (tok->varId() == 0) - mSymbolDatabase->debugMessage(tok, "varid0", "CheckClass::isMemberVar found used member variable \'" + tok->str() + "\' with varid 0"); + const Token* fqTok = tok; + while (Token::Match(fqTok->tokAt(-2), "%name% ::")) + fqTok = fqTok->tokAt(-2); + if (fqTok->strAt(-1) == "::") + fqTok = fqTok->previous(); + bool isMember = tok == fqTok; + std::string scopeStr; + const Scope* curScope = scope; + while (!isMember && curScope && curScope->type != Scope::ScopeType::eGlobal) { + scopeStr.insert(0, curScope->className + " :: "); + isMember = Token::Match(fqTok, scopeStr.c_str()); - return !var.isStatic(); + curScope = curScope->nestedIn; + } + if (isMember) { + if (tok->varId() == 0) + mSymbolDatabase->debugMessage(tok, "varid0", "CheckClass::isMemberVar found used member variable \'" + tok->str() + "\' with varid 0"); + + return !var.isStatic(); + } } } diff --git a/test/testclass.cpp b/test/testclass.cpp index 58449c261..892e6775f 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -191,6 +191,7 @@ private: TEST_CASE(const72); // ticket #10520 TEST_CASE(const73); // ticket #10735 TEST_CASE(const74); // ticket #10671 + TEST_CASE(const75); // ticket #10065 TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(assigningPointerToPointerIsNotAConstOperation); @@ -5963,6 +5964,36 @@ private: ASSERT_EQUALS("", errout.str()); } + void const75() { // #10065 + checkConst("namespace N { int i = 0; }\n" + "struct S {\n" + " int i;\n" + " void f() {\n" + " if (N::i) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'S::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); + + checkConst("int i = 0;\n" + "struct S {\n" + " int i;\n" + " void f() {\n" + " if (::i) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'S::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); + + checkConst("namespace N {\n" + " struct S {\n" + " int i;\n" + " void f() {\n" + " if (N::S::i) {}\n" + " }\n" + " };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'N::S::f' can be const.\n", errout.str()); + } + void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n"