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
This commit is contained in:
PKEuS 2014-08-05 15:33:57 +02:00
parent f5730a7d12
commit f3e0df7501
6 changed files with 96 additions and 13 deletions

View File

@ -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<Scope*>::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

View File

@ -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 &&

View File

@ -2276,6 +2276,8 @@ static bool setVarIdParseDeclaration(const Token **tok, const std::map<std::stri
bool bracket = false;
while (tok2) {
if (tok2->isName()) {
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::map<std::stri
// In executable scopes, references must be assigned
// Catching by reference is an exception
if (executableScope && ref) {
if (tok2->str() == "(" || 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()) {

View File

@ -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"

View File

@ -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<int> vec{1, 2, 3};\n"
"namespace n { int z; };\n"
"int& j{i};\n"));
}
void varidclass1() {
const std::string actual = tokenizeDebugListing(
"class Fred\n"

View File

@ -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<std::mutex> l{ m };\n"
" return 0;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void localvaralias1() {
functionVariableUsage("void foo()\n"
"{\n"