reuse symbol database in checkmemoryleak.cpp. ticket: #2219

This commit is contained in:
Robert Reif 2010-11-23 18:41:07 +01:00 committed by Daniel Marjamäki
parent f1eef49fcb
commit 1842a122da
9 changed files with 119 additions and 384 deletions

View File

@ -43,14 +43,15 @@ CheckClass instance;
CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(tokenizer, settings, errorLogger),
symbolDatabase(NULL)
symbolDatabase(NULL), ownSymbolDatabase(false)
{
}
CheckClass::~CheckClass()
{
delete symbolDatabase;
if (ownSymbolDatabase)
delete symbolDatabase;
}
void CheckClass::createSymbolDatabase()
@ -59,7 +60,13 @@ void CheckClass::createSymbolDatabase()
if (symbolDatabase)
return;
symbolDatabase = new SymbolDatabase(_tokenizer, _settings, _errorLogger);
if (_tokenizer->_symbolDatabase)
symbolDatabase = _tokenizer->_symbolDatabase;
else
{
symbolDatabase = new SymbolDatabase(_tokenizer, _settings, _errorLogger);
ownSymbolDatabase = true;
}
}
//---------------------------------------------------------------------------

View File

@ -36,7 +36,7 @@ class CheckClass : public Check
{
public:
/** @brief This constructor is used when registering the CheckClass */
CheckClass() : Check(), symbolDatabase(NULL)
CheckClass() : Check(), symbolDatabase(NULL), ownSymbolDatabase(false)
{ }
/** @brief This constructor is used when running checks. */
@ -113,6 +113,7 @@ private:
void createSymbolDatabase();
SymbolDatabase *symbolDatabase;
bool ownSymbolDatabase;
// Reporting errors..
void noConstructorError(const Token *tok, const std::string &classname, bool isStruct);

View File

@ -2573,81 +2573,49 @@ void CheckMemoryLeakInFunction::check()
void CheckMemoryLeakInClass::check()
{
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
{
if (tok->str() == "{")
tok = tok->link();
SymbolDatabase * symbolDatabase = _tokenizer->_symbolDatabase;
bool ownSymbolDatabase = false;
else if (Token::Match(tok, "class %var% [{:]"))
if (symbolDatabase == NULL)
{
symbolDatabase = new SymbolDatabase(_tokenizer, _settings, _errorLogger);
ownSymbolDatabase = true;
}
std::list<SymbolDatabase::SpaceInfo *>::iterator i;
for (i = symbolDatabase->spaceInfoList.begin(); i != symbolDatabase->spaceInfoList.end(); ++i)
{
const SymbolDatabase::SpaceInfo *info = *i;
// only check classes and structures
if (info->type == SymbolDatabase::SpaceInfo::Class)
{
std::vector<std::string> classname;
classname.push_back(tok->strAt(1));
parseClass(tok, classname);
std::list<SymbolDatabase::Var>::const_iterator var;
for (var = info->varlist.begin(); var != info->varlist.end(); ++var)
{
if (!var->isStatic && var->token->previous()->str() == "*")
{
// allocation but no deallocation of private variables in public function..
if (var->access == SymbolDatabase::Private && var->token->tokAt(-2)->isStandardType())
checkPublicFunctions(var->token, var->token->varId());
if (var->token->tokAt(-2)->isStandardType())
variable(info, var->token);
}
}
}
}
if (ownSymbolDatabase)
delete symbolDatabase;
}
void CheckMemoryLeakInClass::parseClass(const Token *tok1, std::vector<std::string> &classname)
{
// Go into class.
while (tok1 && tok1->str() != "{")
tok1 = tok1->next();
tok1 = tok1 ? tok1->next() : 0;
// are we parsing the private scope of the class?
bool privateScope = true;
unsigned int indentlevel = 0;
for (const Token *tok = tok1; tok; tok = tok->next())
{
if (tok->str() == "{")
++indentlevel;
else if (tok->str() == "}")
{
if (indentlevel == 0)
return;
--indentlevel;
}
else if (tok->isName() && tok->str().find(":") != std::string::npos)
privateScope = bool(tok->str() == "private:");
// Only parse this particular class.. not subclasses
if (indentlevel > 0)
continue;
// skip static variables..
if (Token::Match(tok, "static %type% * %var% ;"))
{
tok = tok->tokAt(4);
}
// Declaring subclass.. recursive checking
if (Token::Match(tok, "class %var% [{:]"))
{
classname.push_back(tok->strAt(1));
parseClass(tok, classname);
classname.pop_back();
}
// Declaring member variable.. check allocations and deallocations
if (Token::Match(tok->previous(), ";|{|}|private:|protected:|public: %type% * %var% ;"))
{
// allocation but no deallocation of private variables in public function..
if (privateScope && tok->isStandardType())
checkPublicFunctions(tok1, tok->tokAt(2)->varId());
if (!isclass(_tokenizer, tok))
variable(classname.back(), tok->tokAt(2));
}
}
}
void CheckMemoryLeakInClass::variable(const std::string &classname, const Token *tokVarname)
void CheckMemoryLeakInClass::variable(const SymbolDatabase::SpaceInfo *classinfo, const Token *tokVarname)
{
const std::string varname = tokVarname->strAt(0);
const std::string classname = classinfo->className;
// Check if member variable has been allocated and deallocated..
CheckMemoryLeak::AllocType Alloc = CheckMemoryLeak::No;
@ -2656,14 +2624,13 @@ void CheckMemoryLeakInClass::variable(const std::string &classname, const Token
bool allocInConstructor = false;
bool deallocInDestructor = false;
// Loop through all tokens. Inspect member functions
int indent_ = 0;
const Token *functionToken = _tokenizer->findClassFunction(_tokenizer->tokens(), classname.c_str(), "~| %var%", indent_);
while (functionToken)
// Inspect member functions
std::list<SymbolDatabase::Func>::const_iterator func;
for (func = classinfo->functionList.begin(); func != classinfo->functionList.end(); ++func)
{
const bool constructor(Token::Match(functionToken, (classname + " (").c_str()) || Token::Match(functionToken, (classname + " :: " + classname + " (").c_str()));
const bool destructor(functionToken->str() == "~" || functionToken->tokAt(2)->str() == "~");
const Token *functionToken = func->token;
const bool constructor = func->type == SymbolDatabase::Func::Constructor;
const bool destructor = func->type == SymbolDatabase::Func::Destructor;
unsigned int indent = 0;
bool initlist = false;
for (const Token *tok = functionToken; tok; tok = tok->next())
@ -2765,8 +2732,6 @@ void CheckMemoryLeakInClass::variable(const std::string &classname, const Token
}
}
}
functionToken = _tokenizer->Tokenizer::findClassFunction(functionToken->next(), classname.c_str(), "~| %var%", indent_);
}
if (allocInConstructor && !deallocInDestructor)

View File

@ -35,6 +35,7 @@
*/
#include "check.h"
#include "symboldatabase.h"
#include <list>
#include <string>
@ -376,8 +377,7 @@ public:
void check();
private:
void parseClass(const Token *tok1, std::vector<std::string> &classname);
void variable(const std::string &classname, const Token *tokVarname);
void variable(const SymbolDatabase::SpaceInfo *spaceinfo, const Token *tokVarname);
/** Public functions: possible double-allocation */
void checkPublicFunctions(const Token *classtok, const unsigned int varid);

View File

@ -256,6 +256,11 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
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
{
@ -493,11 +498,17 @@ bool SymbolDatabase::argsMatch(const Token *first, const Token *second, const st
void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Token *argStart)
{
const Token *tok1 = (*tok)->tokAt(-2); // skip class/struct name
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() == "::")
@ -565,6 +576,17 @@ void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Toke
func->arg = argStart;
}
}
else if (func->type == SymbolDatabase::Func::Destructor &&
(*tok)->previous()->str() == "~" &&
func->tokenDef->str() == (*tok)->str())
{
if (argsMatch(func->tokenDef->next(), (*tok)->next(), path, path_length))
{
func->hasBody = true;
func->token = *tok;
func->arg = argStart;
}
}
else if (func->tokenDef->str() == (*tok)->str())
{
if (argsMatch(func->tokenDef->next(), (*tok)->next(), path, path_length))
@ -576,7 +598,7 @@ void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Toke
(!func->isConst && (*tok)->next()->link()->next()->str() != "const"))
{
func->hasBody = true;
func->token = (*tok);
func->token = *tok;
func->arg = argStart;
}
}
@ -586,7 +608,7 @@ void SymbolDatabase::addFunction(SpaceInfo **info, const Token **tok, const Toke
{
// todo check for const
func->hasBody = true;
func->token = (*tok);
func->token = *tok;
func->arg = argStart;
}
}

