cppcheck/lib/symboldatabase.cpp
Pete Johns 2e61736c73 Refactoring following #2377 (Technically the member function xxx can be const)
symboldatabase now recognises variables with arbitrarily many scopes.

Extracted method isVariableDeclaration()

Added unit tests for isVariableDeclaration in new file testsymboldatabase.cpp

Extracted givenACodeSampleToTokenize helper class into testutils.h to reduce duplication.
2010-12-30 19:46:44 +11:00

1953 lines
61 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2010 Daniel Marjamäki and Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
#include "symboldatabase.h"
#include "tokenize.h"
#include "token.h"
#include "settings.h"
#include "errorlogger.h"
#include "check.h"
#include <locale>
#include <cstring>
#include <string>
#include <sstream>
#include <algorithm>
//---------------------------------------------------------------------------
SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: _tokenizer(tokenizer), _settings(settings), _errorLogger(errorLogger)
{
// find all namespaces (class,struct and namespace)
SpaceInfo *info = new SpaceInfo(this, NULL, NULL);
spaceInfoList.push_back(info);
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
{
// Locate next class
if (Token::Match(tok, "class|struct|namespace %var% [{:]"))
{
SpaceInfo *new_info = new SpaceInfo(this, tok, info);
const Token *tok2 = tok->tokAt(2);
// only create base list for classes and structures
if (new_info->isClassOrStruct())
{
// goto initial '{'
tok2 = initBaseInfo(new_info, tok);
}
new_info->classStart = tok2;
new_info->classEnd = tok2->link();
// make sure we have valid code
if (!new_info->classEnd)
{
delete new_info;
break;
}
info = new_info;
// add namespace
spaceInfoList.push_back(info);
tok = tok2;
}
else
{
// check for end of space
if (tok == info->classEnd)
{
info = info->nestedIn;
continue;
}
// check if in class or structure
else if (info->type == SpaceInfo::Class || info->type == SpaceInfo::Struct)
{
const Token *funcStart = 0;
const Token *argStart = 0;
// What section are we in..
if (tok->str() == "private:")
info->access = Private;
else if (tok->str() == "protected:")
info->access = Protected;
else if (tok->str() == "public:")
info->access = Public;
else if (Token::Match(tok, "public|protected|private %var% :"))
{
if (tok->str() == "private")
info->access = Private;
else if (tok->str() == "protected")
info->access = Protected;
else if (tok->str() == "public")
info->access = Public;
tok = tok->tokAt(2);
}
// class function?
else if (tok->previous()->str() != "::" && isFunction(tok, &funcStart, &argStart))
{
Func function;
// save the function definition argument start '('
function.argDef = argStart;
// save the access type
function.access = info->access;
// save the function name location
function.tokenDef = funcStart;
// operator function
if (function.tokenDef->previous()->str() == "operator")
{
function.isOperator = true;
// 'operator =' is special
if (function.tokenDef->str() == "=")
function.type = Func::OperatorEqual;
}
// class constructor/destructor
else if (function.tokenDef->str() == info->className)
{
if (function.tokenDef->previous()->str() == "~")
function.type = Func::Destructor;
else if ((Token::Match(function.tokenDef, "%var% ( const %var% & )") ||
Token::Match(function.tokenDef, "%var% ( const %var% & %var% )")) &&
function.tokenDef->strAt(3) == info->className)
function.type = Func::CopyConstructor;
else
function.type = Func::Constructor;
if (function.tokenDef->previous()->str() == "explicit")
function.isExplicit = true;
}
// function returning function pointer
else if (tok->str() == "(")
{
function.retFuncPtr = true;
}
const Token *tok1 = tok;
// look for end of previous statement
while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{|public:|protected:|private:"))
{
// virtual function
if (tok1->previous()->str() == "virtual")
{
function.isVirtual = true;
break;
}
// static function
else if (tok1->previous()->str() == "static")
{
function.isStatic = true;
break;
}
// friend function
else if (tok1->previous()->str() == "friend")
{
function.isFriend = true;
break;
}
tok1 = tok1->previous();
}
const Token *end;
if (!function.retFuncPtr)
end = function.argDef->link();
else
end = tok->link()->next()->link();
// const function
if (end->next()->str() == "const")
function.isConst = true;
// pure virtual function
if (Token::Match(end, ") const| = 0 ;"))
function.isPure = true;
// count the number of constructors
if (function.type == Func::Constructor || function.type == Func::CopyConstructor)
info->numConstructors++;
// assume implementation is inline (definition and implementation same)
function.token = function.tokenDef;
// out of line function
if (Token::Match(end, ") const| ;") ||
Token::Match(end, ") const| = 0 ;"))
{
// find the function implementation later
tok = end->next();
info->functionList.push_back(function);
}
// inline function
else
{
function.isInline = true;
function.hasBody = true;
function.arg = function.argDef;
info->functionList.push_back(function);
const Token *tok2 = funcStart;
SpaceInfo *functionOf = info;
addNewFunction(&info, &tok2);
if (info)
info->functionOf = functionOf;
tok = tok2;
}
}
// nested class function?
else if (tok->previous()->str() == "::" && isFunction(tok, &funcStart, &argStart))
addFunction(&info, &tok, argStart);
// friend class declaration?
else if (Token::Match(tok, "friend class| %any% ;"))
{
FriendInfo friendInfo;
friendInfo.name = tok->strAt(1) == "class" ? tok->strAt(2) : tok->strAt(1);
/** @todo fill this in later after parsing is complete */
friendInfo.spaceInfo = 0;
info->friendList.push_back(friendInfo);
}
}
else if (info->type == SpaceInfo::Namespace || info->type == SpaceInfo::Global)
{
const Token *funcStart = 0;
const Token *argStart = 0;
// function?
if (isFunction(tok, &funcStart, &argStart))
{
// has body?
if (Token::Match(argStart->link(), ") const| {|:"))
{
// class function
if (tok->previous() && tok->previous()->str() == "::")
addFunction(&info, &tok, argStart);
// class destructor
else if (tok->previous() && tok->previous()->str() == "~" &&
tok->previous()->previous() && tok->previous()->previous()->str() == "::")
addFunction(&info, &tok, argStart);
// regular function
else
{
Func function;
// save the function definition argument start '('
function.argDef = argStart;
// save the access type
function.access = Public;
// save the function name location
function.tokenDef = funcStart;
function.token = funcStart;
function.isInline = false;
function.hasBody = true;
function.arg = function.argDef;
function.type = Func::Function;
SpaceInfo *old_info = info;
addNewFunction(&info, &tok);
if (info)
old_info->functionList.push_back(function);
// syntax error
else
{
info = old_info;
break;
}
}
}
// function returning function pointer with body
else if (Token::simpleMatch(argStart->link(), ") ) (") &&
Token::Match(argStart->link()->tokAt(2)->link(), ") const| {"))
{
const Token *tok1 = funcStart;
SpaceInfo *old_info = info;
// class function
if (tok1->previous()->str() == "::")
addFunction(&info, &tok1, argStart);
// regular function
else
addNewFunction(&info, &tok1);
// syntax error?
if (!info)
info = old_info;
tok = tok1;
}
}
}
}
}
std::list<SpaceInfo *>::iterator it;
// fill in base class info
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
// skip namespaces and functions
if (!info->isClassOrStruct())
continue;
// finish filling in base class info
for (unsigned int i = 0; i < info->derivedFrom.size(); ++i)
{
std::list<SpaceInfo *>::iterator it1;
for (it1 = spaceInfoList.begin(); it1 != spaceInfoList.end(); ++it1)
{
SpaceInfo *spaceInfo = *it1;
/** @todo handle derived base classes and namespaces */
if (spaceInfo->type == SpaceInfo::Class || spaceInfo->type == SpaceInfo::Struct)
{
// do class names match?
if (spaceInfo->className == info->derivedFrom[i].name)
{
// are they in the same namespace or different namespaces with same name?
if ((spaceInfo->nestedIn == info->nestedIn) ||
((spaceInfo->nestedIn && spaceInfo->nestedIn->type == SpaceInfo::Namespace) &&
(info->nestedIn && info->nestedIn->type == SpaceInfo::Namespace) &&
(spaceInfo->nestedIn->className == info->nestedIn->className)))
{
info->derivedFrom[i].spaceInfo = spaceInfo;
break;
}
}
}
}
}
}
// fill in variable info
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
// skip functions
if (info->type != SpaceInfo::Function)
{
// find variables
info->getVarList();
}
}
// determine if user defined type needs initialization
unsigned int unknowns = 0; // stop checking when there are no unknowns
unsigned int retry = 0; // bail if we don't resolve all the variable types for some reason
do
{
unknowns = 0;
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
if (info->isClassOrStruct() && info->needInitialization == SpaceInfo::Unknown)
{
// check for default constructor
bool hasDefaultConstructor = false;
std::list<SymbolDatabase::Func>::const_iterator func;
for (func = info->functionList.begin(); func != info->functionList.end(); ++func)
{
if (func->type == SymbolDatabase::Func::Constructor)
{
// check for no arguments: func ( )
/** @todo check for arguments with default values someday */
if (func->argDef->next() == func->argDef->link())
{
hasDefaultConstructor = true;
break;
}
}
}
// User defined types with user defined default constructor doesn't need initialization.
// We assume the default constructor initializes everything.
// Another check will figure out if the constructor actually initializes everything.
if (hasDefaultConstructor)
info->needInitialization = SpaceInfo::False;
// check each member variable to see if it needs initialization
else
{
bool needInitialization = false;
bool unknown = false;
std::list<Var>::const_iterator var;
for (var = info->varlist.begin(); var != info->varlist.end(); ++var)
{
if (var->isClass)
{
if (var->type)
{
// does this type need initialization?
if (var->type->needInitialization == SpaceInfo::True)
needInitialization = true;
else if (var->type->needInitialization == SpaceInfo::Unknown)
unknown = true;
}
}
else
needInitialization = true;
}
if (!unknown)
{
if (needInitialization)
info->needInitialization = SpaceInfo::True;
else
info->needInitialization = SpaceInfo::False;
}
if (info->needInitialization == SpaceInfo::Unknown)
unknowns++;
}
}
}
retry++;
}
while (unknowns && retry < 100);
// this shouldn't happen so output a debug warning
if (retry == 100 && _settings->debugwarnings)
{
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
if (info->isClassOrStruct() && info->needInitialization == SpaceInfo::Unknown)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = info->classDef->linenr();
loc.setfile(_tokenizer->file(info->classDef));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"SymbolDatabase::SymbolDatabase couldn't resolve all user defined types.",
"debug");
if (_errorLogger)
_errorLogger->reportErr(errmsg);
else
Check::reportError(errmsg);
}
}
}
}
SymbolDatabase::~SymbolDatabase()
{
std::list<SpaceInfo *>::iterator it;
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
delete *it;
}
bool SymbolDatabase::isFunction(const Token *tok, const Token **funcStart, const Token **argStart) const
{
// function returning function pointer? '... ( ... %var% ( ... ))( ... ) {'
if (tok->str() == "(" &&
tok->link()->previous()->str() == ")" &&
tok->link()->next() &&
tok->link()->next()->str() == "(" &&
tok->link()->next()->link()->next() &&
Token::Match(tok->link()->next()->link()->next(), "{|;|const|="))
{
*funcStart = tok->link()->previous()->link()->previous();
*argStart = tok->link()->previous()->link();
return true;
}
// regular function?
else if (Token::Match(tok, "%var% (") && Token::Match(tok->next()->link(), ") const| ;|{|=|:"))
{
*funcStart = tok;
*argStart = tok->next();
return true;
}
// simple operator?
else if (Token::Match(tok, "operator %any% (") && Token::Match(tok->tokAt(2)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(2);
return true;
}
// operator[] or operator()?
else if (Token::Match(tok, "operator %any% %any% (") && Token::Match(tok->tokAt(3)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(3);
return true;
}
// operator new/delete []?
else if (Token::Match(tok, "operator %any% %any% %any% (") && Token::Match(tok->tokAt(4)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(4);
return true;
}
// complex user defined operator?
else if (Token::Match(tok, "operator %any% %any% %any% %any% (") && Token::Match(tok->tokAt(5)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(5);
return true;
}
return false;
}
bool SymbolDatabase::argsMatch(const SpaceInfo *info, const Token *first, const Token *second, const std::string &path, unsigned int depth) const
{
bool match = false;
while (first->str() == second->str())
{
// at end of argument list
if (first->str() == ")")
{
match = true;
break;
}
// skip default value assignment
else if (first->next()->str() == "=")
first = first->tokAt(2);
// definition missing variable name
else if (first->next()->str() == "," && second->next()->str() != ",")
second = second->next();
else if (first->next()->str() == ")" && second->next()->str() != ")")
second = second->next();
// function missing variable name
else if (second->next()->str() == "," && first->next()->str() != ",")
first = first->next();
else if (second->next()->str() == ")" && first->next()->str() != ")")
first = first->next();
// argument list has different number of arguments
else if (second->str() == ")")
break;
// variable names are different
else if ((Token::Match(first->next(), "%var% ,|)|=") &&
Token::Match(second->next(), "%var% ,|)")) &&
(first->next()->str() != second->next()->str()))
{
// skip variable names
first = first->next();
second = second->next();
// skip default value assignment
if (first->next()->str() == "=")
first = first->tokAt(2);
}
// variable with class path
else if (depth && Token::Match(first->next(), "%var%"))
{
std::string param = path + first->next()->str();
if (Token::Match(second->next(), param.c_str()))
{
second = second->tokAt(int(depth) * 2);
}
else if (depth > 1)
{
std::string short_path = path;
// remove last " :: "
short_path.resize(short_path.size() - 4);
// remove last name
while (!short_path.empty() && short_path[short_path.size() - 1] != ' ')
short_path.resize(short_path.size() - 1);
param = short_path + first->next()->str();
if (Token::Match(second->next(), param.c_str()))
{
second = second->tokAt((int(depth) - 1) * 2);
}
}
}
// nested class variable
else if (depth == 0 && Token::Match(first->next(), "%var%") &&
second->next()->str() == info->className && second->strAt(2) == "::" &&
first->next()->str() == second->strAt(3))
{
second = second->tokAt(2);
}
first = first->next();
second = second->next();
}
return match;
}
void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Token *argStart)
{
int count = 0;
bool added = false;
std::string path;
unsigned int path_length = 0;
const Token *tok1;
// skip class/struct name
if ((*tok)->previous()->str() == "~")
tok1 = (*tok)->tokAt(-3);
else
tok1 = (*tok)->tokAt(-2);
// back up to head of path
while (tok1 && tok1->previous() && tok1->previous()->str() == "::")
{
path = tok1->str() + " :: " + path;
tok1 = tok1->tokAt(-2);
count++;
path_length++;
}
if (count)
{
path = tok1->str() + " :: " + path;
path_length++;
}
std::list<SpaceInfo *>::iterator it1;
// search for match
for (it1 = spaceInfoList.begin(); it1 != spaceInfoList.end(); ++it1)
{
SpaceInfo *info1 = *it1;
bool match = false;
if (info1->className == tok1->str() && (info1->type != SpaceInfo::Function))
{
// do the spaces match (same space) or do their names match (multiple namespaces)
if ((*info == info1->nestedIn) || (*info && info1 &&
(*info)->className == info1->nestedIn->className &&
!(*info)->className.empty() &&
(*info)->type == info1->nestedIn->type))
{
SpaceInfo *info2 = info1;
while (info2 && count > 0)
{
count--;
tok1 = tok1->tokAt(2);
info2 = info2->findInNestedList(tok1->str());
}
if (count == 0 && info2)
{
match = true;
info1 = info2;
}
}
}
if (match)
{
std::list<Func>::iterator func;
for (func = info1->functionList.begin(); func != info1->functionList.end(); ++func)
{
if (!func->hasBody)
{
if (func->isOperator &&
(*tok)->str() == "operator" &&
func->tokenDef->str() == (*tok)->strAt(1))
{
if (argsMatch(info1, func->tokenDef->tokAt(2), (*tok)->tokAt(3), path, path_length))
{
func->hasBody = true;
func->token = (*tok)->next();
func->arg = argStart;
}
}
else if (func->type == SymbolDatabase::Func::Destructor &&
(*tok)->previous()->str() == "~" &&
func->tokenDef->str() == (*tok)->str())
{
if (argsMatch(info1, func->tokenDef->next(), (*tok)->next(), path, path_length))
{
func->hasBody = true;
func->token = *tok;
func->arg = argStart;
}
}
else if (func->tokenDef->str() == (*tok)->str() && (*tok)->previous()->str() != "~")
{
if (argsMatch(info1, func->tokenDef->next(), (*tok)->next(), path, path_length))
{
// normal function?
if (!func->retFuncPtr && (*tok)->next()->link())
{
if ((func->isConst && (*tok)->next()->link()->next()->str() == "const") ||
(!func->isConst && (*tok)->next()->link()->next()->str() != "const"))
{
func->hasBody = true;
func->token = *tok;
func->arg = argStart;
}
}
// function returning function pointer?
else if (func->retFuncPtr)
{
// todo check for const
func->hasBody = true;
func->token = *tok;
func->arg = argStart;
}
}
}
if (func->hasBody)
{
addNewFunction(info, tok);
if (info)
{
(*info)->functionOf = info1;
added = true;
}
break;
}
}
}
}
}
// check for class function for unknown class
if (!added)
addNewFunction(info, tok);
}
void SymbolDatabase::addNewFunction(SymbolDatabase::SpaceInfo **info, const Token **tok)
{
const Token *tok1 = *tok;
SpaceInfo *new_info = new SpaceInfo(this, tok1, *info);
// skip to start of function
while (tok1 && tok1->str() != "{")
tok1 = tok1->next();
if (tok1)
{
new_info->classStart = tok1;
new_info->classEnd = tok1->link();
// syntax error?
if (!new_info->classEnd)
{
delete new_info;
while (tok1->next())
tok1 = tok1->next();
*info = NULL;
*tok = tok1;
return;
}
*info = new_info;
// add space
spaceInfoList.push_back(new_info);
*tok = tok1;
}
}
const Token *SymbolDatabase::initBaseInfo(SpaceInfo *info, const Token *tok)
{
// goto initial '{'
const Token *tok2 = tok->tokAt(2);
int level = 0;
while (tok2 && tok2->str() != "{")
{
// skip unsupported templates
if (tok2->str() == "<")
level++;
else if (tok2->str() == ">")
level--;
// check for base classes
else if (level == 0 && Token::Match(tok2, ":|,"))
{
BaseInfo base;
tok2 = tok2->next();
if (tok2->str() == "public")
{
base.access = Public;
tok2 = tok2->next();
}
else if (tok2->str() == "protected")
{
base.access = Protected;
tok2 = tok2->next();
}
else if (tok2->str() == "private")
{
base.access = Private;
tok2 = tok2->next();
}
else
{
if (tok->str() == "class")
base.access = Private;
else if (tok->str() == "struct")
base.access = Public;
}
// handle derived base classes
while (Token::Match(tok2, "%var% ::"))
{
base.name += tok2->str();
base.name += " :: ";
tok2 = tok2->tokAt(2);
}
base.name += tok2->str();
base.spaceInfo = 0;
// don't add unhandled templates
if (tok2->next()->str() == "<")
{
int level1 = 1;
while (tok2->next())
{
if (tok2->next()->str() == ">")
{
level1--;
if (level == 0)
break;
}
else if (tok2->next()->str() == "<")
level1++;
tok2 = tok2->next();
}
}
// save pattern for base class name
else
{
info->derivedFrom.push_back(base);
}
}
tok2 = tok2->next();
}
return tok2;
}
//---------------------------------------------------------------------------
SymbolDatabase::SpaceInfo::SpaceInfo(SymbolDatabase *check_, const Token *classDef_, SymbolDatabase::SpaceInfo *nestedIn_) :
check(check_),
classDef(classDef_),
classStart(NULL),
classEnd(NULL),
nestedIn(nestedIn_),
numConstructors(0),
needInitialization(SpaceInfo::Unknown),
functionOf(NULL)
{
if (!classDef)
{
type = SpaceInfo::Global;
access = Public;
}
else if (classDef->str() == "class")
{
type = SpaceInfo::Class;
className = classDef->next()->str();
access = Private;
}
else if (classDef->str() == "struct")
{
type = SpaceInfo::Struct;
className = classDef->next()->str();
access = Public;
}
else if (classDef->str() == "union")
{
type = SpaceInfo::Union;
className = classDef->next()->str();
access = Public;
}
else if (classDef->str() == "namespace")
{
type = SpaceInfo::Namespace;
className = classDef->next()->str();
access = Public;
}
else
{
type = SpaceInfo::Function;
className = classDef->str();
access = Public;
}
if (nestedIn)
nestedIn->nestedList.push_back(this);
}
bool
SymbolDatabase::SpaceInfo::hasDefaultConstructor() const
{
if (numConstructors)
{
std::list<Func>::const_iterator func;
for (func = functionList.begin(); func != functionList.end(); ++func)
{
if (func->type == Func::Constructor &&
func->argDef->link() == func->argDef->next())
return true;
}
}
return false;
}
// Get variable list..
void SymbolDatabase::SpaceInfo::getVarList()
{
AccessControl varaccess = type == Class ? Private : Public;
const Token *start;
if (classStart)
start = classStart->next();
else
start = check->_tokenizer->tokens();
for (const Token *tok = start; tok; tok = tok->next())
{
// end of space?
if (tok->str() == "}")
break;
// syntax error?
else if (tok->next() == NULL)
break;
// Is it a function?
else if (tok->str() == "{")
{
tok = tok->link();
// syntax error?
if (!tok)
return;
continue;
}
// Is it a nested class or structure?
else if (Token::Match(tok, "class|struct|union|namespace %type% :|{"))
{
tok = tok->tokAt(2);
while (tok && tok->str() != "{")
tok = tok->next();
if (tok)
{
// skip implementation
tok = tok->link();
continue;
}
else
break;
}
// Borland C++: Skip all variables in the __published section.
// These are automatically initialized.
else if (tok->str() == "__published:")
{
for (; tok; tok = tok->next())
{
if (tok->str() == "{")
tok = tok->link();
if (Token::Match(tok->next(), "private:|protected:|public:"))
break;
}
if (tok)
continue;
else
break;
}
// "private:" "public:" "protected:" etc
else if (tok->str() == "public:")
{
varaccess = Public;
continue;
}
else if (tok->str() == "protected:")
{
varaccess = Protected;
continue;
}
else if (tok->str() == "private:")
{
varaccess = Private;
continue;
}
// Is it a forward declaration?
else if (Token::Match(tok, "class|struct|union %var% ;"))
{
tok = tok->tokAt(2);
continue;
}
// Borland C++: Ignore properties..
else if (tok->str() == "__property")
continue;
// Search for start of statement..
else if (!tok->previous() || !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:"))
continue;
else if (Token::Match(tok, ";|{|}"))
continue;
// This is the start of a statement
const Token *vartok = NULL;
const Token *typetok = NULL;
// Is it const..?
bool isConst = false;
if (tok->str() == "const")
{
tok = tok->next();
isConst = true;
}
// Is it a static variable?
const bool isStatic(Token::simpleMatch(tok, "static"));
if (isStatic)
{
tok = tok->next();
}
// Is it a mutable variable?
const bool isMutable(Token::simpleMatch(tok, "mutable"));
if (isMutable)
{
tok = tok->next();
}
// Is it const..?
if (tok->str() == "const")
{
tok = tok->next();
isConst = true;
}
bool isClass = false;
if (isVariableDeclaration(tok, vartok))
{
typetok = vartok->previous();
isClass = (!typetok->isStandardType());
tok = vartok->next();
}
// Structure?
else if (Token::Match(tok, "struct|union %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(2);
typetok = vartok->previous();
tok = vartok->next();
}
// Pointer?
else if (Token::Match(tok, "%type% * %var% ;"))
{
vartok = tok->tokAt(2);
typetok = tok;
tok = vartok->next();
}
else if (Token::Match(tok, "%type% %type% * %var% ;"))
{
vartok = tok->tokAt(3);
typetok = vartok->tokAt(-2);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% * %var% ;"))
{
vartok = tok->tokAt(4);
typetok = vartok->tokAt(-2);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% :: %type% * %var% ;"))
{
vartok = tok->tokAt(6);
typetok = vartok->tokAt(-2);
tok = vartok->next();
}
// Array?
else if (Token::Match(tok, "%type% %var% [") && tok->next()->str() != "operator")
{
if (!tok->isStandardType())
{
isClass = true;
typetok = tok;
}
vartok = tok->next();
tok = vartok->next()->link()->next();
}
// Pointer array?
else if (Token::Match(tok, "%type% * %var% ["))
{
vartok = tok->tokAt(2);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% * %var% ["))
{
vartok = tok->tokAt(4);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% :: %type% * %var% ["))
{
vartok = tok->tokAt(6);
tok = vartok->next();
}
// Container..
else if (Token::Match(tok, ":: %type% :: %type% :: %type% <") ||
Token::Match(tok, "%type% :: %type% :: %type% <") ||
Token::Match(tok, ":: %type% :: %type% <") ||
Token::Match(tok, "%type% :: %type% <") ||
Token::Match(tok, ":: %type% <") ||
Token::Match(tok, "%type% <"))
{
// got an unhandled template?
if (tok->str() == "template")
continue;
// find matching ">"
int level = 0;
const Token *tok1 = NULL;
for (; tok; tok = tok->next())
{
if (tok->str() == "<")
{
if (level == 0)
tok1 = tok->previous();
level++;
}
else if (tok->str() == ">")
{
level--;
if (level == 0)
break;
}
else if (tok->str() == ">>")
{
level-=2;
if (level <= 0)
break;
}
else if (tok->str() == "(")
tok = tok->link();
// don't crash on unhandled templates
if (tok->next() == NULL)
break;
}
if (tok && (Token::Match(tok, "> %var% ;") || Token::Match(tok, ">> %var% ;")))
{
isClass = true;
vartok = tok->next();
typetok = tok1;
tok = vartok->next();
}
else if (tok && (Token::Match(tok, "> :: %type% %var% ;") || Token::Match(tok, ">> :: %type% %var% ;")))
{
isClass = true;
vartok = tok->tokAt(3);
typetok = vartok->previous();
tok = vartok->next();
}
else if (tok && (Token::Match(tok, "> * %var% ;") || Token::Match(tok, ">> * %var% ;")))
{
vartok = tok->tokAt(2);
tok = vartok->next();
}
}
// If the vartok was set in the if-blocks above, create a entry for this variable..
if (vartok && vartok->str() != "operator")
{
if (vartok->varId() == 0 && check->_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = vartok->linenr();
loc.setfile(check->_tokenizer->file(vartok));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"SymbolDatabase::SpaceInfo::getVarList found variable \'" + vartok->str() + "\' with varid 0.",
"debug");
if (check->_errorLogger)
check->_errorLogger->reportErr(errmsg);
else
Check::reportError(errmsg);
}
const SpaceInfo *spaceInfo = NULL;
if (typetok)
spaceInfo = check->findVarType(this, typetok);
addVar(vartok, varaccess, isMutable, isStatic, isConst, isClass, spaceInfo);
}
}
}
bool SymbolDatabase::SpaceInfo::isVariableDeclaration(const Token* tok, const Token*& vartok) const
{
if (Token::simpleMatch(tok, "::"))
{
tok = tok->next();
}
while (Token::Match(tok, "%type% :: "))
{
tok = tok->tokAt(2);
}
if (Token::Match(tok, "%type% %var% ;"))
{
vartok = tok->next();
}
return NULL != vartok;
}
//---------------------------------------------------------------------------
const SymbolDatabase::SpaceInfo *SymbolDatabase::findVarType(const SpaceInfo *start, const Token *type) const
{
std::list<SpaceInfo *>::const_iterator it;
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
const SpaceInfo *info = *it;
// skip namespaces and functions
if (info->type == SpaceInfo::Namespace || info->type == SpaceInfo::Function || info->type == SpaceInfo::Global)
continue;
// do the names match?
if (info->className == type->str())
{
// check if type does not have a namespace
if (type->previous()->str() != "::")
{
const SpaceInfo *parent = start;
// check if in same namespace
while (parent && parent != info->nestedIn)
parent = parent->nestedIn;
if (info->nestedIn == parent)
return info;
}
// type has a namespace
else
{
// FIXME check if namespace path matches supplied path
return info;
}
}
}
return NULL;
}
//---------------------------------------------------------------------------
SymbolDatabase::SpaceInfo * SymbolDatabase::SpaceInfo::findInNestedList(const std::string & name)
{
std::list<SpaceInfo *>::iterator it;
for (it = nestedList.begin(); it != nestedList.end(); ++it)
{
if ((*it)->className == name)
return (*it);
}
return 0;
}
//---------------------------------------------------------------------------
const SymbolDatabase::Func *SymbolDatabase::SpaceInfo::getDestructor() const
{
std::list<Func>::const_iterator it;
for (it = functionList.begin(); it != functionList.end(); ++it)
{
if (it->type == Func::Destructor)
return &*it;
}
return 0;
}
//---------------------------------------------------------------------------
unsigned int SymbolDatabase::SpaceInfo::getNestedNonFunctions() const
{
unsigned int nested = 0;
std::list<SpaceInfo *>::const_iterator ni;
for (ni = nestedList.begin(); ni != nestedList.end(); ++ni)
{
if ((*ni)->type != SpaceInfo::Function)
nested++;
}
return nested;
}
//---------------------------------------------------------------------------
void SymbolDatabase::SpaceInfo::assignVar(const std::string &varname)
{
std::list<Var>::iterator i;
for (i = varlist.begin(); i != varlist.end(); ++i)
{
if (i->token->str() == varname)
{
i->assign = true;
return;
}
}
}
void SymbolDatabase::SpaceInfo::initVar(const std::string &varname)
{
std::list<Var>::iterator i;
for (i = varlist.begin(); i != varlist.end(); ++i)
{
if (i->token->str() == varname)
{
i->init = true;
return;
}
}
}
void SymbolDatabase::SpaceInfo::assignAllVar()
{
std::list<Var>::iterator i;
for (i = varlist.begin(); i != varlist.end(); ++i)
i->assign = true;
}
void SymbolDatabase::SpaceInfo::clearAllVar()
{
std::list<Var>::iterator i;
for (i = varlist.begin(); i != varlist.end(); ++i)
{
i->assign = false;
i->init = false;
}
}
//---------------------------------------------------------------------------
bool SymbolDatabase::SpaceInfo::isBaseClassFunc(const Token *tok)
{
// Iterate through each base class...
for (unsigned int i = 0; i < derivedFrom.size(); ++i)
{
const SpaceInfo *info = derivedFrom[i].spaceInfo;
// Check if base class exists in database
if (info)
{
std::list<Func>::const_iterator it;
for (it = info->functionList.begin(); it != info->functionList.end(); ++it)
{
if (it->tokenDef->str() == tok->str())
return true;
}
}
// Base class not found so assume it is in it.
else
return true;
}
return false;
}
void SymbolDatabase::SpaceInfo::initializeVarList(const Func &func, std::list<std::string> &callstack)
{
bool Assign = false;
unsigned int indentlevel = 0;
const Token *ftok = func.token;
for (; ftok; ftok = ftok->next())
{
if (!ftok->next())
break;
// Class constructor.. initializing variables like this
// clKalle::clKalle() : var(value) { }
if (indentlevel == 0)
{
if (Assign && Token::Match(ftok, "%var% ("))
{
initVar(ftok->str());
// assignment in the initializer..
// : var(value = x)
if (Token::Match(ftok->tokAt(2), "%var% ="))
assignVar(ftok->strAt(2));
}
Assign |= (ftok->str() == ":");
}
if (ftok->str() == "{")
{
++indentlevel;
Assign = false;
}
else if (ftok->str() == "}")
{
if (indentlevel <= 1)
break;
--indentlevel;
}
if (indentlevel < 1)
continue;
// Variable getting value from stream?
if (Token::Match(ftok, ">> %var%"))
{
assignVar(ftok->strAt(1));
}
// Before a new statement there is "[{};)=]"
if (! Token::Match(ftok, "[{};()=]"))
continue;
if (Token::simpleMatch(ftok, "( !"))
ftok = ftok->next();
// Using the operator= function to initialize all variables..
if (Token::simpleMatch(ftok->next(), "* this = "))
{
assignAllVar();
break;
}
// Calling member variable function?
if (Token::Match(ftok->next(), "%var% . %var% ("))
{
std::list<Var>::const_iterator var;
for (var = varlist.begin(); var != varlist.end(); ++var)
{
if (var->token->varId() == ftok->next()->varId())
{
/** @todo false negative: we assume function changes variable state */
assignVar(ftok->next()->str());
continue;
}
}
ftok = ftok->tokAt(2);
}
if (!Token::Match(ftok->next(), "%var%") &&
!Token::Match(ftok->next(), "this . %var%") &&
!Token::Match(ftok->next(), "* %var% =") &&
!Token::Match(ftok->next(), "( * this ) . %var%"))
continue;
// Goto the first token in this statement..
ftok = ftok->next();
// Skip "( * this )"
if (Token::simpleMatch(ftok, "( * this ) ."))
{
ftok = ftok->tokAt(5);
}
// Skip "this->"
if (Token::simpleMatch(ftok, "this ."))
ftok = ftok->tokAt(2);
// Skip "classname :: "
if (Token::Match(ftok, "%var% ::"))
ftok = ftok->tokAt(2);
// Clearing all variables..
if (Token::simpleMatch(ftok, "memset ( this ,"))
{
assignAllVar();
return;
}
// Clearing array..
else if (Token::Match(ftok, "memset ( %var% ,"))
{
assignVar(ftok->strAt(2));
ftok = ftok->next()->link();
continue;
}
// Calling member function?
else if (Token::Match(ftok, "%var% (") && ftok->str() != "if")
{
// Passing "this" => assume that everything is initialized
for (const Token *tok2 = ftok->next()->link(); tok2 && tok2 != ftok; tok2 = tok2->previous())
{
if (tok2->str() == "this")
{
assignAllVar();
return;
}
}
// recursive call / calling overloaded function
// assume that all variables are initialized
if (std::find(callstack.begin(), callstack.end(), ftok->str()) != callstack.end())
{
assignAllVar();
return;
}
// check if member function
std::list<Func>::const_iterator it;
for (it = functionList.begin(); it != functionList.end(); ++it)
{
if (ftok->str() == it->tokenDef->str() && it->type != Func::Constructor)
break;
}
// member function found
if (it != functionList.end())
{
// member function has implementation
if (it->hasBody)
{
// initialize variable use list using member function
callstack.push_back(ftok->str());
initializeVarList(*it, callstack);
callstack.pop_back();
}
// there is a called member function, but it has no implementation, so we assume it initializes everything
else
{
assignAllVar();
}
}
// not member function
else
{
// could be a base class virtual function, so we assume it initializes everything
if (func.type != Func::Constructor && isBaseClassFunc(ftok))
{
/** @todo False Negative: we should look at the base class functions to see if they
* call any derived class virtual functions that change the derived class state
*/
assignAllVar();
}
// has friends, so we assume it initializes everything
if (!friendList.empty())
assignAllVar();
// the function is external and it's neither friend nor inherited virtual function.
// assume all variables that are passed to it are initialized..
else
{
unsigned int indentlevel2 = 0;
for (const Token *tok = ftok->tokAt(2); tok; tok = tok->next())
{
if (tok->str() == "(")
++indentlevel2;
else if (tok->str() == ")")
{
if (indentlevel2 == 0)
break;
--indentlevel2;
}
if (tok->isName())
{
assignVar(tok->str());
}
}
}
}
}
// Assignment of member variable?
else if (Token::Match(ftok, "%var% ="))
{
assignVar(ftok->str());
}
// Assignment of array item of member variable?
else if (Token::Match(ftok, "%var% [ %any% ] ="))
{
assignVar(ftok->str());
}
// Assignment of member of array item of member variable?
else if (Token::Match(ftok, "%var% [ %any% ] . %var% =") ||
Token::Match(ftok, "%var% [ %any% ] . %var% . %var% ="))
{
assignVar(ftok->str());
}
// Assignment of array item of member variable?
else if (Token::Match(ftok, "%var% [ %any% ] [ %any% ] ="))
{
assignVar(ftok->str());
}
// Assignment of array item of member variable?
else if (Token::Match(ftok, "* %var% ="))
{
assignVar(ftok->next()->str());
}
// Assignment of struct member of member variable?
else if (Token::Match(ftok, "%var% . %any% ="))
{
assignVar(ftok->str());
}
// The functions 'clear' and 'Clear' are supposed to initialize variable.
if (Token::Match(ftok, "%var% . clear|Clear ("))
{
assignVar(ftok->str());
}
}
}
bool SymbolDatabase::isMemberVar(const SymbolDatabase::SpaceInfo *info, const Token *tok)
{
const Token *tok1 = tok;
while (tok->previous() && !Token::Match(tok->previous(), "}|{|;|public:|protected:|private:|return|:|?"))
{
if (Token::Match(tok->previous(), "* this"))
return true;
tok = tok->previous();
}
if (tok->str() == "this")
return true;
if (Token::Match(tok, "( * %var% ) [") || (Token::Match(tok, "( * %var% ) <<") && tok1->next()->str() == "<<"))
tok = tok->tokAt(2);
// ignore class namespace
if (tok->str() == info->className && tok->next()->str() == "::")
tok = tok->tokAt(2);
std::list<Var>::const_iterator var;
for (var = info->varlist.begin(); var != info->varlist.end(); ++var)
{
if (var->token->str() == tok->str())
{
return !var->isMutable;
}
}
// not found in this class
if (!info->derivedFrom.empty())
{
// check each base class
for (unsigned int i = 0; i < info->derivedFrom.size(); ++i)
{
// find the base class
const SpaceInfo *spaceInfo = info->derivedFrom[i].spaceInfo;
// find the function in the base class
if (spaceInfo)
{
if (isMemberVar(spaceInfo, tok))
return true;
}
}
}
return false;
}
bool SymbolDatabase::isConstMemberFunc(const SymbolDatabase::SpaceInfo *info, const Token *tok)
{
std::list<Func>::const_iterator func;
for (func = info->functionList.begin(); func != info->functionList.end(); ++func)
{
if (func->tokenDef->str() == tok->str() && func->isConst)
return true;
}
// not found in this class
if (!info->derivedFrom.empty())
{
// check each base class
for (unsigned int i = 0; i < info->derivedFrom.size(); ++i)
{
// find the base class
const SymbolDatabase::SpaceInfo *spaceInfo = info->derivedFrom[i].spaceInfo;
// find the function in the base class
if (spaceInfo)
{
if (isConstMemberFunc(spaceInfo, tok))
return true;
}
}
}
return false;
}
bool SymbolDatabase::checkConstFunc(const SymbolDatabase::SpaceInfo *info, const Token *tok)
{
// if the function doesn't have any assignment nor function call,
// it can be a const function..
unsigned int indentlevel = 0;
bool isconst = true;
for (const Token *tok1 = tok; tok1; tok1 = tok1->next())
{
if (tok1->str() == "{")
++indentlevel;
else if (tok1->str() == "}")
{
if (indentlevel <= 1)
break;
--indentlevel;
}
// assignment.. = += |= ..
else if (tok1->str() == "=" ||
(tok1->str().find("=") == 1 &&
tok1->str().find_first_of("<!>") == std::string::npos))
{
if (tok1->previous()->varId() == 0 && !info->derivedFrom.empty())
{
isconst = false;
break;
}
else if (isMemberVar(info, tok1->previous()))
{
isconst = false;
break;
}
else if (tok1->previous()->str() == "]")
{
// TODO: I assume that the assigned variable is a member variable
// don't assume it
isconst = false;
break;
}
else if (tok1->next()->str() == "this")
{
isconst = false;
break;
}
// FIXME: I assume that a member union/struct variable is assigned.
else if (Token::Match(tok1->tokAt(-2), ". %var%"))
{
isconst = false;
break;
}
}
// streaming: <<
else if (tok1->str() == "<<" && isMemberVar(info, tok1->previous()))
{
isconst = false;
break;
}
// increment/decrement (member variable?)..
else if (Token::Match(tok1, "++|--"))
{
isconst = false;
break;
}
// function call..
else if (Token::Match(tok1, "%var% (") &&
!(Token::Match(tok1, "return|c_str|if|string") || tok1->isStandardType()))
{
if (!isConstMemberFunc(info, tok1))
{
isconst = false;
break;
}
}
else if (Token::Match(tok1, "%var% < %any% > ("))
{
isconst = false;
break;
}
// delete..
else if (tok1->str() == "delete")
{
isconst = false;
break;
}
}
return isconst;
}
//---------------------------------------------------------------------------
// check if this function is defined virtual in the base classes
bool SymbolDatabase::isVirtualFunc(const SymbolDatabase::SpaceInfo *info, const Token *functionToken) const
{
// check each base class
for (unsigned int i = 0; i < info->derivedFrom.size(); ++i)
{
// check if base class exists in database
if (info->derivedFrom[i].spaceInfo)
{
const SymbolDatabase::SpaceInfo *derivedFrom = info->derivedFrom[i].spaceInfo;
std::list<SymbolDatabase::Func>::const_iterator func;
// check if function defined in base class
for (func = derivedFrom->functionList.begin(); func != derivedFrom->functionList.end(); ++func)
{
if (func->isVirtual)
{
const Token *tok = func->tokenDef;
if (tok->str() == functionToken->str())
{
const Token *temp1 = tok->previous();
const Token *temp2 = functionToken->previous();
bool returnMatch = true;
// check for matching return parameters
while (temp1->str() != "virtual")
{
if (temp1->str() != temp2->str())
{
returnMatch = false;
break;
}
temp1 = temp1->previous();
temp2 = temp2->previous();
}
// check for matching function parameters
if (returnMatch && argsMatch(info, tok->tokAt(2), functionToken->tokAt(2), std::string(""), 0))
{
return true;
}
}
}
}
if (!derivedFrom->derivedFrom.empty())
{
if (isVirtualFunc(derivedFrom, functionToken))
return true;
}
}
else
{
// unable to find base class so assume it has a virtual function
return true;
}
}
return false;
}