From f3e0df75016e1bc8582ff1b6f657fad22cfd48b0 Mon Sep 17 00:00:00 2001 From: PKEuS Date: Tue, 5 Aug 2014 15:33:57 +0200 Subject: [PATCH] Support C++11 style initialization with {}: -> Support in setVarId and SymbolDatabase (#4344) -> Fixed false positives in unused variable checking (#5491, #5494) Side-effect: Support global variables initialized with brackets (C++03 style) in SymbolDatabase --- lib/checkunusedvar.cpp | 7 +++--- lib/symboldatabase.cpp | 13 +++++------ lib/tokenize.cpp | 14 ++++++++++-- test/testsymboldatabase.cpp | 45 +++++++++++++++++++++++++++++++++++++ test/testtokenize.cpp | 13 +++++++++++ test/testunusedvar.cpp | 17 ++++++++++++++ 6 files changed, 96 insertions(+), 13 deletions(-) diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 14d815de2..6a98bc27b 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -698,7 +698,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const for (; defValTok; defValTok = defValTok->next()) { if (defValTok->str() == "[") defValTok = defValTok->link(); - else if (defValTok->str() == "(" || defValTok->str() == "=") { + else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=") { variables.addVar(&*i, type, true); break; } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") { @@ -718,8 +718,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const variables.read(tok->varId(), i->nameToken()); } else doAssignment(variables, i->nameToken(), false, scope); - } else if (Token::Match(defValTok, "( %var% )")) // Variables used to initialize the variable read. - variables.readAll(defValTok->next()->varId(), i->nameToken()); // ReadAll? + } } } @@ -741,7 +740,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const if (!tok) break; } - if (tok->str() == "{" && tok != scope->classStart) { + if (tok->str() == "{" && tok != scope->classStart && !tok->previous()->varId()) { for (std::list::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->classStart == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(*i, variables, false); // Scan child scope diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 3f5016dba..741a77636 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -766,7 +766,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti else if (scope->type == Scope::eCatch) scope->checkVariable(tok->tokAt(2), Throw); // check for variable declaration and add it to new scope if found tok = tok1; - } else if (tok->str() == "{") { + } else if (tok->str() == "{" && !tok->previous()->varId()) { if (!Token::Match(tok->previous(), "=|,")) { scopeList.push_back(Scope(this, tok, scope, Scope::eUnconditional, tok)); scope->nestedList.push_back(&scopeList.back()); @@ -2605,7 +2605,7 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess) if (tok && isVariableDeclaration(tok, vartok, typetok)) { // If the vartok was set in the if-blocks above, create a entry for this variable.. tok = vartok->next(); - while (tok && tok->str() == "[") + while (tok && (tok->str() == "[" || tok->str() == "{")) tok = tok->link()->next(); if (vartok->varId() == 0) { @@ -2689,9 +2689,9 @@ bool Scope::isVariableDeclaration(const Token* tok, const Token*& vartok, const if (closeTok) { localVarTok = skipPointers(closeTok->next()); - if (Token::Match(localVarTok, ":: %type% %var% ;|=|(")) { + if (Token::Match(localVarTok, ":: %type% %var% [;=({]")) { if (localVarTok->strAt(3) != "(" || - Token::simpleMatch(localVarTok->linkAt(3), ") ;")) { + Token::Match(localVarTok->linkAt(3), "[)}] ;")) { localTypeTok = localVarTok->next(); localVarTok = localVarTok->tokAt(2); } @@ -2710,9 +2710,8 @@ bool Scope::isVariableDeclaration(const Token* tok, const Token*& vartok, const } else if (Token::Match(localVarTok, "%var% )|[") && localVarTok->str() != "operator") { vartok = localVarTok; typetok = localTypeTok; - } else if ((isLocal() || type == Scope::eFunction) && - Token::Match(localVarTok, "%var% (") && - Token::simpleMatch(localVarTok->next()->link(), ") ;") && localVarTok->varId()) { + } else if (localVarTok && localVarTok->varId() && Token::Match(localVarTok, "%var% (|{") && + Token::Match(localVarTok->next()->link(), ")|} ;")) { vartok = localVarTok; typetok = localTypeTok; } else if (type == eCatch && diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 6a0b6da09..e82ae7091 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -2276,6 +2276,8 @@ static bool setVarIdParseDeclaration(const Token **tok, const std::mapisName()) { + if (cpp && tok2->str() == "namespace") + return false; if (tok2->str() == "struct" || tok2->str() == "union" || (cpp && (tok2->str() == "class" || tok2->str() == "typename"))) { hasstruct = true; typeCount = 0; @@ -2315,7 +2317,7 @@ static bool setVarIdParseDeclaration(const Token **tok, const std::mapstr() == "(" || tok2->str() == "=") + if (tok2->str() == "(" || tok2->str() == "=" || tok2->str() == "{") ; // reference is assigned => ok else if (tok2->str() != ")" || tok2->link()->strAt(-1) != "catch") return false; // not catching by reference => not declaration @@ -2594,11 +2596,19 @@ void Tokenizer::setVarId() continue; const Token *tok3 = tok2->next(); - if (!tok3->isStandardType() && tok3->str() != "void" && !Token::Match(tok3, "struct|union|class %type%") && tok3->str() != "." && (notstart.find(tok3->str()) != notstart.end() ||!setVarIdParseDeclaration(&tok3, variableId, executableScope.top(), isCPP()))) { + if (!tok3->isStandardType() && tok3->str() != "void" && !Token::Match(tok3, "struct|union|class %type%") && tok3->str() != "." && (notstart.find(tok3->str()) != notstart.end() || !setVarIdParseDeclaration(&tok3, variableId, executableScope.top(), isCPP()))) { variableId[tok2->previous()->str()] = ++_varId; tok = tok2->previous(); } } + + else if (decl && isCPP() && Token::Match(tok2->previous(), "%type% {") && Token::simpleMatch(tok2->link(), "} ;")) { // C++11 initialization style + if (Token::Match(tok2->previous(), "do|try|else")) + continue; + + variableId[tok2->previous()->str()] = ++_varId; + tok = tok2->previous(); + } } if (tok->isName()) { diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 4cdfd7ae4..fbba6df8e 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -106,6 +106,8 @@ private: TEST_CASE(test_isVariableDeclarationCanHandleNull); TEST_CASE(test_isVariableDeclarationIdentifiesSimpleDeclaration); + TEST_CASE(test_isVariableDeclarationIdentifiesInitialization); + TEST_CASE(test_isVariableDeclarationIdentifiesCpp11Initialization); TEST_CASE(test_isVariableDeclarationIdentifiesScopedDeclaration); TEST_CASE(test_isVariableDeclarationIdentifiesStdDeclaration); TEST_CASE(test_isVariableDeclarationIdentifiesScopedStdDeclaration); @@ -221,6 +223,7 @@ private: TEST_CASE(symboldatabase41); // ticket #5197 (unknown macro) TEST_CASE(symboldatabase42); // only put variables in variable list TEST_CASE(symboldatabase43); // #4738 + TEST_CASE(symboldatabase44); TEST_CASE(isImplicitlyVirtual); @@ -282,6 +285,34 @@ private: ASSERT(false == v.isReference()); } + void test_isVariableDeclarationIdentifiesInitialization() { + reset(); + givenACodeSampleToTokenize simpleDeclaration("int x (1);"); + bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok); + ASSERT_EQUALS(true, result); + ASSERT_EQUALS("x", vartok->str()); + ASSERT_EQUALS("int", typetok->str()); + Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0); + ASSERT(false == v.isArray()); + ASSERT(false == v.isPointer()); + ASSERT(false == v.isReference()); + ASSERT(true == v.isIntegralType()); + } + + void test_isVariableDeclarationIdentifiesCpp11Initialization() { + reset(); + givenACodeSampleToTokenize simpleDeclaration("int x {1};"); + bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok); + ASSERT_EQUALS(true, result); + ASSERT_EQUALS("x", vartok->str()); + ASSERT_EQUALS("int", typetok->str()); + Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0); + ASSERT(false == v.isArray()); + ASSERT(false == v.isPointer()); + ASSERT(false == v.isReference()); + ASSERT(true == v.isIntegralType()); + } + void test_isVariableDeclarationIdentifiesScopedDeclaration() { reset(); givenACodeSampleToTokenize ScopedDeclaration("::int x;"); @@ -1879,6 +1910,20 @@ private: ASSERT_EQUALS("", errout.str()); } + void symboldatabase44() { + GET_SYMBOL_DB("int i { 1 };\n" + "int j ( i );\n" + "void foo() {\n" + " int k { 1 };\n" + " int l ( 1 );\n" + "}"); + ASSERT(db != nullptr); + ASSERT_EQUALS(4U, db->getVariableListSize() - 1); + ASSERT_EQUALS(2U, db->scopeList.size()); + for (std::size_t i = 1U; i < db->getVariableListSize(); i++) + ASSERT(db->getVariableFromVarId(i) != nullptr); + } + void isImplicitlyVirtual() { { GET_SYMBOL_DB("class Base {\n" diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 83a6eea1b..8584086d7 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -317,6 +317,7 @@ private: TEST_CASE(varid_sizeofPassed); // #5295 TEST_CASE(varid_classInFunction); // #5293 TEST_CASE(varid_pointerToArray); // #2645 + TEST_CASE(varid_cpp11initialization); // #4344 TEST_CASE(varidclass1); TEST_CASE(varidclass2); @@ -4923,6 +4924,18 @@ private: "int f4(int(&a6)[10], int i) { return a6[i]; }")); } + void varid_cpp11initialization() { + ASSERT_EQUALS("\n\n##file 0\n" + "1: int i@1 { 1 } ;\n" + "2: std :: vector < int > vec@2 { 1 , 2 , 3 } ;\n" + "3: namespace n { int z@3 ; } ;\n" + "4: int & j@4 { i@1 } ;\n", + tokenizeDebugListing("int i{1};\n" + "std::vector vec{1, 2, 3};\n" + "namespace n { int z; };\n" + "int& j{i};\n")); + } + void varidclass1() { const std::string actual = tokenizeDebugListing( "class Fred\n" diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index c07e869e4..e2d8f0c77 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -96,6 +96,7 @@ private: TEST_CASE(localvar44); // ticket #3602 TEST_CASE(localvar45); // ticket #4020 TEST_CASE(localvar46); // ticket #4899 + TEST_CASE(localvar47); // ticket #5491 (C++11 style initialization) TEST_CASE(localvaralias1); TEST_CASE(localvaralias2); // ticket #1637 TEST_CASE(localvaralias3); // ticket #1639 @@ -1875,6 +1876,22 @@ private: ASSERT_EQUALS("", errout.str()); } + void localvar47() { // #5491/#5494 + functionVariableUsage("int func() {\n" + " int i = 0;\n" + " int j{i};\n" + " return j;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int func() {\n" + " std::mutex m;\n" + " std::unique_lock l{ m };\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void localvaralias1() { functionVariableUsage("void foo()\n" "{\n"