From 2808fc615efe3ee0a0867457a085a763e487ed56 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 24 Sep 2022 12:49:37 +0200 Subject: [PATCH] Fix #10057 "statement without effect" / #4779 FN unusedScopedObject does not work for classes in different namespace (#4500) * Partial fix for #10057 unused variable without assignment not detected * Add test for #9672 * Fix #4779 FN unusedScopedObject does not work for classes in different namespace * Merge * Fix #10057 "statement without effect" (unused variable without assignment) not detected * Format --- lib/checkother.cpp | 47 +++++++++++++++++++++++++++++++++++----------- test/testother.cpp | 25 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index cc51b00f3..43cc9a06e 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2063,20 +2063,45 @@ void CheckOther::checkMisusedScopedObject() if (!mSettings->severity.isEnabled(Severity::style)) return; - const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); + auto getConstructorTok = [](const Token* tok, std::string& typeStr) -> const Token* { + if (!Token::Match(tok, "[;{}] %name%")) + return nullptr; + tok = tok->next(); + typeStr.clear(); + while (Token::Match(tok, "%name% ::")) { + typeStr += tok->str(); + typeStr += "::"; + tok = tok->tokAt(2); + } + typeStr += tok->str(); + const Token* endTok = tok; + if (Token::Match(endTok, "%name% <")) + endTok = endTok->linkAt(1); + if (Token::Match(endTok, "%name%|> (|{") && Token::Match(endTok->linkAt(1), ")|} ; !!}") && + !Token::simpleMatch(endTok->next()->astParent(), ";")) { // for loop condition + return tok; + } + return nullptr; + }; + + auto isLibraryConstructor = [&](const Token* tok, const std::string& typeStr) -> bool { + if (mSettings->library.getTypeCheck("unusedvar", typeStr) == Library::TypeCheck::check) + return true; + return mSettings->library.detectContainerOrIterator(tok); + }; + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + std::string typeStr; for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { - if ((tok->next()->type() || tok->next()->isStandardType() || (tok->next()->function() && tok->next()->function()->isConstructor())) // TODO: The rhs of || should be removed; It is a workaround for a symboldatabase bug - && Token::Match(tok, "[;{}] %name% (|{") - && Token::Match(tok->linkAt(2), ")|} ; !!}") - && (!tok->next()->function() || // is not a function on this scope - tok->next()->function()->isConstructor()) // or is function in this scope and it's a ctor - && !Token::simpleMatch(tok->tokAt(2)->astParent(), ";") // for loop condition - && tok->next()->str() != "void") { - if (const Token* arg = tok->tokAt(2)->astOperand2()) { + const Token* ctorTok = getConstructorTok(tok, typeStr); + if (ctorTok && (((ctorTok->type() || ctorTok->isStandardType() || (ctorTok->function() && ctorTok->function()->isConstructor())) // TODO: The rhs of || should be removed; It is a workaround for a symboldatabase bug + && (!ctorTok->function() || ctorTok->function()->isConstructor()) // // is not a function on this scope or is function in this scope and it's a ctor + && ctorTok->str() != "void") || isLibraryConstructor(tok->next(), typeStr))) { + if (const Token* arg = ctorTok->next()->astOperand2()) { if (!isConstStatement(arg, mTokenizer->isCPP())) continue; - if (tok->strAt(2) == "(") { + if (ctorTok->strAt(1) == "(") { if (arg->varId()) // TODO: check if this is a declaration continue; const Token* rml = nextAfterAstRightmostLeaf(arg); @@ -2085,7 +2110,7 @@ void CheckOther::checkMisusedScopedObject() } } tok = tok->next(); - misusedScopeObjectError(tok, tok->str()); + misusedScopeObjectError(ctorTok, typeStr); tok = tok->next(); } } diff --git a/test/testother.cpp b/test/testother.cpp index c63866036..38503f6c6 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -140,6 +140,7 @@ private: TEST_CASE(testMisusedScopeObjectInConstructor); TEST_CASE(testMisusedScopeObjectNoCodeAfter); TEST_CASE(testMisusedScopeObjectStandardType); + TEST_CASE(testMisusedScopeObjectNamespace); TEST_CASE(trac2071); TEST_CASE(trac2084); TEST_CASE(trac3693); @@ -5088,6 +5089,30 @@ private: ASSERT_EQUALS("", errout.str()); } + void testMisusedScopeObjectNamespace() { + check("namespace M {\n" // #4479 + " namespace N {\n" + " struct S {};\n" + " }\n" + "}\n" + "int f() {\n" + " M::N::S();\n" + " return 0;\n" + "}\n", "test.cpp"); + ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'M::N::S' object is destroyed immediately.\n", errout.str()); + + check("void f() {\n" // #10057 + " std::string(\"abc\");\n" + " std::string{ \"abc\" };\n" + " std::pair(1, 2);\n" + " (void)0;\n" + "}\n", "test.cpp"); + ASSERT_EQUALS("[test.cpp:2]: (style) Instance of 'std::string' object is destroyed immediately.\n" + "[test.cpp:3]: (style) Instance of 'std::string' object is destroyed immediately.\n" + "[test.cpp:4]: (style) Instance of 'std::pair' object is destroyed immediately.\n", + errout.str()); + } + void trac2084() { check("void f()\n" "{\n"