View File

@ -30,6 +30,7 @@
#include "errorlogger.h"
#include "check.h"
#include "path.h"
#include "symboldatabase.h"
#include <locale>
#include <fstream>
@ -52,6 +53,7 @@ Tokenizer::Tokenizer()
_tokens = 0;
_tokensBack = 0;
_codeWithTemplates = false;
_symbolDatabase = NULL;
}
Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger)
@ -60,11 +62,13 @@ Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger)
_tokens = 0;
_tokensBack = 0;
_codeWithTemplates = false;
_symbolDatabase = NULL;
}
Tokenizer::~Tokenizer()
{
deallocateTokens();
delete _symbolDatabase;
}
//---------------------------------------------------------------------------
@ -3737,7 +3741,8 @@ void Tokenizer::simplifySizeof()
bool Tokenizer::simplifyTokenList()
{
// clear the _functionList so it can't contain dead pointers
_functionList.clear();
delete _symbolDatabase;
_symbolDatabase = NULL;
for (Token *tok = _tokens; tok; tok = tok->next())
{
@ -7461,11 +7466,19 @@ void Tokenizer::simplifyStd()
const Token *Tokenizer::getFunctionTokenByName(const char funcname[]) const
{
for (unsigned int i = 0; i < _functionList.size(); ++i)
if (_symbolDatabase == NULL)
return NULL;
std::list<SymbolDatabase::SpaceInfo *>::iterator i;
for (i = _symbolDatabase->spaceInfoList.begin(); i != _symbolDatabase->spaceInfoList.end(); ++i)
{
if (_functionList[i]->str() == funcname)
SymbolDatabase::SpaceInfo *info = *i;
if (info->type == SymbolDatabase::SpaceInfo::Function)
{
return _functionList[i];
if (info->classDef->str() == funcname)
return info->classDef;
}
}
return NULL;
@ -7474,80 +7487,7 @@ const Token *Tokenizer::getFunctionTokenByName(const char funcname[]) const
void Tokenizer::fillFunctionList()
{
_functionList.clear();
for (const Token *tok = _tokens; tok; tok = tok->next())
{
if (tok->str() == "{")
{
tok = tok->link();
if (!tok)
break;
continue;
}
if (Token::Match(tok, "%var% ("))
{
// Check if this is the first token of a function implementation..
for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next())
{
if (tok2->str() == ";")
{
tok = tok2;
break;
}
else if (tok2->str() == "{")
{
break;
}
else if (tok2->str() == ")")
{
if (Token::Match(tok2, ") const| {"))
{
_functionList.push_back(tok);
tok = tok2;
}
else
{
tok = tok2;
while (tok->next() && !Token::Match(tok->next(), "[;{]"))
tok = tok->next();
}
break;
}
}
}
}
// If the _functionList functions with duplicate names, remove them
/** @todo handle when functions with the same name */
for (unsigned int func1 = 0; func1 < _functionList.size();)
{
bool hasDuplicates = false;
for (unsigned int func2 = func1 + 1; func2 < _functionList.size();)
{
if (_functionList[func1]->str() == _functionList[func2]->str())
{
hasDuplicates = true;
_functionList.erase(_functionList.begin() + static_cast<int>(func2));
}
else
{
++func2;
}
}
if (! hasDuplicates)
{
++func1;
}
else
{
_functionList.erase(_functionList.begin() + static_cast<int>(func1));
}
}
_symbolDatabase = new SymbolDatabase(this, _settings, _errorLogger);
}
//---------------------------------------------------------------------------
@ -7604,106 +7544,6 @@ std::string Tokenizer::file(const Token *tok) const
//---------------------------------------------------------------------------
const Token * Tokenizer::findClassFunction(const Token *tok, const std::string &classname, const std::string &funcname, int &indentlevel, bool isStruct) const
{
if (indentlevel < 0 || tok == NULL)
return NULL;
/*
// TODO: This is currently commented out as updateClassList doesn't
// fully work yet and call to updateClassList is currently also
// commented out.
//std::cout << tok->str()<<"--\n";
if( tok == _tokens || tok->str() == "class" || tok->str() == "struct")
tok = 0;
std::map<std::string, ClassInfo>::const_iterator iter;
iter = _classInfoList.find(classname);
if( iter == _classInfoList.end() )
return NULL;
for( size_t i = 0; i < iter->second._memberFunctions.size(); i++ )
if( Token::Match( iter->second._memberFunctions[i]._declaration, funcname ) )
{
if( tok != 0 )
{
if( tok == iter->second._memberFunctions[i]._implementation ||
tok->previous() == iter->second._memberFunctions[i]._implementation)
tok = 0;
continue;
}
return iter->second._memberFunctions[i]._implementation;
}
return NULL;
*/
const std::string classPattern(std::string(isStruct ? "struct " : "class ") + classname + " :|{");
const std::string internalPattern(std::string("!!~ ") + funcname + " (");
const std::string externalPattern(classname + " :: " + funcname + " (");
for (; tok; tok = tok->next())
{
if (indentlevel == 0 && Token::Match(tok, classPattern.c_str()))
{
while (tok && tok->str() != "{")
tok = tok->next();
if (tok)
tok = tok->next();
if (! tok)
break;
indentlevel = 1;
}
if (tok->str() == "{")
{
// If indentlevel==0 don't go to indentlevel 1. Skip the block.
if (indentlevel > 0)
++indentlevel;
else
{
// skip the block
tok = tok->link();
if (tok == NULL)
return NULL;
continue;
}
}
if (tok->str() == "}")
{
--indentlevel;
if (indentlevel < 0)
return NULL;
}
if (indentlevel == 1)
{
// Member function implemented in the class declaration?
if (Token::Match(tok->previous(), internalPattern.c_str()))
{
const Token *tok2 = tok;
while (tok2 && tok2->str() != "{" && tok2->str() != ";")
tok2 = tok2->next();
if (tok2 && tok2->str() == "{")
return tok;
}
}
else if (indentlevel == 0 && Token::Match(tok, externalPattern.c_str()))
{
if (!Token::simpleMatch(tok->previous(), "::"))
return tok;
}
}
// Not found
return NULL;
}
//---------------------------------------------------------------------------
void Tokenizer::syntaxError(const Token *tok)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;

View File

@ -29,6 +29,7 @@
class Token;
class ErrorLogger;
class Settings;
class SymbolDatabase;
/// @addtogroup Core
/// @{
@ -124,17 +125,6 @@ public:
std::string file(const Token *tok) const;
/**
* Find a class or struct member function
* @param tok where to begin the search
* @param classname name of class
* @param funcname name of function ("~ Fred" => destructor for fred, "%var%" => any function)
* @param indentlevel Just an integer that you initialize to 0 before the first call.
* @param isStruct is it a struct
* @return First matching token or NULL.
*/
const Token *findClassFunction(const Token *tok, const std::string &classname, const std::string &funcname, int &indentlevel, bool isStruct = false) const;
/**
* get error messages
*/
@ -285,7 +275,7 @@ public:
/** Simplify "if else" */
void elseif();
std::vector<const Token *> _functionList;
SymbolDatabase * _symbolDatabase;
void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno, bool split = false);
void addtoken(const Token *tok, const unsigned int lineno, const unsigned int fileno);

View File

@ -3139,6 +3139,7 @@ private:
TEST_CASE(class16);
TEST_CASE(class17);
TEST_CASE(class18);
TEST_CASE(class19); // ticket #2219
TEST_CASE(staticvar);
@ -3528,6 +3529,25 @@ private:
ASSERT_EQUALS("", errout.str());
}
void class19()
{
// Ticket #2219
check("class Foo\n"
"{\n"
"private:\n"
" TRadioButton* rp1;\n"
" TRadioButton* rp2;\n"
"public:\n"
" Foo();\n"
"};\n"
"Foo::Foo()\n"
"{\n"
" rp1 = new TRadioButton(this);\n"
" rp2 = new TRadioButton(this);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void staticvar()
{
check("class A\n"

View File

@ -67,10 +67,6 @@ private:
TEST_CASE(inlineasm);
TEST_CASE(dupfuncname);
TEST_CASE(const_and_volatile_functions);
TEST_CASE(ifAddBraces1);
TEST_CASE(ifAddBraces2);
TEST_CASE(ifAddBraces3);
@ -204,9 +200,6 @@ private:
TEST_CASE(simplify_constants);
TEST_CASE(simplify_constants2);
TEST_CASE(findClassFunction1);
TEST_CASE(findClassFunction2);
TEST_CASE(vardecl1);
TEST_CASE(vardecl2);
TEST_CASE(vardecl3);
@ -587,61 +580,6 @@ private:
}
void dupfuncname()
{
const char code[] = "void a()\n"
"{ }\n"
"void a(int i)\n"
"{ }\n"
"void b()\n"
"{ }\n";
// tokenize..
Tokenizer tokenizer;
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
tokenizer.fillFunctionList();
ASSERT_EQUALS(1, static_cast<unsigned int>(tokenizer._functionList.size()));
ASSERT_EQUALS("b", tokenizer._functionList[0]->str());
}
void const_and_volatile_functions()
{
const char code[] = "class B\n\
{\n\
public:\n\
void a();\n\
void b() const;\n\
void c() volatile;\n\
};\n\
\n\
void B::a()\n\
{}\n\
\n\
void B::b() const\n\
{}\n\
\n\
void B::c() volatile\n\
{}\n";
// tokenize..
Tokenizer tokenizer;
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
tokenizer.fillFunctionList();
ASSERT_EQUALS(3, static_cast<unsigned int>(tokenizer._functionList.size()));
if (tokenizer._functionList.size() == 3)
{
ASSERT_EQUALS("a", tokenizer._functionList[0]->str());
ASSERT_EQUALS("b", tokenizer._functionList[1]->str());
ASSERT_EQUALS("c", tokenizer._functionList[2]->str());
}
}
void pointers_condition()
{
ASSERT_EQUALS("( p )", tokenizeAndStringify("( p != NULL )", true));
@ -3531,54 +3469,6 @@ private:
ASSERT_EQUALS(oss.str(), ostr.str());
}
void findClassFunction1()
{
const char code[] =
"class Fred"
"{\n"
"public:\n"
" Fred()\n"
" { }\n"
"};\n";
// tokenize..
Tokenizer tokenizer;
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
int i;
i = 0;
const Token *tok = tokenizer.findClassFunction(tokenizer.tokens(), "Fred", "%var%", i);
ASSERT_EQUALS(true, Token::simpleMatch(tok, "Fred ( ) {"));
tok = tokenizer.findClassFunction(tok->next(), "Fred", "%var%", i);
ASSERT_EQUALS(0, tok ? 1 : 0);
}
void findClassFunction2()
{
const char code[] =
"struct Fred"
"{\n"
" Fred()\n"
" { }\n"
"};\n";
// tokenize..
Tokenizer tokenizer;
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
int i;
i = 0;
const Token *tok = tokenizer.findClassFunction(tokenizer.tokens(), "Fred", "%var%", i, true);
ASSERT_EQUALS(true, Token::simpleMatch(tok, "Fred ( ) {"));
tok = tokenizer.findClassFunction(tok->next(), "Fred", "%var%", i, false);
ASSERT_EQUALS(0, tok ? 1 : 0);
}
void vardecl1()
{
const char code[] = "unsigned int a, b;";