Refactoring: Split up the CheckMemoryLeak into CheckMemoryLeakInFunction and CheckMemoryLeakInClass
This commit is contained in:
parent
15dbf9c085
commit
2c07c22d9e
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue