Improved support for references and pointers in SymbolDatabase

Replaced several isPointer functions by Variable::isPointer function
Refactorizations & Make use of symbolDatabase more often
This commit is contained in:
PKEuS 2011-12-17 19:04:03 +01:00 committed by Daniel Marjamäki
parent f09a5b408b
commit f306246c7f
8 changed files with 205 additions and 275 deletions

View File

@ -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::vector<std::str
}
// undefined behaviour: result of pointer arithmetic is out of bounds
if (varid && Token::Match(tok, "= %varid% + %num% ;", varid)) {
else if (varid && Token::Match(tok, "= %varid% + %num% ;", varid)) {
const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(3));
if (index > size && _settings->isEnabled("portability"))
pointerOutOfBoundsError(tok->next(), "buffer");
@ -1020,7 +997,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
pointerIsOutOfBounds = true;
}
if (pointerIsOutOfBounds && Token::Match(tok, "[;{}=] * %varid% [;=]", varid)) {
else if (pointerIsOutOfBounds && Token::Match(tok, "[;{}=] * %varid% [;=]", varid)) {
outOfBoundsError(tok->tokAt(2), tok->strAt(2), false, 0, 0);
}
}

View File

@ -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<unsigned int> 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<const Token *> var;
parseFunctionCall(*tok2, var, 0);
for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) {
std::list<const Token *> vars;
parseFunctionCall(*tok2, vars, 0);
for (std::list<const Token *>::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;
}

View File

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

View File

@ -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<unsigned int> 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<unsigned int> 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<unsigned int> 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());
}

View File

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

View File

@ -25,8 +25,6 @@
#include "errorlogger.h"
#include "check.h"
#include <locale>
#include <cstring>
#include <string>
#include <sstream>
@ -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<Dimension> 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<Dimension> 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<Function>::const_iterator func;
@ -1548,10 +1545,12 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess)
}
bool isArray = false;
bool isPointer = false;
std::vector<Dimension> 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;
}

View File

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

View File

@ -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<char>* 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<int>*** 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<int> 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<int> 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<int>::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<std::set<int> > 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 T> 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() {