From f306246c7f0e834a93291f24c12581ee6e1963e3 Mon Sep 17 00:00:00 2001 From: PKEuS Date: Sat, 17 Dec 2011 19:04:03 +0100 Subject: [PATCH] Improved support for references and pointers in SymbolDatabase Replaced several isPointer functions by Variable::isPointer function Refactorizations & Make use of symbolDatabase more often --- lib/checkbufferoverrun.cpp | 51 +++------- lib/checknullpointer.cpp | 72 ++++---------- lib/checknullpointer.h | 6 -- lib/checkother.cpp | 187 +++++++++++++++--------------------- lib/checkstl.cpp | 17 ++-- lib/symboldatabase.cpp | 60 ++++++------ lib/symboldatabase.h | 21 +++- test/testsymboldatabase.cpp | 66 +++++++++---- 8 files changed, 205 insertions(+), 275 deletions(-) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 1d4bb3301..280d8b570 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -422,25 +422,11 @@ static bool for_bailout(const Token * const tok1, unsigned int varid) } -void CheckBufferOverrun::parse_for_body(const Token *tok2, const ArrayInfo &arrayInfo, const std::string &strindex, bool condition_out_of_bounds, unsigned int counter_varid, const std::string &min_counter_value, const std::string &max_counter_value) +void CheckBufferOverrun::parse_for_body(const Token *tok, const ArrayInfo &arrayInfo, const std::string &strindex, bool condition_out_of_bounds, unsigned int counter_varid, const std::string &min_counter_value, const std::string &max_counter_value) { const std::string pattern((arrayInfo.varid() ? std::string("%varid%") : arrayInfo.varname()) + " [ " + strindex + " ]"); - // count { and } for tok2 - int indentlevel2 = 0; - for (; tok2; tok2 = tok2->next()) { - if (tok2->str() == ";" && indentlevel2 == 0) - break; - - if (tok2->str() == "{") - ++indentlevel2; - - if (tok2->str() == "}") { - --indentlevel2; - if (indentlevel2 <= 0) - break; - } - + for (const Token* tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { // TODO: try to reduce false negatives. This is just a quick fix // for TestBufferOverrun::array_index_for_question if (tok2->str() == "?") @@ -713,31 +699,22 @@ void CheckBufferOverrun::checkFunctionCall(const Token *tok, const ArrayInfo &ar } callstack.push_back(tok); + const Token *tok2 = tok->tokAt(2); // 1st parameter.. - if (Token::Match(tok->tokAt(2), "%varid% ,|)", arrayInfo.varid())) + if (Token::Match(tok2, "%varid% ,|)", arrayInfo.varid())) checkFunctionParameter(*tok, 1, arrayInfo, callstack); - else if (Token::Match(tok->tokAt(2), "%varid% + %num% ,|)", arrayInfo.varid())) { - const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok->strAt(4)))); + else if (Token::Match(tok2, "%varid% + %num% ,|)", arrayInfo.varid())) { + const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok2->strAt(2)))); checkFunctionParameter(*tok, 1, ai, callstack); } // goto 2nd parameter and check it.. - for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { - if (tok2->str() == "(") { - tok2 = tok2->link(); - continue; - } - if (tok2->str() == ";" || tok2->str() == ")") - break; - if (tok2->str() == ",") { - if (Token::Match(tok2, ", %varid% ,|)", arrayInfo.varid())) - checkFunctionParameter(*tok, 2, arrayInfo, callstack); - else if (Token::Match(tok2, ", %varid% + %num% ,|)", arrayInfo.varid())) { - const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok2->strAt(3)))); - checkFunctionParameter(*tok, 2, ai, callstack); - } - break; - } + tok2 = tok2->nextArgument(); + if (Token::Match(tok2, "%varid% ,|)", arrayInfo.varid())) + checkFunctionParameter(*tok, 2, arrayInfo, callstack); + else if (Token::Match(tok2, "%varid% + %num% ,|)", arrayInfo.varid())) { + const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok2->strAt(2)))); + checkFunctionParameter(*tok, 2, ai, callstack); } } @@ -1012,7 +989,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vectorstrAt(3)); if (index > size && _settings->isEnabled("portability")) pointerOutOfBoundsError(tok->next(), "buffer"); @@ -1020,7 +997,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vectortokAt(2), tok->strAt(2), false, 0, 0); } } diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index dc92dbba8..86c574074 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -288,31 +288,6 @@ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown) return false; } -bool CheckNullPointer::isPointer(const unsigned int varid) -{ - // Check if given variable is a pointer - const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); - const Variable *variableInfo = symbolDatabase->getVariableFromVarId(varid); - const Token *tok = variableInfo ? variableInfo->typeStartToken() : NULL; - - if (Token::Match(tok, "%type% %type% * %varid% [;)=]", varid)) - return true; - - // maybe not a pointer - if (!Token::Match(tok, "%type% * %varid% [;)=]", varid)) - return false; - - // it is a pointer - if (!tok->previous() || - Token::Match(tok->previous(), "[({};]") || - tok->previous()->isName()) { - return true; - } - - // it is not a pointer - return false; -} - void CheckNullPointer::nullPointerAfterLoop() { const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); @@ -327,12 +302,10 @@ void CheckNullPointer::nullPointerAfterLoop() continue; // Get variable id for the loop variable - const unsigned int varid(tok->tokAt(2)->varId()); - if (varid == 0) - continue; + const Variable* var(symbolDatabase->getVariableFromVarId(tok->tokAt(2)->varId())); // Is variable a pointer? - if (!isPointer(varid)) + if (!var || !var->isPointer()) continue; // Get variable name for the loop variable @@ -371,7 +344,7 @@ void CheckNullPointer::nullPointerAfterLoop() break; // loop variable is found.. - else if (tok2->varId() == varid) { + else if (tok2->varId() == var->varId()) { // dummy variable.. is it unknown if pointer is dereferenced or not? bool unknown = _settings->inconclusive; @@ -445,8 +418,9 @@ void CheckNullPointer::nullPointerLinkedList() ++indentlevel4; else if (tok4->str() == "}") { if (indentlevel4 <= 1) { + const Variable* var = symbolDatabase->getVariableFromVarId(varid); // Is this variable a pointer? - if (isPointer(varid)) + if (var && var->isPointer()) nullPointerError(tok1, varname, tok3->linenr()); break; @@ -580,7 +554,9 @@ void CheckNullPointer::nullPointerStructByDeRefAndChec() // is pointer local? bool isLocal = false; const Variable * var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok1->varId()); - if (var && (var->isLocal() || var->isArgument())) + if (!var) + continue; + if (var->isLocal() || var->isArgument()) isLocal = true; // member function may or may not nullify the pointer if it's global (#2647) @@ -641,7 +617,7 @@ void CheckNullPointer::nullPointerStructByDeRefAndChec() // TODO: false negatives for "if (!p || .." else if (Token::Match(tok2, "if ( !| %varid% )|&&", varid1)) { // Is this variable a pointer? - if (isPointer(varid1)) + if (var->isPointer()) nullPointerError(tok1, varname, tok2->linenr(), inconclusive); break; } @@ -674,13 +650,9 @@ void CheckNullPointer::nullPointerByDeRefAndChec() // Name of pointer const std::string varname(vartok->str()); + const Variable* var = symbolDatabase->getVariableFromVarId(varid); // Check that variable is a pointer.. - if (!isPointer(varid)) - continue; - - // Token where pointer is declared - const Variable *var = symbolDatabase->getVariableFromVarId(varid); - if (!var) + if (!var || !var->isPointer()) continue; const Token * const decltok = var->nameToken(); @@ -785,15 +757,8 @@ void CheckNullPointer::nullPointerByCheckAndDeRef() { // Check if pointer is NULL and then dereference it.. - // used to check if a variable is a pointer. - // TODO: Use isPointer? - std::set pointerVariables; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - if (Token::Match(tok, "* %var% [;,)=]")) - pointerVariables.insert(tok->next()->varId()); - - else if (Token::simpleMatch(tok, "if (")) { + if (Token::simpleMatch(tok, "if (")) { // TODO: investigate false negatives: // - handle "while"? // - if there are logical operators @@ -828,8 +793,9 @@ void CheckNullPointer::nullPointerByCheckAndDeRef() const unsigned int linenr = vartok->linenr(); - // Check if variable is a pointer. TODO: Use isPointer? - if (pointerVariables.find(varid) == pointerVariables.end()) + const Variable* var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(varid); + // Check if variable is a pointer + if (!var || !var->isPointer()) continue; if (Token::Match(vartok->next(), "&& ( %varid% =", varid)) @@ -906,9 +872,9 @@ void CheckNullPointer::nullPointerByCheckAndDeRef() // function call, check if pointer is dereferenced if (Token::Match(tok2, "%var% (")) { - std::list var; - parseFunctionCall(*tok2, var, 0); - for (std::list::const_iterator it = var.begin(); it != var.end(); ++it) { + std::list vars; + parseFunctionCall(*tok2, vars, 0); + for (std::list::const_iterator it = vars.begin(); it != vars.end(); ++it) { if (Token::Match(*it, "%varid% [,)]", varid)) { nullPointerError(*it, pointerName, linenr, inconclusive); break; @@ -932,8 +898,6 @@ void CheckNullPointer::nullPointerByCheckAndDeRef() } // init function (global variables) - const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); - const Variable *var = symbolDatabase->getVariableFromVarId(varid); if (!var || !(var->isLocal() || var->isArgument())) break; } diff --git a/lib/checknullpointer.h b/lib/checknullpointer.h index f747555e6..1a7d2bb58 100644 --- a/lib/checknullpointer.h +++ b/lib/checknullpointer.h @@ -120,12 +120,6 @@ public: private: - /** - * Check if a variable is a pointer - * @param varid variable id for variable - */ - bool isPointer(const unsigned int varid); - /** * @brief Does one part of the check for nullPointer(). * Locate insufficient null-pointer handling after loop diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 0597de815..21a110d34 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -750,24 +750,25 @@ void CheckOther::coutCerrMisusageError(const Token* tok, const std::string& stre // // int y = y; // <- redundant initialization to self //--------------------------------------------------------------------------- +static bool isPOD(const Variable* var) +{ + // TODO: Implement real support for POD definition + return(var && var->nameToken()->previous()->isStandardType()); +} + void CheckOther::checkSelfAssignment() { if (!_settings->isEnabled("style")) return; - // POD variables.. - std::set pod; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - if (tok->isStandardType() && Token::Match(tok->tokAt(2), "[,);]") && tok->next()->varId()) - pod.insert(tok->next()->varId()); - } + const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const char selfAssignmentPattern[] = "%var% = %var% ;|=|)"; const Token *tok = Token::findmatch(_tokenizer->tokens(), selfAssignmentPattern); while (tok) { if (Token::Match(tok->previous(), "[;{}]") && tok->varId() && tok->varId() == tok->tokAt(2)->varId() && - pod.find(tok->varId()) != pod.end()) { + isPOD(symbolDatabase->getVariableFromVarId(tok->varId()))) { bool err = true; // no false positive for 'x = x ? x : 1;' @@ -1512,29 +1513,26 @@ void CheckOther::unreachableCodeError(const Token *tok) //--------------------------------------------------------------------------- // Check for unsigned divisions //--------------------------------------------------------------------------- +static bool isUnsigned(const Variable* var) +{ + return(var && var->typeStartToken()->isUnsigned() && var->typeStartToken() == var->typeEndToken()); +} + void CheckOther::checkUnsignedDivision() { - // Check for "ivar / uvar" and "uvar / ivar" - std::set uvars; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - if (Token::Match(tok, "[{};(,] %type% %var% [;=,)]")) { - if (tok->next()->isUnsigned()) - uvars.insert(tok->tokAt(2)->varId()); - } + const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); - else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / %num%")) { - if (tok->strAt(3)[0] == '-') { - if (uvars.find(tok->next()->varId()) != uvars.end()) { - udivError(tok->next()); - } + // Check for "ivar / uvar" and "uvar / ivar" + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / %num%")) { + if (tok->strAt(3)[0] == '-' && isUnsigned(symbolDatabase->getVariableFromVarId(tok->next()->varId()))) { + udivError(tok->next()); } } else if (Token::Match(tok, "(|[|=|%op% %num% / %var%")) { - if (tok->strAt(1)[0] == '-') { - if (uvars.find(tok->tokAt(3)->varId()) != uvars.end()) { - udivError(tok->next()); - } + if (tok->strAt(1)[0] == '-' && isUnsigned(symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId()))) { + udivError(tok->next()); } } } @@ -1805,6 +1803,11 @@ void CheckOther::passedByValueError(const Token *tok, const std::string &parname //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- +static bool isSignedChar(const Variable* var) +{ + return(var && var->nameToken()->previous()->str() == "char" && !var->nameToken()->previous()->isUnsigned() && var->nameToken()->next()->str() != "["); +} + void CheckOther::checkCharVariable() { if (!_settings->isEnabled("style")) @@ -1813,87 +1816,50 @@ void CheckOther::checkCharVariable() const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - // Declaring the variable.. - if (Token::Match(tok, "[{};(,] const| char *| const| %var% [;=,)]") || - Token::Match(tok, "[{};(,] const| char %var% [")) { - // goto 'char' token - tok = tok->next(); - if (tok->str() == "const") - tok = tok->next(); + if ((tok->str() != ".") && Token::Match(tok->next(), "%var% [ %var% ]")) { + const Variable* var = symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId()); + if (isSignedChar(var)) + charArrayIndexError(tok->next()); + } - // Check for unsigned char - if (tok->isUnsigned()) + else if (Token::Match(tok, "[;{}] %var% = %any% [&|] %any% ;")) { + // is a char variable used in the calculation? + if (!isSignedChar(symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId())) && + !isSignedChar(symbolDatabase->getVariableFromVarId(tok->tokAt(5)->varId()))) continue; - // Set tok to point to the variable name - tok = tok->next(); - const bool isPointer(tok->str() == "*" || tok->strAt(1) == "["); - if (tok->str() == "*") - tok = tok->next(); - if (tok->str() == "const") - tok = tok->next(); - - const unsigned int varid = tok->varId(); - if (!varid) - continue; - - // Check usage of char variable.. - unsigned int indentlevel = 0; - for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { - if (tok2->str() == "{") - ++indentlevel; - - else if (tok2->str() == "}") { - if (!indentlevel) - break; - --indentlevel; - } - - if (!isPointer) { - if ((tok2->str() != ".") && Token::Match(tok2->next(), "%var% [ %varid% ]", varid)) { - charArrayIndexError(tok2->next()); - break; - } - } - - if (Token::Match(tok2, "[;{}] %var% = %any% [&|] %any% ;")) { - // is the char variable used in the calculation? - if (tok2->tokAt(3)->varId() != tok->varId() && tok2->tokAt(5)->varId() != tok->varId()) - continue; - - // it's ok with a bitwise and where the other operand is 0xff or less.. - if (tok2->strAt(4) == "&") { - if (tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3))) - continue; - if (tok2->tokAt(5)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(5))) - continue; - } - - // is the result stored in a short|int|long? - const Variable *var = symbolDatabase->getVariableFromVarId(tok2->next()->varId()); - if (!(var && Token::Match(var->typeEndToken(), "short|int|long"))) - continue; - - // This is an error.. - charBitOpError(tok2); - break; - } - - if (isPointer && Token::Match(tok2, "[;{}] %var% = %any% [&|] ( * %varid% ) ;", tok->varId())) { - // it's ok with a bitwise and where the other operand is 0xff or less.. - if (tok2->strAt(4) == "&" && tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3))) - continue; - - // is the result stored in a short|int|long? - const Variable *var = symbolDatabase->getVariableFromVarId(tok2->next()->varId()); - if (!(var && Token::Match(var->typeEndToken(), "short|int|long"))) - continue; - - // This is an error.. - charBitOpError(tok2); - break; - } + // it's ok with a bitwise and where the other operand is 0xff or less.. + if (tok->strAt(4) == "&") { + if (tok->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok->strAt(3))) + continue; + if (tok->tokAt(5)->isNumber() && MathLib::isGreater("0x100", tok->strAt(5))) + continue; } + + // is the result stored in a short|int|long? + const Variable *var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); + if (!(var && Token::Match(var->typeEndToken(), "short|int|long"))) + continue; + + // This is an error.. + charBitOpError(tok); + } + + else if (Token::Match(tok, "[;{}] %var% = %any% [&|] ( * %var% ) ;")) { + const Variable* var = symbolDatabase->getVariableFromVarId(tok->tokAt(7)->varId()); + if (!var || !var->isPointer()) + continue; + // it's ok with a bitwise and where the other operand is 0xff or less.. + if (tok->strAt(4) == "&" && tok->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok->strAt(3))) + continue; + + // is the result stored in a short|int|long? + var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); + if (!(var && Token::Match(var->typeEndToken(), "short|int|long"))) + continue; + + // This is an error.. + charBitOpError(tok); } } } @@ -1973,6 +1939,11 @@ void CheckOther::constStatementError(const Token *tok, const std::string &type) //--------------------------------------------------------------------------- // str plus char //--------------------------------------------------------------------------- +static bool isChar(const Variable* var) +{ + return(var && var->nameToken()->previous()->str() == "char" && var->nameToken()->next()->str() != "["); +} + void CheckOther::strPlusChar() { // Don't use this check for Java and C# programs.. @@ -1980,25 +1951,17 @@ void CheckOther::strPlusChar() return; } - std::set charVars; + const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - // Declaring char variable.. - if (Token::Match(tok, "char %var% [;=]")) { - unsigned int varid = tok->next()->varId(); - if (varid > 0) - charVars.insert(varid); - } - - // - else if (Token::Match(tok, "[=(] %str% + %any%")) { + if (Token::Match(tok, "[=(] %str% + %any%")) { // char constant.. if (tok->strAt(3)[0] == '\'') strPlusCharError(tok->next()); // char variable.. unsigned int varid = tok->tokAt(3)->varId(); - if (varid > 0 && charVars.find(varid) != charVars.end()) + if (isChar(symbolDatabase->getVariableFromVarId(varid))) strPlusCharError(tok->next()); } } @@ -2102,7 +2065,7 @@ void CheckOther::checkCCTypeFunctions() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->varId() == 0 && - Token::Match(tok, "isalnum|isalpha|iscntrl|isdigit|isgraph|islower|isprint|ispunct|isspace|isupper|isxdigit ( %num% )") && + Token::Match(tok, "isalnum|isalpha|iscntrl|isdigit|isgraph|islower|isprint|ispunct|isspace|isupper|isxdigit ( %num% )") && MathLib::isNegative(tok->strAt(2))) { cctypefunctionCallError(tok, tok->str(), tok->tokAt(2)->str()); } diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 1d8d86aa7..cd1e77c65 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -997,12 +997,6 @@ void CheckStl::missingComparisonError(const Token *incrementToken1, const Token } -static bool isPointer(const SymbolDatabase* symbolDatabase, unsigned int varid) -{ - const Variable* var = symbolDatabase->getVariableFromVarId(varid); - return var && var->nameToken()->previous()->str() == "*"; -} - static bool isLocal(const SymbolDatabase* symbolDatabase, unsigned int varid) { const Variable* var = symbolDatabase->getVariableFromVarId(varid); @@ -1023,13 +1017,16 @@ void CheckStl::string_c_str() // Invalid usage.. if (Token::Match(tok, "throw %var% . c_str ( ) ;") && isLocal(symbolDatabase, tok->next()->varId())) { string_c_strThrowError(tok); - } else if (Token::Match(tok, "[;{}] %var% = %var% . str ( ) . c_str ( ) ;") && isPointer(symbolDatabase, tok->next()->varId())) { - string_c_strError(tok); + } else if (Token::Match(tok, "[;{}] %var% = %var% . str ( ) . c_str ( ) ;")) { + const Variable* var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); + if (var && var->isPointer()) + string_c_strError(tok); } else if (Token::Match(tok, "[;{}] %var% = %var% (") && Token::simpleMatch(tok->linkAt(4), ") . c_str ( ) ;") && - isPointer(symbolDatabase, tok->next()->varId()) && Token::findmatch(_tokenizer->tokens(), ("std :: string " + tok->strAt(3) + " (").c_str())) { - string_c_strError(tok); + const Variable* var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); + if (var && var->isPointer()) + string_c_strError(tok); } // Using c_str() to get the return value is only dangerous if the function returns a char* diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index ab522f99a..67a338408 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -25,8 +25,6 @@ #include "errorlogger.h" #include "check.h" -#include - #include #include #include @@ -111,19 +109,26 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti // unnamed struct and union else if (Token::Match(tok, "struct|union {") && - Token::Match(tok->next()->link(), "} %var% ;|[")) { + Token::Match(tok->next()->link(), "} |* %var% ;|[")) { scopeList.push_back(Scope(this, tok, scope)); Scope *new_scope = &scopeList.back(); std::vector dimensions; + bool isPointer = false; bool isArray = false; - if (tok->next()->link()->strAt(2) == "[") - isArray = arrayDimensions(dimensions, tok->next()->link()->tokAt(2)); + const Token* varNameTok = tok->next()->link()->next(); + if (varNameTok->str() == "*") { + isPointer = true; + varNameTok = varNameTok->next(); + } - scope->addVariable(tok->next()->link()->next(), tok, tok, scope->access, false, false, false, true, new_scope, scope, isArray, dimensions); + if (varNameTok->next()->str() == "[") + isArray = arrayDimensions(dimensions, varNameTok->next()); + + scope->addVariable(varNameTok, tok, tok, scope->access, false, false, false, true, new_scope, scope, isArray, isPointer, dimensions); const Token *tok2 = tok->next(); @@ -1210,20 +1215,14 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Function // check for non-empty argument list "( ... )" if (arg->link() != arg->next() && !Token::simpleMatch(arg, "( void )")) { unsigned int count = 0; - const Token *startTok; - const Token *endTok; - const Token *nameTok; - bool isConstVar; - bool isArrayVar; - bool hasDefault; - const Token *tok = arg->next(); - for (;;) { - startTok = tok; - endTok = NULL; - nameTok = NULL; - isConstVar = bool(tok->str() == "const"); - isArrayVar = false; - hasDefault = false; + + for (const Token* tok = arg->next(); tok; tok = tok->next()) { + const Token* startTok = tok; + const Token* endTok = NULL; + const Token* nameTok = NULL; + bool isConstVar = bool(tok->str() == "const"); + bool isArrayVar = false; + bool hasDefault = false; std::vector dimensions; while (tok->str() != "," && tok->str() != ")" && tok->str() != "=") { @@ -1282,6 +1281,7 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Function argType = symbolDatabase->findVariableType(scope, typeTok); bool isClassVar = startTok == endTok && !startTok->isStandardType(); + bool isPointerVar = nameTok->strAt(-1) == "*" || nameTok->strAt(-2) == "*"; // skip default values if (tok->str() == "=") { @@ -1291,12 +1291,10 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Function tok = tok->next(); } - argumentList.push_back(Variable(nameTok, startTok, endTok, count++, Argument, false, false, isConstVar, isClassVar, argType, functionScope, isArrayVar, hasDefault, dimensions)); + argumentList.push_back(Variable(nameTok, startTok, endTok, count++, Argument, false, false, isConstVar, isClassVar, argType, functionScope, isArrayVar, isPointerVar, hasDefault, dimensions)); if (tok->str() == ")") break; - - tok = tok->next(); } } } @@ -1359,8 +1357,7 @@ Scope::Scope(SymbolDatabase *check_, const Token *classDef_, Scope *nestedIn_) : } } -bool -Scope::hasDefaultConstructor() const +bool Scope::hasDefaultConstructor() const { if (numConstructors) { std::list::const_iterator func; @@ -1548,10 +1545,12 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess) } bool isArray = false; + bool isPointer = false; std::vector dimensions; - if (tok && isVariableDeclaration(tok, vartok, typetok, isArray)) { - isClass = (!typetok->isStandardType() && vartok->previous()->str() != "*"); + if (tok && isVariableDeclaration(tok, vartok, typetok, isArray, isPointer)) { + isPointer = vartok->previous()->str() == "*" || vartok->strAt(-2) == "*"; + isClass = (!typetok->isStandardType() && !isPointer && vartok->previous()->str() != "&"); if (isArray) { isArray = check->arrayDimensions(dimensions, vartok->next()); tok = vartok->next(); @@ -1571,7 +1570,7 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess) if (typetok) scope = check->findVariableType(this, typetok); - addVariable(vartok, typestart, vartok->previous(), varaccess, isMutable, isStatic, isConst, isClass, scope, this, isArray, dimensions); + addVariable(vartok, typestart, vartok->previous(), varaccess, isMutable, isStatic, isConst, isClass, scope, this, isArray, isPointer, dimensions); } return tok; @@ -1607,14 +1606,14 @@ inline const Token* skipPointers(const Token* tok) { const Token* ret = tok; - while (Token::simpleMatch(ret, "*")) { + while (Token::Match(ret, "*|&")) { ret = ret->next(); } return ret; } -bool Scope::isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok, bool &isArray) const +bool Scope::isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok, bool &isArray, bool &isPointer) const { const Token* localTypeTok = skipScopeIdentifiers(tok); const Token* localVarTok = NULL; @@ -1649,6 +1648,7 @@ bool Scope::isVariableDeclaration(const Token* tok, const Token*& vartok, const typetok = localTypeTok; isArray = false; } + isPointer = vartok && (vartok->strAt(-1) == "*" || vartok->strAt(-2) == "*"); return NULL != vartok; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 65a8d9b5f..de6aed777 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -61,7 +61,8 @@ class Variable { fIsConst = (1 << 2), /** @brief const variable */ fIsClass = (1 << 3), /** @brief user defined type */ fIsArray = (1 << 4), /** @brief array variable */ - fHasDefault = (1 << 5) /** @brief function argument with default value */ + fIsPointer = (1 << 5), /** @brief pointer variable */ + fHasDefault = (1 << 6) /** @brief function argument with default value */ }; /** @@ -86,7 +87,7 @@ public: Variable(const Token *name_, const Token *start_, const Token *end_, std::size_t index_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_, const Scope *type_, - const Scope *scope_, bool array_, bool default_, + const Scope *scope_, bool array_, bool pointer_, bool default_, const std::vector &dimensions_) : _name(name_), _start(start_), @@ -101,6 +102,7 @@ public: setFlag(fIsConst, const_); setFlag(fIsClass, class_); setFlag(fIsArray, array_); + setFlag(fIsPointer, pointer_); setFlag(fHasDefault, default_); _dimensions = dimensions_; } @@ -259,6 +261,14 @@ public: return getFlag(fIsArray); } + /** + * Is pointer or array variable. + * @return true if pointer, false otherwise + */ + bool isPointer() const { + return getFlag(fIsPointer); + } + /** * Does variable have a default value. * @return true if has a default falue, false if not @@ -451,11 +461,11 @@ public: void addVariable(const Token *token_, const Token *start_, const Token *end_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_, const Scope *type_, - const Scope *scope_, bool array_, + const Scope *scope_, bool array_, bool pointer_, const std::vector &dimensions_) { varlist.push_back(Variable(token_, start_, end_, varlist.size(), access_, mutable_, static_, const_, class_, - type_, scope_, array_, false, dimensions_)); + type_, scope_, array_, pointer_, false, dimensions_)); } /** @brief initialize varlist */ @@ -497,9 +507,10 @@ private: * @param vartok populated with pointer to the variable token, if found * @param typetok populated with pointer to the type token, if found * @param isArray reference to variable to set if array is found + * @param isPointer reference to variable to set if pointer is found * @return true if tok points to a variable declaration, false otherwise */ - bool isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok, bool &isArray) const; + bool isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok, bool &isArray, bool &isPointer) const; bool isSimpleVariable(const Token* tok) const; bool isArrayVariable(const Token* tok) const; bool findClosingBracket(const Token* tok, const Token*& close) const; diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index e9df251ff..d6cfbb38b 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -38,6 +38,7 @@ public: ,t(NULL) ,found(false) ,isArray(false) + ,isPointer(false) {} virtual void reportOut(const std::string &outmsg) { @@ -51,6 +52,7 @@ private: const Token* t; bool found; bool isArray; + bool isPointer; void reset() { vartok = NULL; @@ -58,6 +60,7 @@ private: t = NULL; found = false; isArray = false; + isPointer = false; } void run() { @@ -132,209 +135,230 @@ private: void test_isVariableDeclarationCanHandleNull() { reset(); - bool result = si.isVariableDeclaration(NULL, vartok, typetok, isArray); + bool result = si.isVariableDeclaration(NULL, vartok, typetok, isArray, isPointer); ASSERT_EQUALS(false, result); ASSERT(NULL == vartok); ASSERT(NULL == typetok); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesSimpleDeclaration() { reset(); givenACodeSampleToTokenize simpleDeclaration("int x;"); - bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("int", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesScopedDeclaration() { reset(); givenACodeSampleToTokenize ScopedDeclaration("::int x;"); - bool result = si.isVariableDeclaration(ScopedDeclaration.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(ScopedDeclaration.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("int", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesStdDeclaration() { reset(); givenACodeSampleToTokenize StdDeclaration("std::string x;"); - bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("string", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesScopedStdDeclaration() { reset(); givenACodeSampleToTokenize StdDeclaration("::std::string x;"); - bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("string", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesManyScopes() { reset(); givenACodeSampleToTokenize manyScopes("AA::BB::CC::DD::EE x;"); - bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("EE", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesPointers() { reset(); givenACodeSampleToTokenize pointer("int* p;"); - bool result = si.isVariableDeclaration(pointer.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(pointer.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("p", vartok->str()); ASSERT_EQUALS("int", typetok->str()); ASSERT(false == isArray); + ASSERT(true == isPointer); } void test_isVariableDeclarationDoesNotIdentifyConstness() { reset(); givenACodeSampleToTokenize constness("const int* cp;"); - bool result = si.isVariableDeclaration(constness.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(constness.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(false, result); ASSERT(NULL == vartok); ASSERT(NULL == typetok); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesFirstOfManyVariables() { reset(); givenACodeSampleToTokenize multipleDeclaration("int first, second;"); - bool result = si.isVariableDeclaration(multipleDeclaration.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(multipleDeclaration.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("first", vartok->str()); ASSERT_EQUALS("int", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesScopedPointerDeclaration() { reset(); givenACodeSampleToTokenize manyScopes("AA::BB::CC::DD::EE* p;"); - bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("p", vartok->str()); ASSERT_EQUALS("EE", typetok->str()); ASSERT(false == isArray); + ASSERT(true == isPointer); } void test_isVariableDeclarationIdentifiesDeclarationWithIndirection() { reset(); givenACodeSampleToTokenize pointerToPointer("int** pp;"); - bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("pp", vartok->str()); ASSERT_EQUALS("int", typetok->str()); ASSERT(false == isArray); + ASSERT(true == isPointer); } void test_isVariableDeclarationIdentifiesDeclarationWithMultipleIndirection() { reset(); givenACodeSampleToTokenize pointerToPointer("int***** p;"); - bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("p", vartok->str()); ASSERT_EQUALS("int", typetok->str()); ASSERT(false == isArray); + ASSERT(true == isPointer); } void test_isVariableDeclarationIdentifiesArray() { reset(); givenACodeSampleToTokenize array("::std::string v[3];"); - bool result = si.isVariableDeclaration(array.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(array.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("v", vartok->str()); ASSERT_EQUALS("string", typetok->str()); ASSERT(true == isArray); + ASSERT(false == isPointer); } void test_isVariableDeclarationIdentifiesOfArrayPointers() { reset(); givenACodeSampleToTokenize array("A *a[5];"); - bool result = si.isVariableDeclaration(array.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(array.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("a", vartok->str()); ASSERT_EQUALS("A", typetok->str()); ASSERT(true == isArray); + ASSERT(true == isPointer); } void isVariableDeclarationIdentifiesTemplatedPointerVariable() { reset(); givenACodeSampleToTokenize var("std::set* chars;"); - bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("chars", vartok->str()); ASSERT_EQUALS("set", typetok->str()); ASSERT(false == isArray); + ASSERT(true == isPointer); } void isVariableDeclarationIdentifiesTemplatedPointerToPointerVariable() { reset(); givenACodeSampleToTokenize var("std::deque*** ints;"); - bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("ints", vartok->str()); ASSERT_EQUALS("deque", typetok->str()); ASSERT(false == isArray); + ASSERT(true == isPointer); } void isVariableDeclarationIdentifiesTemplatedArrayVariable() { reset(); givenACodeSampleToTokenize var("std::deque ints[3];"); - bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("ints", vartok->str()); ASSERT_EQUALS("deque", typetok->str()); ASSERT(true == isArray); + ASSERT(false == isPointer); } void isVariableDeclarationIdentifiesTemplatedVariable() { reset(); givenACodeSampleToTokenize var("std::vector ints;"); - bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("ints", vartok->str()); ASSERT_EQUALS("vector", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void isVariableDeclarationIdentifiesTemplatedVariableIterator() { reset(); givenACodeSampleToTokenize var("std::list::const_iterator floats;"); - bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("floats", vartok->str()); ASSERT_EQUALS("const_iterator", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void isVariableDeclarationIdentifiesNestedTemplateVariable() { reset(); givenACodeSampleToTokenize var("std::deque > intsets;"); - bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(true, result); ASSERT_EQUALS("intsets", vartok->str()); ASSERT_EQUALS("deque", typetok->str()); ASSERT(false == isArray); + ASSERT(false == isPointer); } void isVariableDeclarationDoesNotIdentifyTemplateClass() { reset(); givenACodeSampleToTokenize var("template class SomeClass{};"); - bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok, isArray, isPointer); ASSERT_EQUALS(false, result); ASSERT(false == isArray); + ASSERT(false == isPointer); } void canFindMatchingBracketsNeedsOpen() {