Symbol database: add global space and add all global functions and variables to it. Ticket: #2198

This commit is contained in:
Robert Reif 2010-11-20 07:26:50 +01:00 committed by Daniel Marjamäki
parent 3fba8b52c0
commit 27cae2f0d7
3 changed files with 240 additions and 190 deletions

View File

@ -79,8 +79,8 @@ void CheckClass::constructors()
{ {
SymbolDatabase::SpaceInfo *info = *i; SymbolDatabase::SpaceInfo *info = *i;
// don't check namespaces // only check classes and structures
if (info->type == SymbolDatabase::SpaceInfo::Namespace || info->type == SymbolDatabase::SpaceInfo::Function) if (!info->isClassOrStruct())
continue; continue;
// There are no constructors. // There are no constructors.
@ -98,25 +98,25 @@ void CheckClass::constructors()
} }
} }
std::list<SymbolDatabase::Func>::const_iterator it; std::list<SymbolDatabase::Func>::const_iterator func;
for (it = info->functionList.begin(); it != info->functionList.end(); ++it) for (func = info->functionList.begin(); func != info->functionList.end(); ++func)
{ {
if (!it->hasBody || !(it->type == SymbolDatabase::Func::Constructor || it->type == SymbolDatabase::Func::CopyConstructor || it->type == SymbolDatabase::Func::OperatorEqual)) if (!func->hasBody || !(func->type == SymbolDatabase::Func::Constructor || func->type == SymbolDatabase::Func::CopyConstructor || func->type == SymbolDatabase::Func::OperatorEqual))
continue; continue;
// Mark all variables not used // Mark all variables not used
info->clearAllVar(); info->clearAllVar();
std::list<std::string> callstack; std::list<std::string> callstack;
info->initializeVarList(*it, callstack); info->initializeVarList(*func, callstack);
// Check if any variables are uninitialized // Check if any variables are uninitialized
std::list<SymbolDatabase::Var>::const_iterator var; std::list<SymbolDatabase::Var>::const_iterator var;
for (var = info->varlist.begin(); var != info->varlist.end(); ++var) for (var = info->varlist.begin(); var != info->varlist.end(); ++var)
{ {
// skip classes for regular constructor // skip classes for regular constructor
if (var->isClass && it->type == SymbolDatabase::Func::Constructor) if (var->isClass && func->type == SymbolDatabase::Func::Constructor)
continue; continue;
if (var->assign || var->init || var->isStatic) if (var->assign || var->init || var->isStatic)
@ -126,13 +126,13 @@ void CheckClass::constructors()
continue; continue;
// It's non-static and it's not initialized => error // It's non-static and it's not initialized => error
if (it->type == SymbolDatabase::Func::OperatorEqual) if (func->type == SymbolDatabase::Func::OperatorEqual)
{ {
const Token *operStart = 0; const Token *operStart = 0;
if (it->token->str() == "=") if (func->token->str() == "=")
operStart = it->token->tokAt(1); operStart = func->token->tokAt(1);
else else
operStart = it->token->tokAt(3); operStart = func->token->tokAt(3);
bool classNameUsed = false; bool classNameUsed = false;
for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next()) for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next())
@ -145,10 +145,10 @@ void CheckClass::constructors()
} }
if (classNameUsed) if (classNameUsed)
operatorEqVarError(it->token, info->className, var->token->str()); operatorEqVarError(func->token, info->className, var->token->str());
} }
else if (it->access != SymbolDatabase::Private) else if (func->access != SymbolDatabase::Private)
uninitVarError(it->token, info->className, var->token->str()); uninitVarError(func->token, info->className, var->token->str());
} }
} }
} }
@ -196,8 +196,8 @@ void CheckClass::privateFunctions()
{ {
SymbolDatabase::SpaceInfo *info = *i; SymbolDatabase::SpaceInfo *info = *i;
// don't check namespaces // only check classes and structures
if (info->type == SymbolDatabase::SpaceInfo::Namespace || info->type == SymbolDatabase::SpaceInfo::Function) if (!info->isClassOrStruct())
continue; continue;
// dont check derived classes // dont check derived classes
@ -545,7 +545,8 @@ void CheckClass::operatorEqRetRefThis()
{ {
const SymbolDatabase::SpaceInfo *info = *i; const SymbolDatabase::SpaceInfo *info = *i;
if (info->type == SymbolDatabase::SpaceInfo::Class || info->type == SymbolDatabase::SpaceInfo::Struct) // only check classes and structures
if (info->isClassOrStruct())
{ {
std::list<SymbolDatabase::Func>::const_iterator func; std::list<SymbolDatabase::Func>::const_iterator func;
@ -896,6 +897,10 @@ void CheckClass::checkConst()
{ {
SymbolDatabase::SpaceInfo *info = *it; SymbolDatabase::SpaceInfo *info = *it;
// only check classes and structures
if (!info->isClassOrStruct())
continue;
std::list<SymbolDatabase::Func>::const_iterator func; std::list<SymbolDatabase::Func>::const_iterator func;
for (func = info->functionList.begin(); func != info->functionList.end(); ++func) for (func = info->functionList.begin(); func != info->functionList.end(); ++func)
@ -905,7 +910,7 @@ void CheckClass::checkConst()
{ {
// get last token of return type // get last token of return type
const Token *previous = func->tokenDef->isName() ? func->token->previous() : func->token->tokAt(-2); const Token *previous = func->tokenDef->isName() ? func->token->previous() : func->token->tokAt(-2);
while (previous->str() == "::") while (previous && previous->str() == "::")
previous = previous->tokAt(-2); previous = previous->tokAt(-2);
// does the function return a pointer or reference? // does the function return a pointer or reference?
@ -970,7 +975,7 @@ void CheckClass::checkConst()
{ {
std::string classname = info->className; std::string classname = info->className;
SymbolDatabase::SpaceInfo *nest = info->nestedIn; SymbolDatabase::SpaceInfo *nest = info->nestedIn;
while (nest) while (nest && nest->type != SymbolDatabase::SpaceInfo::Global)
{ {
classname = std::string(nest->className + "::" + classname); classname = std::string(nest->className + "::" + classname);
nest = nest->nestedIn; nest = nest->nestedIn;

View File

@ -39,7 +39,8 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
: _tokenizer(tokenizer), _settings(settings), _errorLogger(errorLogger) : _tokenizer(tokenizer), _settings(settings), _errorLogger(errorLogger)
{ {
// find all namespaces (class,struct and namespace) // find all namespaces (class,struct and namespace)
SpaceInfo *info = 0; SpaceInfo *info = new SpaceInfo(this, NULL, NULL);
spaceInfoList.push_back(info);
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
{ {
// Locate next class // Locate next class
@ -49,7 +50,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
const Token *tok2 = tok->tokAt(2); const Token *tok2 = tok->tokAt(2);
// only create base list for classes and structures // only create base list for classes and structures
if (new_info->type == SpaceInfo::Class || new_info->type == SpaceInfo::Struct) if (new_info->isClassOrStruct())
{ {
// goto initial '{' // goto initial '{'
tok2 = initBaseInfo(new_info, tok); tok2 = initBaseInfo(new_info, tok);
@ -66,8 +67,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
tok = tok2; tok = tok2;
} }
// check if in space else
else if (info)
{ {
// check for end of space // check for end of space
if (tok == info->classEnd) if (tok == info->classEnd)
@ -241,13 +241,66 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
info->friendList.push_back(friendInfo); info->friendList.push_back(friendInfo);
} }
} }
else if (info->type == SpaceInfo::Namespace) else if (info->type == SpaceInfo::Namespace || info->type == SpaceInfo::Global)
addIfFunction(&info, &tok); {
} const Token *funcStart = 0;
const Token *argStart = 0;
// not in SpaceInfo // function?
else if (isFunction(tok, &funcStart, &argStart))
addIfFunction(&info, &tok); {
// has body?
if (Token::Match(argStart->link(), ") const| {|:"))
{
// class function
if (tok->previous() && tok->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;
info->functionList.push_back(function);
addNewFunction(&info, &tok);
}
}
// function returning function pointer with body
else if (Token::simpleMatch(argStart->link(), ") ) (") &&
Token::Match(argStart->link()->tokAt(2)->link(), ") const| {"))
{
const Token *tok1 = funcStart;
// class function
if (tok1->previous()->str() == "::")
addFunction(&info, &tok1, argStart);
// regular function
else
addNewFunction(&info, &tok1);
tok = tok1;
}
}
}
}
} }
std::list<SpaceInfo *>::iterator it; std::list<SpaceInfo *>::iterator it;
@ -440,14 +493,14 @@ bool SymbolDatabase::argsMatch(const Token *first, const Token *second, const st
void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Token *argStart) void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Token *argStart)
{ {
const Token *tok1 = (*tok)->tokAt(-2); const Token *tok1 = (*tok)->tokAt(-2); // skip class/struct name
int count = 0; int count = 0;
bool added = false; bool added = false;
std::string path; std::string path;
unsigned int path_length = 0; unsigned int path_length = 0;
// back up to head of path // back up to head of path
while (tok1->previous() && tok1->previous()->str() == "::") while (tok1 && tok1->previous() && tok1->previous()->str() == "::")
{ {
path = tok1->str() + " :: " + path; path = tok1->str() + " :: " + path;
tok1 = tok1->tokAt(-2); tok1 = tok1->tokAt(-2);
@ -468,35 +521,28 @@ void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Toke
{ {
SpaceInfo *info1 = *it1; SpaceInfo *info1 = *it1;
// is this class at global scope?
if (*info)
{
if (!info1->nestedIn)
continue;
}
else
{
if (info1->nestedIn)
continue;
}
bool match = false; bool match = false;
if (info1->className == tok1->str() && if (info1->className == tok1->str() && (info1->type != SpaceInfo::Function))
((*info) ? (info1->nestedIn->className == (*info)->className) : true))
{ {
SpaceInfo *info2 = info1; // do the spaces match (same space) or do their names match (multiple namespaces)
if ((*info == info1->nestedIn) || (*info && info1 &&
while (info2 && count > 0) (*info)->className == info1->nestedIn->className && !(*info)->className.empty() &&
(*info)->type == info1->nestedIn->type))
{ {
count--; SpaceInfo *info2 = info1;
tok1 = tok1->tokAt(2);
info2 = info2->findInNestedList(tok1->str());
}
if (count == 0 && info2) while (info2 && count > 0)
{ {
match = true; count--;
info1 = info2; tok1 = tok1->tokAt(2);
info2 = info2->findInNestedList(tok1->str());
}
if (count == 0 && info2)
{
match = true;
info1 = info2;
}
} }
} }
@ -585,45 +631,6 @@ void SymbolDatabase::addNewFunction(SymbolDatabase::SpaceInfo **info, const Toke
} }
} }
void SymbolDatabase::addIfFunction(SpaceInfo **info, const Token **tok)
{
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);
// regular function
else
addNewFunction(info, tok);
}
// function returning function pointer with body
else if (Token::simpleMatch(argStart->link(), ") ) (") &&
Token::Match(argStart->link()->tokAt(2)->link(), ") const| {"))
{
const Token *tok1 = funcStart;
// class function
if (tok1->previous()->str() == "::")
addFunction(info, &tok1, argStart);
// regular function
else
addNewFunction(info, &tok1);
*tok = tok1;
}
}
}
const Token *SymbolDatabase::initBaseInfo(SpaceInfo *info, const Token *tok) const Token *SymbolDatabase::initBaseInfo(SpaceInfo *info, const Token *tok)
{ {
// goto initial '{' // goto initial '{'
@ -697,7 +704,12 @@ SymbolDatabase::SpaceInfo::SpaceInfo(SymbolDatabase *check_, const Token *classD
nestedIn(nestedIn_), nestedIn(nestedIn_),
numConstructors(0) numConstructors(0)
{ {
if (classDef->str() == "class") if (!classDef)
{
type = SpaceInfo::Global;
access = Public;
}
else if (classDef->str() == "class")
{ {
type = SpaceInfo::Class; type = SpaceInfo::Class;
className = classDef->next()->str(); className = classDef->next()->str();
@ -732,31 +744,48 @@ SymbolDatabase::SpaceInfo::SpaceInfo(SymbolDatabase *check_, const Token *classD
nestedIn->nestedList.push_back(this); nestedIn->nestedList.push_back(this);
} }
// Get variable list..
void SymbolDatabase::SpaceInfo::getVarList() void SymbolDatabase::SpaceInfo::getVarList()
{ {
// Get variable list.. AccessControl varaccess = type == Class ? Private : Public;
unsigned int indentlevel = 0; const Token *start;
AccessControl varaccess = type == Struct ? Public : Private;
for (const Token *tok = classStart; tok; tok = tok->next()) if (classStart)
start = classStart->next();
else
start = check->_tokenizer->tokens();
for (const Token *tok = start; tok; tok = tok->next())
{ {
if (!tok->next()) if (tok->str() == "}")
break; break;
if (tok->str() == "{") // Is it a function?
++indentlevel; else if (tok->str() == "{")
else if (tok->str() == "}")
{ {
if (indentlevel <= 1) tok = tok->link();
break; continue;
--indentlevel;
} }
if (indentlevel != 1) // Is it a nested class or structure?
continue; 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. // Borland C++: Skip all variables in the __published section.
// These are automaticly initialized. // These are automaticly initialized.
if (tok->str() == "__published:") else if (tok->str() == "__published:")
{ {
for (; tok; tok = tok->next()) for (; tok; tok = tok->next())
{ {
@ -772,165 +801,177 @@ void SymbolDatabase::SpaceInfo::getVarList()
} }
// "private:" "public:" "protected:" etc // "private:" "public:" "protected:" etc
bool b = false; else if (tok->str() == "public:")
if (tok->str() == "public:")
{ {
varaccess = Public; varaccess = Public;
b = true; continue;
} }
else if (tok->str() == "protected:") else if (tok->str() == "protected:")
{ {
varaccess = Protected; varaccess = Protected;
b = true; continue;
} }
else if (tok->str() == "private:") else if (tok->str() == "private:")
{ {
varaccess = Private; varaccess = Private;
b = true; continue;
} }
// Search for start of statement..
if (! Token::Match(tok, "[;{}]") && ! b)
continue;
// This is the start of a statement
const Token *next = tok->next();
const Token *vartok = 0;
// If next token contains a ":".. it is not part of a variable declaration
if (next->str().find(":") != std::string::npos)
continue;
// Is it a forward declaration? // Is it a forward declaration?
if (Token::Match(next, "class|struct|union %var% ;")) else if (Token::Match(tok, "class|struct|union %var% ;"))
{ {
tok = tok->tokAt(2); tok = tok->tokAt(2);
continue; continue;
} }
// It it a nested class or structure?
if (Token::Match(next, "class|struct|union %type% :|{"))
{
tok = tok->tokAt(2);
while (tok->str() != "{")
tok = tok->next();
// skip implementation
tok = tok->link();
continue;
}
// Borland C++: Ignore properties.. // Borland C++: Ignore properties..
if (next->str() == "__property") else if (tok->str() == "__property")
continue; continue;
// Search for start of statement..
else if (!tok->previous() || !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:"))
continue;
// This is the start of a statement
const Token *vartok = 0;
// Is it const..? // Is it const..?
bool isConst = false; bool isConst = false;
if (next->str() == "const") if (tok->str() == "const")
{ {
next = next->next(); tok = tok->next();
isConst = true; isConst = true;
} }
// Is it a static variable? // Is it a static variable?
const bool isStatic(Token::simpleMatch(next, "static")); const bool isStatic(Token::simpleMatch(tok, "static"));
if (isStatic) if (isStatic)
{ {
next = next->next(); tok = tok->next();
} }
// Is it a mutable variable? // Is it a mutable variable?
const bool isMutable(Token::simpleMatch(next, "mutable")); const bool isMutable(Token::simpleMatch(tok, "mutable"));
if (isMutable) if (isMutable)
{ {
next = next->next(); tok = tok->next();
} }
// Is it const..? // Is it const..?
if (next->str() == "const") if (tok->str() == "const")
{ {
next = next->next(); tok = tok->next();
isConst = true; isConst = true;
} }
// Is it a variable declaration?
bool isClass = false; bool isClass = false;
if (Token::Match(next, "%type% %var% ;|:"))
// Is it a variable declaration?
if (Token::Match(tok, "%type% %var% ;|:"))
{ {
if (!next->isStandardType()) if (!tok->isStandardType())
isClass = true; isClass = true;
vartok = next->tokAt(1); vartok = tok->tokAt(1);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(3);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% :: %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(5);
tok = vartok->next();
} }
// Structure? // Structure?
else if (Token::Match(next, "struct|union %type% %var% ;")) else if (Token::Match(tok, "struct|union %type% %var% ;"))
{ {
vartok = next->tokAt(2); vartok = tok->tokAt(2);
tok = vartok->next();
} }
// Pointer? // Pointer?
else if (Token::Match(next, "%type% * %var% ;")) else if (Token::Match(tok, "%type% * %var% ;"))
vartok = next->tokAt(2); {
else if (Token::Match(next, "%type% %type% * %var% ;")) vartok = tok->tokAt(2);
vartok = next->tokAt(3); tok = vartok->next();
else if (Token::Match(next, "%type% :: %type% * %var% ;")) }
vartok = next->tokAt(4); else if (Token::Match(tok, "%type% %type% * %var% ;"))
else if (Token::Match(next, "%type% :: %type% :: %type% * %var% ;")) {
vartok = next->tokAt(6); vartok = tok->tokAt(3);
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();
}
// Array? // Array?
else if (Token::Match(next, "%type% %var% [") && next->next()->str() != "operator") else if (Token::Match(tok, "%type% %var% [") && tok->next()->str() != "operator")
{ {
if (!next->isStandardType()) if (!tok->isStandardType())
isClass = true; isClass = true;
vartok = next->tokAt(1); vartok = tok->tokAt(1);
tok = vartok->next()->link()->next();
} }
// Pointer array? // Pointer array?
else if (Token::Match(next, "%type% * %var% [")) else if (Token::Match(tok, "%type% * %var% ["))
vartok = next->tokAt(2);
else if (Token::Match(next, "%type% :: %type% * %var% ["))
vartok = next->tokAt(4);
else if (Token::Match(next, "%type% :: %type% :: %type% * %var% ["))
vartok = next->tokAt(6);
// std::string..
else if (Token::Match(next, "%type% :: %type% %var% ;"))
{ {
isClass = true; vartok = tok->tokAt(2);
vartok = next->tokAt(3); tok = vartok->next();
} }
else if (Token::Match(next, "%type% :: %type% :: %type% %var% ;")) else if (Token::Match(tok, "%type% :: %type% * %var% ["))
{ {
isClass = true; vartok = tok->tokAt(4);
vartok = next->tokAt(5); tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% :: %type% * %var% ["))
{
vartok = tok->tokAt(6);
tok = vartok->next();
} }
// Container.. // Container..
else if (Token::Match(next, "%type% :: %type% <") || else if (Token::Match(tok, "%type% :: %type% <") ||
Token::Match(next, "%type% <")) Token::Match(tok, "%type% <"))
{ {
// find matching ">" // find matching ">"
int level = 0; int level = 0;
for (; next; next = next->next()) for (; tok; tok = tok->next())
{ {
if (next->str() == "<") if (tok->str() == "<")
level++; level++;
else if (next->str() == ">") else if (tok->str() == ">")
{ {
level--; level--;
if (level == 0) if (level == 0)
break; break;
} }
} }
if (next && Token::Match(next, "> %var% ;")) if (tok && Token::Match(tok, "> %var% ;"))
{ {
isClass = true; isClass = true;
vartok = next->tokAt(1); vartok = tok->tokAt(1);
tok = vartok->next();
}
else if (tok && Token::Match(tok, "> * %var% ;"))
{
vartok = tok->tokAt(2);
tok = vartok->next();
} }
else if (next && Token::Match(next, "> * %var% ;"))
vartok = next->tokAt(2);
} }
// If the vartok was set in the if-blocks above, create a entry for this variable.. // If the vartok was set in the if-blocks above, create a entry for this variable..

View File

@ -147,7 +147,7 @@ public:
class SpaceInfo class SpaceInfo
{ {
public: public:
enum SpaceType { Class, Struct, Union, Namespace, Function }; enum SpaceType { Global, Class, Struct, Union, Namespace, Function };
SpaceInfo(SymbolDatabase *check_, const Token *classDef_, SpaceInfo *nestedIn_); SpaceInfo(SymbolDatabase *check_, const Token *classDef_, SpaceInfo *nestedIn_);
@ -166,6 +166,11 @@ public:
AccessControl access; AccessControl access;
unsigned int numConstructors; unsigned int numConstructors;
bool isClassOrStruct() const
{
return (type == Class || type == Struct);
}
/** /**
* @brief find if name is in nested list * @brief find if name is in nested list
* @param name name of nested space * @param name name of nested space
@ -237,7 +242,6 @@ public:
private: private:
void addFunction(SpaceInfo **info, const Token **tok, const Token *argStart); void addFunction(SpaceInfo **info, const Token **tok, const Token *argStart);
void addNewFunction(SpaceInfo **info, const Token **tok); void addNewFunction(SpaceInfo **info, const Token **tok);
void addIfFunction(SpaceInfo **info, const Token **tok);
bool isFunction(const Token *tok, const Token **funcStart, const Token **argStart) const; bool isFunction(const Token *tok, const Token **funcStart, const Token **argStart) const;
bool argsMatch(const Token *first, const Token *second, const std::string &path, unsigned int depth) const; bool argsMatch(const Token *first, const Token *second, const std::string &path, unsigned int depth) const;