Refactoring: Split up the CheckMemoryLeak into CheckMemoryLeakInFunction and CheckMemoryLeakInClass

This commit is contained in:
Daniel Marjamäki 2009-06-08 20:20:43 +02:00
parent 15dbf9c085
commit 2c07c22d9e
3 changed files with 626 additions and 561 deletions

View File

@ -31,12 +31,13 @@
// Register this check class (by creating a static instance of it)
namespace
{
CheckMemoryLeak instance;
CheckMemoryLeakInFunction instance1;
CheckMemoryLeakInClass instance2;
}
//---------------------------------------------------------------------------
bool CheckMemoryLeak::isclass(const Token *tok)
bool CheckMemoryLeak::isclass(const Tokenizer *_tokenizer, const Token *tok) const
{
if (tok->isStandardType())
return false;
@ -122,15 +123,6 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::GetAllocationType(const Token *tok2)
if (Token::Match(tok2, "opendir|fdopendir ("))
return Dir;
// Userdefined allocation function..
std::list<AllocFunc>::const_iterator it = _listAllocFunc.begin();
while (it != _listAllocFunc.end())
{
if (tok2->str() == it->funcname)
return it->alloctype;
++it;
}
return No;
}
@ -206,7 +198,90 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::GetDeallocationType(const Token *tok
}
//--------------------------------------------------------------------------
const char * CheckMemoryLeak::call_func(const Token *tok, std::list<const Token *> callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype, bool &all, unsigned int sz)
//--------------------------------------------------------------------------
void CheckMemoryLeak::MemoryLeak(const Token *tok, const char varname[], AllocType alloctype, bool all)
{
if (alloctype == CheckMemoryLeak::File ||
alloctype == CheckMemoryLeak::Pipe ||
alloctype == CheckMemoryLeak::Dir)
resourceLeakError(tok, varname);
else if (all)
memleakallError(tok, varname);
else
memleakError(tok, varname);
}
//---------------------------------------------------------------------------
void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname)
{
error(tok, "error", "memleak", "Memory leak: " + varname);
}
void CheckMemoryLeak::memleakallError(const Token *tok, const std::string &varname)
{
error(tok, "all", "memleakall", "Memory leak: " + varname);
}
void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname)
{
error(tok, "error", "resourceLeak", "Resource leak: " + varname);
}
void CheckMemoryLeak::deallocDeallocError(const Token *tok, const std::string &varname)
{
error(tok, "error", "deallocDealloc", "Deallocating a deallocated pointer: " + varname);
}
void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname)
{
error(tok, "error", "deallocuse", "Using '" + varname + "' after it is deallocated / released");
}
void CheckMemoryLeak::mismatchSizeError(const Token *tok, const std::string &sz)
{
error(tok, "error", "mismatchSize", "The given size " + sz + " is mismatching");
}
void CheckMemoryLeak::mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname)
{
error(callstack, "error", "mismatchAllocDealloc", "Mismatching allocation and deallocation: " + varname);
}
bool CheckMemoryLeakInFunction::MatchFunctionsThatReturnArg(const Token *tok, const std::string &varname)
{
return Token::Match(tok, std::string("; " + varname + " = strcat|memcpy|memmove|strcpy ( " + varname + " ,").c_str());
}
bool CheckMemoryLeakInFunction::notvar(const Token *tok, const char *varnames[], bool endpar)
{
std::string varname;
for (int i = 0; varnames[i]; i++)
{
if (i > 0)
varname += " . ";
varname += varnames[i];
}
const std::string end(endpar ? " &&|)" : " [;)&|]");
return bool(Token::Match(tok, ("! " + varname + end).c_str()) ||
Token::Match(tok, ("! ( " + varname + " )" + end).c_str()));
}
const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<const Token *> callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype, bool &all, unsigned int sz)
{
// Keywords that are not function calls..
if (Token::Match(tok, "if|for|while|return|switch"))
@ -315,44 +390,9 @@ const char * CheckMemoryLeak::call_func(const Token *tok, std::list<const Token
return NULL;
}
//--------------------------------------------------------------------------
void CheckMemoryLeak::MemoryLeak(const Token *tok, const char varname[], AllocType alloctype, bool all)
{
if (alloctype == CheckMemoryLeak::File ||
alloctype == CheckMemoryLeak::Pipe ||
alloctype == CheckMemoryLeak::Dir)
resourceLeakError(tok, varname);
else if (all)
memleakallError(tok, varname);
else
memleakError(tok, varname);
}
//---------------------------------------------------------------------------
bool CheckMemoryLeak::MatchFunctionsThatReturnArg(const Token *tok, const std::string &varname)
{
return Token::Match(tok, std::string("; " + varname + " = strcat|memcpy|memmove|strcpy ( " + varname + " ,").c_str());
}
bool CheckMemoryLeak::notvar(const Token *tok, const char *varnames[], bool endpar)
{
std::string varname;
for (int i = 0; varnames[i]; i++)
{
if (i > 0)
varname += " . ";
varname += varnames[i];
}
const std::string end(endpar ? " &&|)" : " [;)&|]");
return bool(Token::Match(tok, ("! " + varname + end).c_str()) ||
Token::Match(tok, ("! ( " + varname + " )" + end).c_str()));
}
Token *CheckMemoryLeak::getcode(const Token *tok, std::list<const Token *> callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype, bool classmember, bool &all, unsigned int sz)
Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Token *> callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype, bool classmember, bool &all, unsigned int sz)
{
const char *varnames[2];
varnames[0] = varname;
@ -437,7 +477,7 @@ Token *CheckMemoryLeak::getcode(const Token *tok, std::list<const Token *> calls
{
if (Token::Match(tok->tokAt(2), "new %type% [(;]"))
{
if (isclass(tok->tokAt(3)))
if (isclass(_tokenizer, tok->tokAt(3)))
{
if (_settings->_showAll)
{
@ -775,12 +815,12 @@ Token *CheckMemoryLeak::getcode(const Token *tok, std::list<const Token *> calls
return rethead;
}
void CheckMemoryLeak::erase(Token *begin, const Token *end)
{
Token::eraseTokens(begin, end);
}
void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
void CheckMemoryLeakInFunction::simplifycode(Token *tok, bool &all)
{
// Replace "throw" that is not in a try block with "return"
int indentlevel = 0;
@ -842,7 +882,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Delete extra ";"
while (Token::Match(tok2, "[;{}] ;"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
@ -850,21 +890,21 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
if (Token::simpleMatch(tok2->next(), "{ }"))
{
tok2->next()->str(";");
erase(tok2->next(), tok2->tokAt(3));
Token::eraseTokens(tok2->next(), tok2->tokAt(3));
done = false;
}
// Delete braces around a single instruction..
if (Token::Match(tok2->next(), "{ %var% ; }"))
{
erase(tok2, tok2->tokAt(2));
erase(tok2->next()->next(), tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2->next()->next(), tok2->tokAt(4));
done = false;
}
if (Token::Match(tok2->next(), "{ %var% %var% ; }"))
{
erase(tok2, tok2->tokAt(2));
erase(tok2->next()->next()->next(), tok2->tokAt(5));
Token::eraseTokens(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2->next()->next()->next(), tok2->tokAt(5));
done = false;
}
@ -874,21 +914,21 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Delete empty if that is not followed by an else
if (Token::Match(tok2->next(), "if ; !!else"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Delete "if ; else ;"
else if (Token::simpleMatch(tok2->next(), "if ; else ;"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
// Two "if alloc ;" after one another.. perhaps only one of them can be executed each time
else if (!_settings->_showAll && Token::Match(tok2, "[;{}] if alloc ; if alloc ;"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
@ -896,7 +936,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
else if (Token::Match(tok2, "; if ; else assign|use ; assign|use") ||
Token::Match(tok2, "; if assign|use ; else ; assign|use"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
@ -908,12 +948,12 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
{
if (_settings->_showAll)
{
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
all = true;
}
else
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
}
done = false;
}
@ -921,57 +961,57 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Reduce "if if" => "if"
else if (Token::simpleMatch(tok2, "if if"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Reduce "if return ; alloc ;" => "alloc ;"
else if (Token::Match(tok2, "[;{}] if return ; alloc ;"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
// "[;{}] if alloc ; else return ;" => "[;{}] alloc ;"
else if (Token::Match(tok2, "[;{}] if alloc ; else return ;"))
{
erase(tok2, tok2->tokAt(2)); // Remove "if"
erase(tok2->next(), tok2->tokAt(5)); // Remove "; else return"
Token::eraseTokens(tok2, tok2->tokAt(2)); // Remove "if"
Token::eraseTokens(tok2->next(), tok2->tokAt(5)); // Remove "; else return"
done = false;
}
// Reduce "if ; else %var% ;" => "if %var% ;"
else if (Token::Match(tok2->next(), "if ; else %var% ;"))
{
erase(tok2->next(), tok2->tokAt(4));
Token::eraseTokens(tok2->next(), tok2->tokAt(4));
done = false;
}
// Reduce "if ; else return use ;" => "if return use ;"
else if (Token::simpleMatch(tok2->next(), "if ; else return use ;"))
{
erase(tok2->next(), tok2->tokAt(4));
Token::eraseTokens(tok2->next(), tok2->tokAt(4));
done = false;
}
// Reduce "if return ; if return ;" => "if return ;"
else if (Token::simpleMatch(tok2->next(), "if return ; if return ;"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
// Delete first if in .. "if { dealloc|assign|use ; return ; } if return ;"
else if (Token::Match(tok2, "[;{}] if { dealloc|assign|use ; return ; } if return ;"))
{
erase(tok2, tok2->tokAt(8));
Token::eraseTokens(tok2, tok2->tokAt(8));
done = false;
}
// Remove "if { dealloc ; callfunc ; } !!else"
else if (Token::Match(tok2->next(), "if { dealloc|assign|use ; callfunc ; } !!else"))
{
erase(tok2, tok2->tokAt(8));
Token::eraseTokens(tok2, tok2->tokAt(8));
done = false;
}
@ -981,7 +1021,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
if (Token::Match(tok2, "[;{}] if { assign|dealloc|use ; return ; } !!else"))
{
all = true;
erase(tok2, tok2->tokAt(8));
Token::eraseTokens(tok2, tok2->tokAt(8));
done = false;
}
}
@ -992,7 +1032,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Reduce "if(var) dealloc ;" and "if(var) use ;" that is not followed by an else..
if (Token::Match(tok2, "[;{}] if(var) assign|dealloc|use ; !!else"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
@ -1000,7 +1040,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
if (Token::Match(tok2, "; if(!var) alloc ; !!else"))
{
// Remove the "if(!var)"
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
// Insert "dealloc ;" before the "alloc ;"
tok2->insertToken(";");
@ -1012,28 +1052,28 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Remove "catch ;"
if (Token::simpleMatch(tok2->next(), "catch ;"))
{
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
done = false;
}
// Reduce "if* ;" that is not followed by an else..
if (Token::Match(tok2->next(), "if(var)|if(!var)|ifv ; !!else"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Reduce "else ;" => ";"
if (Token::simpleMatch(tok2->next(), "else ;"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Delete if block: "alloc; if return use ;"
if (Token::Match(tok2, "alloc ; if return use ; !!else"))
{
erase(tok2, tok2->tokAt(5));
Token::eraseTokens(tok2, tok2->tokAt(5));
done = false;
}
@ -1041,7 +1081,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Replace "dealloc use ;" with "dealloc ;"
if (Token::simpleMatch(tok2, "dealloc use ;"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
@ -1049,7 +1089,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
if (! _settings->_showAll && Token::Match(tok2, "dealloc ; alloc ; if break|continue ;"))
{
tok2 = tok2->next()->next()->next();
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
done = false;
}
@ -1057,15 +1097,15 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// TODO: If the loop can be executed twice reduce to "loop alloc ;" instead
if (Token::simpleMatch(tok2->next(), "do { alloc ; }"))
{
erase(tok2, tok2->tokAt(3));
erase(tok2->next()->next(), tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2->next()->next(), tok2->tokAt(4));
done = false;
}
// Reduce "loop if break ; => ";"
if (Token::Match(tok2->next(), "loop if break|continue ; !!else"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
@ -1073,88 +1113,88 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
if (Token::Match(tok2->next(), "loop { assign|dealloc|use ; alloc ; if break|continue ; }"))
{
// erase "loop {"
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
// erase "if break|continue ; }"
tok2 = tok2->next()->next()->next()->next();
erase(tok2, tok2->tokAt(5));
Token::eraseTokens(tok2, tok2->tokAt(5));
done = false;
}
// Replace "loop { X ; break ; }" with "X ;"
if (Token::Match(tok2->next(), "loop { %var% ; break ; }"))
{
erase(tok2, tok2->tokAt(3));
erase(tok2->next()->next(), tok2->tokAt(6));
Token::eraseTokens(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2->next()->next(), tok2->tokAt(6));
done = false;
}
// Replace "loop ;" with ";"
if (Token::simpleMatch(tok2->next(), "loop ;"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Replace "loop !var ;" with ";"
if (Token::Match(tok2->next(), "loop !var ;"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
// Replace "loop !var alloc ;" with " alloc ;"
if (Token::Match(tok2->next(), "loop !var alloc ;"))
{
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
done = false;
}
// Replace "loop if return ;" with "if return ;"
if (Token::simpleMatch(tok2->next(), "loop if return"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Delete if block in "alloc ; if(!var) return ;"
if (Token::Match(tok2, "alloc ; if(!var) return ;"))
{
erase(tok2, tok2->tokAt(4));
Token::eraseTokens(tok2, tok2->tokAt(4));
done = false;
}
// Delete if block: "alloc; if return use ;"
if (Token::Match(tok2, "alloc ; if return use ; !!else"))
{
erase(tok2, tok2->tokAt(5));
Token::eraseTokens(tok2, tok2->tokAt(5));
done = false;
}
// Reduce "[;{}] return ; %var%" => "[;{}] return ;"
if (Token::Match(tok2, "[;{}] return ; %var%"))
{
erase(tok2->next()->next(), tok2->tokAt(4));
Token::eraseTokens(tok2->next()->next(), tok2->tokAt(4));
done = false;
}
// Reduce "[;{}] return use ; %var%" => "[;{}] return use ;"
if (Token::Match(tok2, "[;{}] return use ; %var%"))
{
erase(tok2->next()->next()->next(), tok2->tokAt(5));
Token::eraseTokens(tok2->next()->next()->next(), tok2->tokAt(5));
done = false;
}
// Reduce "if(var) return use ;" => "return use ;"
if (Token::Match(tok2->next(), "if(var) return use ; !!else"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Reduce "if(var) assign|dealloc|use ;" => "assign|dealloc|use ;"
if (Token::Match(tok2->next(), "if(var) assign|dealloc|use ; !!else"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
@ -1162,7 +1202,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Reduce "[;{}] alloc ; dealloc ; alloc ;" => "[;{}] alloc ;"
if (Token::Match(tok2, "[;{}] alloc ; dealloc ; alloc ;"))
{
erase(tok2->next(), tok2->tokAt(6));
Token::eraseTokens(tok2->next(), tok2->tokAt(6));
done = false;
}
@ -1170,35 +1210,35 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
if (Token::simpleMatch(tok2->tokAt(2), "alloc ; dealloc ;") &&
tok2->next()->str().find("if") == 0)
{
erase(tok2, tok2->tokAt(5));
Token::eraseTokens(tok2, tok2->tokAt(5));
done = false;
}
// Delete second use in "use ; use ;"
while (Token::Match(tok2, "[;{}] use ; use ;"))
{
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
done = false;
}
// Delete first part in "use ; dealloc ;"
if (Token::Match(tok2, "[;{}] use ; dealloc ;"))
{
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
done = false;
}
// Delete first part in "use ; return use ;"
if (Token::Match(tok2, "[;{}] use ; return use ;"))
{
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
done = false;
}
// Delete second case in "case ; case ;"
while (Token::simpleMatch(tok2, "case ; case ;"))
{
erase(tok2, tok2->tokAt(3));
Token::eraseTokens(tok2, tok2->tokAt(3));
done = false;
}
@ -1239,7 +1279,7 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
{
done = false;
tok2->str(";");
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
tok2 = tok2->next();
bool first = true;
while (Token::Match(tok2, "case|default"))
@ -1278,8 +1318,12 @@ void CheckMemoryLeak::simplifycode(Token *tok, bool &all)
// Check for memory leaks for a function variable.
void CheckMemoryLeak::CheckMemoryLeak_CheckScope(const Token *Tok1, const char varname[], bool classmember, unsigned int sz)
void CheckMemoryLeakInFunction::checkScope(const Token *Tok1, const char varname[], bool classmember, unsigned int sz)
{
std::list<const Token *> callstack;
@ -1297,7 +1341,7 @@ void CheckMemoryLeak::CheckMemoryLeak_CheckScope(const Token *Tok1, const char v
for (Token *tok2 = tok; tok2; tok2 = tok2->next())
{
while (Token::Match(tok2, "[;{}] ;"))
erase(tok2, tok2->tokAt(2));
Token::eraseTokens(tok2, tok2->tokAt(2));
}
if ((result = Token::findmatch(tok, "[;{}] dealloc [;{}] use|use_ ;")) != NULL)
{
@ -1414,11 +1458,16 @@ void CheckMemoryLeak::CheckMemoryLeak_CheckScope(const Token *Tok1, const char v
//---------------------------------------------------------------------------
// Checks for memory leaks inside function..
//---------------------------------------------------------------------------
void CheckMemoryLeak::CheckMemoryLeak_InFunction()
void CheckMemoryLeakInFunction::check()
{
bool classmember = false;
bool beforeParameters = false;
@ -1459,10 +1508,10 @@ void CheckMemoryLeak::CheckMemoryLeak_InFunction()
sz = 1;
if (Token::Match(tok, "[{};] %type% * %var% [;=]"))
CheckMemoryLeak_CheckScope(tok->next(), tok->strAt(3), classmember, sz);
checkScope(tok->next(), tok->strAt(3), classmember, sz);
else if (Token::Match(tok, "[{};] %type% %type% * %var% [;=]"))
CheckMemoryLeak_CheckScope(tok->next(), tok->strAt(4), classmember, sz);
checkScope(tok->next(), tok->strAt(4), classmember, sz);
}
}
}
@ -1470,13 +1519,39 @@ void CheckMemoryLeak::CheckMemoryLeak_InFunction()
//---------------------------------------------------------------------------
// Checks for memory leaks in classes..
//---------------------------------------------------------------------------
void CheckMemoryLeak::CheckMemoryLeak_ClassMembers()
void CheckMemoryLeakInClass::check()
{
int indentlevel = 0;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
@ -1491,13 +1566,13 @@ void CheckMemoryLeak::CheckMemoryLeak_ClassMembers()
{
std::vector<const char *> classname;
classname.push_back(tok->strAt(1));
CheckMemoryLeak_ClassMembers_ParseClass(tok, classname);
parseClass(tok, classname);
}
}
}
void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_ParseClass(const Token *tok1, std::vector<const char *> &classname)
void CheckMemoryLeakInClass::parseClass(const Token *tok1, std::vector<const char *> &classname)
{
// Go into class.
while (tok1 && tok1->str() != "{")
@ -1526,7 +1601,7 @@ void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_ParseClass(const Token *tok1,
if (Token::Match(tok, "class %var% [{:]"))
{
classname.push_back(tok->strAt(1));
CheckMemoryLeak_ClassMembers_ParseClass(tok, classname);
parseClass(tok, classname);
classname.pop_back();
}
@ -1539,20 +1614,20 @@ void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_ParseClass(const Token *tok1,
if (tok->isName() || Token::Match(tok, "[;}]"))
{
if (_settings->_showAll || !isclass(tok->tokAt(1)))
CheckMemoryLeak_ClassMembers_Variable(classname.back(), tok->tokAt(3));
if (_settings->_showAll || !isclass(_tokenizer, tok->tokAt(1)))
variable(classname.back(), tok->tokAt(3));
}
}
}
}
void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_Variable(const char classname[], const Token *tokVarname)
void CheckMemoryLeakInClass::variable(const char classname[], const Token *tokVarname)
{
const char *varname = tokVarname->strAt(0);
// Check if member variable has been allocated and deallocated..
AllocType Alloc = No;
AllocType Dealloc = No;
CheckMemoryLeak::AllocType Alloc = CheckMemoryLeak::No;
CheckMemoryLeak::AllocType Dealloc = CheckMemoryLeak::No;
// Loop through all tokens. Inspect member functions
int indent_ = 0;
@ -1585,13 +1660,13 @@ void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_Variable(const char classname
if (indent == 0 || Token::Match(tok, (std::string(varname) + " =").c_str()))
{
AllocType alloc = GetAllocationType(tok->tokAt((indent > 0) ? 2 : 3));
if (alloc != No)
if (alloc != CheckMemoryLeak::No)
{
if (Alloc != No && Alloc != alloc)
alloc = Many;
alloc = CheckMemoryLeak::Many;
std::list<const Token *> callstack;
if (alloc != Many && Dealloc != No && Dealloc != Many && Dealloc != alloc)
if (alloc != CheckMemoryLeak::Many && Dealloc != CheckMemoryLeak::No && Dealloc != CheckMemoryLeak::Many && Dealloc != alloc)
{
callstack.push_back(tok);
mismatchAllocDealloc(callstack, (std::string(classname) + "::" + varname).c_str());
@ -1615,13 +1690,13 @@ void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_Variable(const char classname
varnames[1] = varname;
dealloc = GetDeallocationType(tok, varnames);
}
if (dealloc != No)
if (dealloc != CheckMemoryLeak::No)
{
if (Dealloc != No && Dealloc != dealloc)
dealloc = Many;
if (Dealloc != CheckMemoryLeak::No && Dealloc != dealloc)
dealloc = CheckMemoryLeak::Many;
std::list<const Token *> callstack;
if (dealloc != Many && Alloc != No && Alloc != Many && Alloc != dealloc)
if (dealloc != CheckMemoryLeak::Many && Alloc != CheckMemoryLeak::No && Alloc != Many && Alloc != dealloc)
{
callstack.push_back(tok);
mismatchAllocDealloc(callstack, (std::string(classname) + "::" + varname).c_str());
@ -1637,7 +1712,7 @@ void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_Variable(const char classname
functionToken = Tokenizer::FindClassFunction(functionToken->next(), classname, "~| %var%", indent_);
}
if (Alloc != No && Dealloc == No)
if (Alloc != CheckMemoryLeak::No && Dealloc == CheckMemoryLeak::No)
{
MemoryLeak(tokVarname, (std::string(classname) + "::" + varname).c_str(), Alloc, true);
}
@ -1645,85 +1720,12 @@ void CheckMemoryLeak::CheckMemoryLeak_ClassMembers_Variable(const char classname
//---------------------------------------------------------------------------
// Non-recursive function analysis
//---------------------------------------------------------------------------
Token * CheckMemoryLeak::functionParameterCode(const Token *ftok, int parameter)
{
int param = 1; // First parameter has index 1
// Extract the code for specified parameter...
for (; ftok; ftok = ftok->next())
{
if (ftok->str() == ")")
break;
if (ftok->str() == ",")
{
++param;
if (param > parameter)
break;
}
if (param != parameter)
continue;
if (! Token::Match(ftok, "* %var% [,)]"))
continue;
// Extract and return the code for this parameter..
const char *parname = ftok->strAt(1);
// Goto function implementation..
while (ftok && ftok->str() != "{")
ftok = ftok->next();
ftok = ftok ? ftok->next() : NULL;
// Return the code..
AllocType alloc = No, dealloc = No;
bool all = false;
std::list<const Token *> callstack;
Token *code = getcode(ftok, callstack, parname, alloc, dealloc, false, all, 1);
simplifycode(code, all);
return code;
}
return NULL;
}
void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname)
{
reportError(tok, "error", "memleak", "Memory leak: " + varname);
}
void CheckMemoryLeak::memleakallError(const Token *tok, const std::string &varname)
{
reportError(tok, "all", "memleakall", "Memory leak: " + varname);
}
void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname)
{
reportError(tok, "error", "resourceLeak", "Resource leak: " + varname);
}
void CheckMemoryLeak::deallocDeallocError(const Token *tok, const std::string &varname)
{
reportError(tok, "error", "deallocDealloc", "Deallocating a deallocated pointer: " + varname);
}
void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname)
{
reportError(tok, "error", "deallocuse", "Using '" + varname + "' after it is deallocated / released");
}
void CheckMemoryLeak::mismatchSizeError(const Token *tok, const std::string &sz)
{
reportError(tok, "error", "mismatchSize", "The given size " + sz + " is mismatching");
}
void CheckMemoryLeak::mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname)
{
reportError(callstack, "error", "mismatchAllocDealloc", "Mismatching allocation and deallocation: " + varname);
}

View File

@ -33,63 +33,77 @@
class Token;
class CheckMemoryLeak : public Check
/** Base class for memory leaks checking */
class CheckMemoryLeak
{
protected:
CheckMemoryLeak() { }
/** What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
enum AllocType { No, Malloc, gMalloc, New, NewArray, File, Pipe, Dir, Many };
void MemoryLeak(const Token *tok, const char varname[], AllocType alloctype, bool all);
void MismatchError(const Token *Tok1, const std::list<const Token *> &callstack, const char varname[]);
AllocType GetDeallocationType(const Token *tok, const char *varnames[]);
AllocType GetAllocationType(const Token *tok2);
AllocType GetReallocationType(const Token *tok2);
bool isclass(const Tokenizer *_tokenizer, const Token *typestr) const;
void memleakError(const Token *tok, const std::string &varname);
void memleakallError(const Token *tok, const std::string &varname);
void resourceLeakError(const Token *tok, const std::string &varname);
void deallocDeallocError(const Token *tok, const std::string &varname);
void deallocuseError(const Token *tok, const std::string &varname);
void mismatchSizeError(const Token *tok, const std::string &sz);
void mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname);
// error message
virtual void error(const Token *tok, const std::string &severity, const std::string &id, const std::string &msg) = 0;
virtual void error(const std::list<const Token *> &callstack, const std::string &severity, const std::string &id, const std::string &msg) = 0;
};
class CheckMemoryLeakInFunction : public CheckMemoryLeak, public Check
{
public:
CheckMemoryLeak() : Check()
CheckMemoryLeakInFunction() : Check()
{ }
CheckMemoryLeak(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(tokenizer, settings, errorLogger)
{ }
void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
{
CheckMemoryLeak checkMemoryLeak(tokenizer, settings, errorLogger);
checkMemoryLeak.CheckMemoryLeak_InFunction();
if (settings->_showAll)
checkMemoryLeak.CheckMemoryLeak_ClassMembers();
CheckMemoryLeakInFunction checkMemoryLeak(tokenizer, settings, errorLogger);
checkMemoryLeak.check();
}
#ifndef UNIT_TESTING
private:
#endif
void check();
/** What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
enum AllocType { No, Malloc, gMalloc, New, NewArray, File, Pipe, Dir, Many };
private:
// Extra allocation..
class AllocFunc
{
public:
const char *funcname;
AllocType alloctype;
AllocFunc(const char f[], AllocType a)
: funcname(f), alloctype(a)
{ }
};
void CheckMemoryLeak_ClassMembers_Variable(const char classname[], const Token *tokVarname);
void CheckMemoryLeak_ClassMembers_ParseClass(const Token *tok1, std::vector<const char *> &classname);
void CheckMemoryLeak_ClassMembers();
void CheckMemoryLeak_InFunction();
void CheckMemoryLeak_CheckScope(const Token *Tok1, const char varname[], bool classmember, unsigned int sz);
bool MatchFunctionsThatReturnArg(const Token *tok, const std::string &varname);
/**
* Simplify code e.g. by replacing empty "{ }" with ";"
* @param tok first token. The tokens list can be modified.
* Check if there is a "!var" match inside a condition
* @param tok first token to match
* @param varnames the varname
* @param endpar if this is true the "!var" must be followed by ")"
* @return true if match
*/
void simplifycode(Token *tok, bool &all);
bool notvar(const Token *tok, const char *varnames[], bool endpar = false);
/**
* Delete tokens between begin and end. E.g. if begin = 1
* and end = 5, tokens 2,3 and 4 would be erased.
*
* @param begin Tokens after this will be erased.
* @param end Tokens before this will be erased.
*/
void erase(Token *begin, const Token *end);
const char * call_func(const Token *tok, std::list<const Token *> callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype, bool &all, unsigned int sz);
/**
* Extract a new tokens list that is easier to parse than the "tokens"
@ -104,53 +118,71 @@ private:
Token *getcode(const Token *tok, std::list<const Token *> callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype, bool classmember, bool &all, unsigned int sz);
/**
* Check if there is a "!var" match inside a condition
* @param tok first token to match
* @param varnames the varname
* @param endpar if this is true the "!var" must be followed by ")"
* @return true if match
* Simplify code e.g. by replacing empty "{ }" with ";"
* @param tok first token. The tokens list can be modified.
*/
bool notvar(const Token *tok, const char *varnames[], bool endpar = false);
void simplifycode(Token *tok, bool &all);
bool MatchFunctionsThatReturnArg(const Token *tok, const std::string &varname);
void MemoryLeak(const Token *tok, const char varname[], AllocType alloctype, bool all);
void MismatchError(const Token *Tok1, const std::list<const Token *> &callstack, const char varname[]);
const char * call_func(const Token *tok, std::list<const Token *> callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype, bool &all, unsigned int sz);
AllocType GetDeallocationType(const Token *tok, const char *varnames[]);
AllocType GetAllocationType(const Token *tok2);
AllocType GetReallocationType(const Token *tok2);
bool isclass(const Token *typestr);
void checkScope(const Token *Tok1, const char varname[], bool classmember, unsigned int sz);
std::list<AllocFunc> _listAllocFunc;
void memleakError(const Token *tok, const std::string &varname);
void memleakallError(const Token *tok, const std::string &varname);
void resourceLeakError(const Token *tok, const std::string &varname);
void deallocDeallocError(const Token *tok, const std::string &varname);
void deallocuseError(const Token *tok, const std::string &varname);
void mismatchSizeError(const Token *tok, const std::string &sz);
void mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname);
void getErrorMessages()
void error(const Token *tok, const std::string &severity, const std::string &id, const std::string &msg)
{
std::cout << "===memory leaks===" << "\n";
memleakError(0, "varname");
memleakallError(0, "varname");
resourceLeakError(0, "varname");
deallocDeallocError(0, "varname");
deallocuseError(0, "varname");
mismatchSizeError(0, "sz");
std::list<const Token *> callstack;
mismatchAllocDealloc(callstack, "varname");
reportError(tok, severity, id, msg);
}
void error(const std::list<const Token *> &callstack, const std::string &severity, const std::string &id, const std::string &msg)
{
reportError(callstack, severity, id, msg);
}
void getErrorMessages()
{ }
// Experimental functionality..
protected:
Token *functionParameterCode(const Token *ftok, int parameter);
};
class CheckMemoryLeakInClass : public CheckMemoryLeak, public Check
{
public:
CheckMemoryLeakInClass() : Check()
{ }
CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(tokenizer, settings, errorLogger)
{ }
void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
{
CheckMemoryLeakInClass checkMemoryLeak(tokenizer, settings, errorLogger);
checkMemoryLeak.check();
}
#ifndef UNIT_TESTING
private:
#endif
void check();
private:
void parseClass(const Token *tok1, std::vector<const char *> &classname);
void variable(const char classname[], const Token *tokVarname);
void error(const Token *tok, const std::string &severity, const std::string &id, const std::string &msg)
{
reportError(tok, severity, id, msg);
}
void error(const std::list<const Token *> &callstack, const std::string &severity, const std::string &id, const std::string &msg)
{
reportError(callstack, severity, id, msg);
}
void getErrorMessages()
{ }
};
//---------------------------------------------------------------------------
#endif

View File

@ -29,10 +29,10 @@
extern std::ostringstream errout;
class TestMemleak : public TestFixture
class TestMemleakInFunction : public TestFixture
{
public:
TestMemleak() : TestFixture("TestMemleak")
TestMemleakInFunction() : TestFixture("TestMemleakInFunction")
{ }
private:
@ -53,9 +53,8 @@ private:
settings._debug = true;
settings._showAll = showAll;
tokenizer.fillFunctionList();
CheckMemoryLeak checkMemoryLeak(&tokenizer, &settings, this);
checkMemoryLeak.CheckMemoryLeak_InFunction();
checkMemoryLeak.CheckMemoryLeak_ClassMembers();
CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings, this);
checkMemoryLeak.check();
}
void run()
@ -145,18 +144,6 @@ private:
TEST_CASE(func12);
TEST_CASE(func13);
TEST_CASE(class1);
TEST_CASE(class2);
TEST_CASE(class3);
TEST_CASE(class4);
TEST_CASE(class5);
TEST_CASE(class6);
TEST_CASE(class7);
TEST_CASE(class8);
TEST_CASE(class9);
TEST_CASE(class10);
TEST_CASE(class11);
TEST_CASE(throw1);
TEST_CASE(throw2);
@ -208,7 +195,6 @@ private:
TEST_CASE(vcl2);
TEST_CASE(autoptr1);
TEST_CASE(free_member_in_sub_func);
TEST_CASE(if_with_and);
TEST_CASE(assign_pclose);
@ -1393,235 +1379,6 @@ private:
void class1()
{
check("class Fred\n"
"{\n"
"private:\n"
" char *str1;\n"
" char *str2;\n"
"public:\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"Fred::Fred()\n"
"{\n"
" str1 = new char[10];\n"
" str2 = new char[10];\n"
"}\n"
"\n"
"Fred::~Fred()\n"
"{\n"
" delete [] str2;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (all) Memory leak: Fred::str1\n", errout.str());
}
void class2()
{
check("class Fred\n"
"{\n"
"private:\n"
" char *str1;\n"
"public:\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"Fred::Fred()\n"
"{\n"
" str1 = new char[10];\n"
"}\n"
"\n"
"Fred::~Fred()\n"
"{\n"
" free(str1);\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:17]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout.str());
}
void class3()
{
check("class Token;\n"
"\n"
"class Tokenizer\n"
"{\n"
"private:\n"
" Token *_tokens;\n"
"\n"
"public:\n"
" Tokenizer();\n"
" ~Tokenizer();\n"
" void deleteTokens(Token *tok);\n"
"};\n"
"\n"
"Tokenizer::Tokenizer()\n"
"{\n"
" _tokens = new Token;\n"
"}\n"
"\n"
"Tokenizer::~Tokenizer()\n"
"{\n"
" deleteTokens(_tokens);\n"
"}\n"
"\n"
"void Tokenizer::deleteTokens(Token *tok)\n"
"{\n"
" while (tok)\n"
" {\n"
" Token *next = tok->next();\n"
" delete tok;\n"
" tok = next;\n"
" }\n"
"}\n", true);
TODO_ASSERT_EQUALS("", errout.str());
}
void class4()
{
check("struct ABC;\n"
"class Fred\n"
"{\n"
"private:\n"
" void addAbc(ABC *abc);\n"
"public:\n"
" void click();\n"
"};\n"
"\n"
"void Fred::addAbc(ABC* abc)\n"
"{\n"
" AbcPosts->Add(abc);\n"
"}\n"
"\n"
"void Fred::click()\n"
"{\n"
" ABC *p = new ABC;\n"
" addAbc( p );\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void class5()
{
check("class Fred\n"
"{\n"
"public:\n"
" void foo();\n"
"};\n"
"\n"
"void Fred::foo()\n"
"{\n"
" char *str = new char[100];\n"
"}\n");
ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: str\n", errout.str());
}
void class6()
{
check("class Fred\n"
"{\n"
"public:\n"
" void foo();\n"
"};\n"
"\n"
"void Fred::foo()\n"
"{\n"
" char *str = new char[100];\n"
" delete [] str;\n"
" hello();\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void class7()
{
check("class Fred\n"
"{\n"
"public:\n"
" int *i;\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"Fred::Fred()\n"
"{\n"
" this->i = new int;\n"
"}\n"
"Fred::~Fred()\n"
"{\n"
" delete this->i;\n"
"}\n", true);
ASSERT_EQUALS("", errout.str());
}
void class8()
{
check("class A\n"
"{\n"
"public:\n"
" void a();\n"
" void doNothing() { }\n"
"};\n"
"\n"
"void A::a()\n"
"{\n"
" int* c = new int(1);\n"
" delete c;\n"
" doNothing(c);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void class9()
{
check("class A\n"
"{\n"
"public:\n"
" int * p;\n"
" A();\n"
" ~A();\n"
"};\n"
"\n"
"A::A()\n"
"{ p = new int; }\n"
"\n"
"A::~A()\n"
"{ delete (p); }\n", true);
ASSERT_EQUALS("", errout.str());
}
void class10()
{
check("class A\n"
"{\n"
"public:\n"
" int * p;\n"
" A() { p = new int; }\n"
"};\n", true);
ASSERT_EQUALS("[test.cpp:4]: (all) Memory leak: A::p\n", errout.str());
}
void class11()
{
check("class A\n"
"{\n"
"public:\n"
" int * p;\n"
" A();\n"
"};\n"
"A::A() : p(new int[10])\n"
"{ }", true);
ASSERT_EQUALS("[test.cpp:4]: (all) Memory leak: A::p\n", errout.str());
}
void throw1()
{
@ -2103,8 +1860,8 @@ private:
settings.autoDealloc(istr);
}
CheckMemoryLeak checkMemoryLeak(&tokenizer, &settings, this);
checkMemoryLeak.CheckMemoryLeak_InFunction();
CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings, this);
checkMemoryLeak.check();
}
@ -2147,37 +1904,6 @@ private:
ASSERT_EQUALS("", errout.str());
}
void free_member_in_sub_func()
{
check("class Tokenizer\n"
"{\n"
"public:\n"
" Tokenizer();\n"
" ~Tokenizer();\n"
"\n"
"private:\n"
" int *_tokens;\n"
" static void deleteTokens(int *tok);\n"
"};\n"
"\n"
"Tokenizer::Tokenizer()\n"
"{\n"
" _tokens = new int;\n"
"}\n"
"\n"
"Tokenizer::~Tokenizer()\n"
"{\n"
" deleteTokens(_tokens);\n"
" _tokens = 0;\n"
"}\n"
"\n"
"void Tokenizer::deleteTokens(int *tok)\n"
"{\n"
" delete tok;\n"
"}\n", true);
TODO_ASSERT_EQUALS("", errout.str());
}
void if_with_and()
{
check("void f()\n"
@ -2438,6 +2164,311 @@ private:
}
};
REGISTER_TEST(TestMemleak)
static TestMemleakInFunction testMemleakInFunction;
class TestMemleakInClass : public TestFixture
{
public:
TestMemleakInClass() : TestFixture("TestMemleakInClass")
{ }
private:
void check(const char code[], bool showAll = false)
{
// Tokenize..
Tokenizer tokenizer;
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
tokenizer.setVarId();
tokenizer.simplifyTokenList();
// Clear the error buffer..
errout.str("");
// Check for memory leaks..
Settings settings;
settings._debug = true;
settings._showAll = showAll;
tokenizer.fillFunctionList();
CheckMemoryLeakInClass checkMemoryLeak(&tokenizer, &settings, this);
checkMemoryLeak.check();
}
void run()
{
TEST_CASE(class1);
TEST_CASE(class2);
TEST_CASE(class3);
TEST_CASE(class4);
TEST_CASE(class6);
TEST_CASE(class7);
TEST_CASE(class8);
TEST_CASE(class9);
TEST_CASE(class10);
TEST_CASE(class11);
TEST_CASE(free_member_in_sub_func);
}
void class1()
{
check("class Fred\n"
"{\n"
"private:\n"
" char *str1;\n"
" char *str2;\n"
"public:\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"Fred::Fred()\n"
"{\n"
" str1 = new char[10];\n"
" str2 = new char[10];\n"
"}\n"
"\n"
"Fred::~Fred()\n"
"{\n"
" delete [] str2;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (all) Memory leak: Fred::str1\n", errout.str());
}
void class2()
{
check("class Fred\n"
"{\n"
"private:\n"
" char *str1;\n"
"public:\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"Fred::Fred()\n"
"{\n"
" str1 = new char[10];\n"
"}\n"
"\n"
"Fred::~Fred()\n"
"{\n"
" free(str1);\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:17]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout.str());
}
void class3()
{
check("class Token;\n"
"\n"
"class Tokenizer\n"
"{\n"
"private:\n"
" Token *_tokens;\n"
"\n"
"public:\n"
" Tokenizer();\n"
" ~Tokenizer();\n"
" void deleteTokens(Token *tok);\n"
"};\n"
"\n"
"Tokenizer::Tokenizer()\n"
"{\n"
" _tokens = new Token;\n"
"}\n"
"\n"
"Tokenizer::~Tokenizer()\n"
"{\n"
" deleteTokens(_tokens);\n"
"}\n"
"\n"
"void Tokenizer::deleteTokens(Token *tok)\n"
"{\n"
" while (tok)\n"
" {\n"
" Token *next = tok->next();\n"
" delete tok;\n"
" tok = next;\n"
" }\n"
"}\n", true);
TODO_ASSERT_EQUALS("", errout.str());
}
void class4()
{
check("struct ABC;\n"
"class Fred\n"
"{\n"
"private:\n"
" void addAbc(ABC *abc);\n"
"public:\n"
" void click();\n"
"};\n"
"\n"
"void Fred::addAbc(ABC* abc)\n"
"{\n"
" AbcPosts->Add(abc);\n"
"}\n"
"\n"
"void Fred::click()\n"
"{\n"
" ABC *p = new ABC;\n"
" addAbc( p );\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void class6()
{
check("class Fred\n"
"{\n"
"public:\n"
" void foo();\n"
"};\n"
"\n"
"void Fred::foo()\n"
"{\n"
" char *str = new char[100];\n"
" delete [] str;\n"
" hello();\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void class7()
{
check("class Fred\n"
"{\n"
"public:\n"
" int *i;\n"
" Fred();\n"
" ~Fred();\n"
"};\n"
"\n"
"Fred::Fred()\n"
"{\n"
" this->i = new int;\n"
"}\n"
"Fred::~Fred()\n"
"{\n"
" delete this->i;\n"
"}\n", true);
ASSERT_EQUALS("", errout.str());
}
void class8()
{
check("class A\n"
"{\n"
"public:\n"
" void a();\n"
" void doNothing() { }\n"
"};\n"
"\n"
"void A::a()\n"
"{\n"
" int* c = new int(1);\n"
" delete c;\n"
" doNothing(c);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void class9()
{
check("class A\n"
"{\n"
"public:\n"
" int * p;\n"
" A();\n"
" ~A();\n"
"};\n"
"\n"
"A::A()\n"
"{ p = new int; }\n"
"\n"
"A::~A()\n"
"{ delete (p); }\n", true);
ASSERT_EQUALS("", errout.str());
}
void class10()
{
check("class A\n"
"{\n"
"public:\n"
" int * p;\n"
" A() { p = new int; }\n"
"};\n", true);
ASSERT_EQUALS("[test.cpp:4]: (all) Memory leak: A::p\n", errout.str());
}
void class11()
{
check("class A\n"
"{\n"
"public:\n"
" int * p;\n"
" A();\n"
"};\n"
"A::A() : p(new int[10])\n"
"{ }", true);
ASSERT_EQUALS("[test.cpp:4]: (all) Memory leak: A::p\n", errout.str());
}
void free_member_in_sub_func()
{
check("class Tokenizer\n"
"{\n"
"public:\n"
" Tokenizer();\n"
" ~Tokenizer();\n"
"\n"
"private:\n"
" int *_tokens;\n"
" static void deleteTokens(int *tok);\n"
"};\n"
"\n"
"Tokenizer::Tokenizer()\n"
"{\n"
" _tokens = new int;\n"
"}\n"
"\n"
"Tokenizer::~Tokenizer()\n"
"{\n"
" deleteTokens(_tokens);\n"
" _tokens = 0;\n"
"}\n"
"\n"
"void Tokenizer::deleteTokens(int *tok)\n"
"{\n"
" delete tok;\n"
"}\n", true);
TODO_ASSERT_EQUALS("", errout.str());
}
};
static TestMemleakInClass testMemleakInClass;