Memory leaks: Code cleanups
This commit is contained in:
parent
8624c0b9fd
commit
9fc7453917
|
@ -549,17 +549,19 @@ const char *CheckMemoryLeak::functionArgAlloc(const Token *tok, unsigned int tar
|
||||||
|
|
||||||
void CheckMemoryLeakInFunction::parse_noreturn()
|
void CheckMemoryLeakInFunction::parse_noreturn()
|
||||||
{
|
{
|
||||||
noreturn.insert("exit");
|
if (noreturn.empty()) {
|
||||||
noreturn.insert("_exit");
|
noreturn.insert("exit");
|
||||||
noreturn.insert("_Exit");
|
noreturn.insert("_exit");
|
||||||
noreturn.insert("abort");
|
noreturn.insert("_Exit");
|
||||||
noreturn.insert("err");
|
noreturn.insert("abort");
|
||||||
noreturn.insert("verr");
|
noreturn.insert("err");
|
||||||
noreturn.insert("errx");
|
noreturn.insert("verr");
|
||||||
noreturn.insert("verrx");
|
noreturn.insert("errx");
|
||||||
noreturn.insert("ExitProcess");
|
noreturn.insert("verrx");
|
||||||
noreturn.insert("ExitThread");
|
noreturn.insert("ExitProcess");
|
||||||
noreturn.insert("pthread_exit");
|
noreturn.insert("ExitThread");
|
||||||
|
noreturn.insert("pthread_exit");
|
||||||
|
}
|
||||||
|
|
||||||
std::list<Scope>::const_iterator scope;
|
std::list<Scope>::const_iterator scope;
|
||||||
|
|
||||||
|
@ -702,90 +704,81 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
|
||||||
return (tok->previous()->str() != "=") ? "callfunc" : NULL;
|
return (tok->previous()->str() != "=") ? "callfunc" : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int par = 1;
|
unsigned int par = 0;
|
||||||
unsigned int parlevel = 0;
|
|
||||||
|
|
||||||
const bool dot(tok->previous()->str() == ".");
|
const bool dot(tok->previous()->str() == ".");
|
||||||
const bool eq(tok->previous()->str() == "=");
|
const bool eq(tok->previous()->str() == "=");
|
||||||
|
|
||||||
for (; tok; tok = tok->next()) {
|
tok = Token::findsimplematch(tok, "(");
|
||||||
if (tok->str() == "(")
|
if (tok)
|
||||||
++parlevel;
|
tok = tok->next();
|
||||||
else if (tok->str() == ")") {
|
|
||||||
--parlevel;
|
for (; tok; tok = tok->nextArgument()) {
|
||||||
if (parlevel < 1) {
|
++par;
|
||||||
return (eq || _settings->experimental) ? 0 : "callfunc";
|
if (varid > 0 && Token::Match(tok, "%varid% [,()]", varid)) {
|
||||||
|
if (dot)
|
||||||
|
return "use";
|
||||||
|
|
||||||
|
const Token *ftok = _tokenizer->getFunctionTokenByName(funcname.c_str());
|
||||||
|
if (!ftok)
|
||||||
|
return "use";
|
||||||
|
|
||||||
|
// how many parameters does the function want?
|
||||||
|
if (numpar != countParameters(ftok))
|
||||||
|
return "recursive";
|
||||||
|
|
||||||
|
const char *parname = Tokenizer::getParameterName(ftok, par);
|
||||||
|
if (! parname)
|
||||||
|
return "recursive";
|
||||||
|
unsigned int parameterVarid = 0;
|
||||||
|
{
|
||||||
|
const Token *partok = Token::findmatch(ftok, parname);
|
||||||
|
if (partok)
|
||||||
|
parameterVarid = partok->varId();
|
||||||
}
|
}
|
||||||
|
if (parameterVarid == 0)
|
||||||
|
return "recursive";
|
||||||
|
// Check if the function deallocates the variable..
|
||||||
|
while (ftok && (ftok->str() != "{"))
|
||||||
|
ftok = ftok->next();
|
||||||
|
if (!ftok)
|
||||||
|
return 0;
|
||||||
|
Token *func = getcode(ftok->next(), callstack, parameterVarid, alloctype, dealloctype, false, sz);
|
||||||
|
//simplifycode(func, all);
|
||||||
|
const Token *func_ = func;
|
||||||
|
while (func_ && func_->str() == ";")
|
||||||
|
func_ = func_->next();
|
||||||
|
|
||||||
|
const char *ret = 0;
|
||||||
|
/** @todo handle "goto" */
|
||||||
|
if (Token::findsimplematch(func_, "dealloc"))
|
||||||
|
ret = "dealloc";
|
||||||
|
else if (Token::findsimplematch(func_, "use"))
|
||||||
|
ret = "use";
|
||||||
|
else if (Token::findsimplematch(func_, "&use"))
|
||||||
|
ret = "&use";
|
||||||
|
|
||||||
|
Tokenizer::deleteTokens(func);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
if (varid > 0 && Token::Match(tok, "& %varid% [,()]", varid)) {
|
||||||
|
const Token *ftok = _tokenizer->getFunctionTokenByName(funcname.c_str());
|
||||||
|
AllocType a;
|
||||||
|
const char *ret = functionArgAlloc(ftok, par, a);
|
||||||
|
|
||||||
if (parlevel == 1) {
|
if (a != No) {
|
||||||
if (tok->str() == ",")
|
if (alloctype == No)
|
||||||
++par;
|
alloctype = a;
|
||||||
if (varid > 0 && Token::Match(tok, "[,()] %varid% [,()]", varid)) {
|
else if (alloctype != a)
|
||||||
if (dot)
|
alloctype = Many;
|
||||||
return "use";
|
allocpar = true;
|
||||||
|
|
||||||
const Token *ftok = _tokenizer->getFunctionTokenByName(funcname.c_str());
|
|
||||||
if (!ftok)
|
|
||||||
return "use";
|
|
||||||
|
|
||||||
// how many parameters does the function want?
|
|
||||||
if (numpar != countParameters(ftok))
|
|
||||||
return "recursive";
|
|
||||||
|
|
||||||
const char *parname = Tokenizer::getParameterName(ftok, par);
|
|
||||||
if (! parname)
|
|
||||||
return "recursive";
|
|
||||||
unsigned int parameterVarid = 0;
|
|
||||||
{
|
|
||||||
const Token *partok = Token::findmatch(ftok, parname);
|
|
||||||
if (partok)
|
|
||||||
parameterVarid = partok->varId();
|
|
||||||
}
|
|
||||||
if (parameterVarid == 0)
|
|
||||||
return "recursive";
|
|
||||||
// Check if the function deallocates the variable..
|
|
||||||
while (ftok && (ftok->str() != "{"))
|
|
||||||
ftok = ftok->next();
|
|
||||||
if (!ftok)
|
|
||||||
return 0;
|
|
||||||
Token *func = getcode(ftok->next(), callstack, parameterVarid, alloctype, dealloctype, false, sz);
|
|
||||||
//simplifycode(func, all);
|
|
||||||
const Token *func_ = func;
|
|
||||||
while (func_ && func_->str() == ";")
|
|
||||||
func_ = func_->next();
|
|
||||||
|
|
||||||
const char *ret = 0;
|
|
||||||
/** @todo handle "goto" */
|
|
||||||
if (Token::findsimplematch(func_, "dealloc"))
|
|
||||||
ret = "dealloc";
|
|
||||||
else if (Token::findsimplematch(func_, "use"))
|
|
||||||
ret = "use";
|
|
||||||
else if (Token::findsimplematch(func_, "&use"))
|
|
||||||
ret = "&use";
|
|
||||||
|
|
||||||
Tokenizer::deleteTokens(func);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (varid > 0 && Token::Match(tok, "[,()] & %varid% [,()]", varid)) {
|
|
||||||
const Token *ftok = _tokenizer->getFunctionTokenByName(funcname.c_str());
|
|
||||||
AllocType a;
|
|
||||||
const char *ret = functionArgAlloc(ftok, par, a);
|
|
||||||
|
|
||||||
if (a != No) {
|
|
||||||
if (alloctype == No)
|
|
||||||
alloctype = a;
|
|
||||||
else if (alloctype != a)
|
|
||||||
alloctype = Many;
|
|
||||||
allocpar = true;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (varid > 0 && Token::Match(tok, "[(,] %varid% . %var% [,)]", varid))
|
|
||||||
return "use";
|
|
||||||
}
|
}
|
||||||
|
if (varid > 0 && Token::Match(tok, "%varid% . %var% [,)]", varid))
|
||||||
|
return "use";
|
||||||
}
|
}
|
||||||
return NULL;
|
return (eq || _settings->experimental) ? 0 : "callfunc";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -835,7 +828,7 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
|
||||||
addtoken(&rettail, tok, ";");
|
addtoken(&rettail, tok, ";");
|
||||||
|
|
||||||
// Start of new statement.. check if the statement has anything interesting
|
// Start of new statement.. check if the statement has anything interesting
|
||||||
if (Token::Match(tok, "[;{}]") && varid > 0 && parlevel == 0) {
|
if (varid > 0 && parlevel == 0 && Token::Match(tok, "[;{}]")) {
|
||||||
if (Token::Match(tok->next(), "[{};]"))
|
if (Token::Match(tok->next(), "[{};]"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1226,7 +1219,7 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue / break..
|
// continue / break..
|
||||||
if (tok->str() == "continue") {
|
else if (tok->str() == "continue") {
|
||||||
addtoken(&rettail, tok, "continue");
|
addtoken(&rettail, tok, "continue");
|
||||||
} else if (tok->str() == "break") {
|
} else if (tok->str() == "break") {
|
||||||
addtoken(&rettail, tok, "break");
|
addtoken(&rettail, tok, "break");
|
||||||
|
@ -2286,36 +2279,23 @@ void CheckMemoryLeakInFunction::checkReallocUsage()
|
||||||
// Checks for memory leaks inside function..
|
// Checks for memory leaks inside function..
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void CheckMemoryLeakInFunction::parseFunctionScope(const Token *tok, const Token *tok1, const bool classmember)
|
void CheckMemoryLeakInFunction::parseFunctionScope(const Token *body, const Token *arg, const bool classmember)
|
||||||
{
|
{
|
||||||
// Check locking/unlocking of global resources..
|
// Check locking/unlocking of global resources..
|
||||||
checkScope(tok->next(), "", 0, classmember, 1);
|
checkScope(body->next(), "", 0, classmember, 1);
|
||||||
|
|
||||||
// Locate parameters and check their usage..
|
// Locate parameters and check their usage..
|
||||||
for (const Token *tok2 = tok1; tok2; tok2 = tok2->next()) {
|
for (const Token *tok2 = arg->next(); tok2; tok2 = tok2->nextArgument()) {
|
||||||
if (tok2 == tok)
|
if (Token::Match(tok2, "%type% * %var% [,)]") && tok2->isStandardType()) {
|
||||||
break;
|
const std::string varname(tok2->strAt(2));
|
||||||
if (tok2->str() == ")")
|
const unsigned int varid = tok2->tokAt(2)->varId();
|
||||||
break;
|
const unsigned int sz = _tokenizer->sizeOfType(tok2);
|
||||||
if (Token::Match(tok2, "[(,] %type% * %var% [,)]") && tok2->next()->isStandardType()) {
|
checkScope(body->next(), varname, varid, classmember, sz);
|
||||||
const std::string varname(tok2->strAt(3));
|
|
||||||
const unsigned int varid = tok2->tokAt(3)->varId();
|
|
||||||
const unsigned int sz = _tokenizer->sizeOfType(tok2->next());
|
|
||||||
checkScope(tok->next(), varname, varid, classmember, sz);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locate variable declarations and check their usage..
|
// Locate variable declarations and check their usage..
|
||||||
unsigned int indentlevel = 0;
|
for (const Token* tok = body; tok && tok != body->link(); tok = tok->next()) {
|
||||||
do {
|
|
||||||
if (tok->str() == "{")
|
|
||||||
++indentlevel;
|
|
||||||
else if (tok->str() == "}") {
|
|
||||||
if (indentlevel <= 1)
|
|
||||||
break;
|
|
||||||
--indentlevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip these weird blocks... "( { ... } )"
|
// Skip these weird blocks... "( { ... } )"
|
||||||
if (Token::simpleMatch(tok, "( {")) {
|
if (Token::simpleMatch(tok, "( {")) {
|
||||||
tok = tok->link();
|
tok = tok->link();
|
||||||
|
@ -2324,36 +2304,37 @@ void CheckMemoryLeakInFunction::parseFunctionScope(const Token *tok, const Token
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Token::Match(tok, "[{};] %type%"))
|
if (!Token::Match(tok, "[{};:] %type%"))
|
||||||
continue;
|
continue;
|
||||||
|
tok = tok->next();
|
||||||
|
|
||||||
// Don't check static/extern variables
|
// Don't check static/extern variables
|
||||||
if (Token::Match(tok->next(), "static|extern"))
|
if (Token::Match(tok, "static|extern"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// return/else is not part of a variable declaration..
|
// return/else is not part of a variable declaration..
|
||||||
if (Token::Match(tok->next(), "return|else"))
|
if (Token::Match(tok, "return|else"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
unsigned int sz = _tokenizer->sizeOfType(tok->next());
|
unsigned int sz = _tokenizer->sizeOfType(tok);
|
||||||
if (sz < 1)
|
if (sz < 1)
|
||||||
sz = 1;
|
sz = 1;
|
||||||
|
|
||||||
if (Token::Match(tok, "[{};] %type% * const| %var% [;=]")) {
|
if (Token::Match(tok, "%type% * const| %var% [;=]")) {
|
||||||
|
const Token *vartok = tok->tokAt(tok->strAt(2) != "const" ? 2 : 3);
|
||||||
|
checkScope(tok, vartok->str(), vartok->varId(), classmember, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Token::Match(tok, "%type% %type% * const| %var% [;=]")) {
|
||||||
const Token *vartok = tok->tokAt(tok->strAt(3) != "const" ? 3 : 4);
|
const Token *vartok = tok->tokAt(tok->strAt(3) != "const" ? 3 : 4);
|
||||||
checkScope(tok->next(), vartok->str(), vartok->varId(), classmember, sz);
|
checkScope(tok, vartok->str(), vartok->varId(), classmember, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (Token::Match(tok, "[{};] %type% %type% * const| %var% [;=]")) {
|
else if (Token::Match(tok, "int %var% [;=]")) {
|
||||||
const Token *vartok = tok->tokAt(tok->strAt(4) != "const" ? 4 : 5);
|
const Token *vartok = tok->next();
|
||||||
checkScope(tok->next(), vartok->str(), vartok->varId(), classmember, sz);
|
checkScope(tok, vartok->str(), vartok->varId(), classmember, sz);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (Token::Match(tok, "[{};] int %var% [;=]")) {
|
|
||||||
const Token *vartok = tok->tokAt(2);
|
|
||||||
checkScope(tok->next(), vartok->str(), vartok->varId(), classmember, sz);
|
|
||||||
}
|
|
||||||
} while (NULL != (tok = tok->next()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckMemoryLeakInFunction::check()
|
void CheckMemoryLeakInFunction::check()
|
||||||
|
@ -2361,18 +2342,15 @@ void CheckMemoryLeakInFunction::check()
|
||||||
// fill the "noreturn"
|
// fill the "noreturn"
|
||||||
parse_noreturn();
|
parse_noreturn();
|
||||||
|
|
||||||
std::list<Scope>::const_iterator scope;
|
for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
|
||||||
|
|
||||||
for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
|
|
||||||
// only check functions
|
// only check functions
|
||||||
if (scope->type != Scope::eFunction)
|
if (scope->type != Scope::eFunction)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Token *tok = scope->classStart;
|
const Token *body = scope->classStart;
|
||||||
const Token *tok1 = scope->classDef->next();
|
const Token *arg = scope->classDef->next();
|
||||||
bool classmember = scope->functionOf != NULL;
|
bool classmember = scope->functionOf != NULL;
|
||||||
|
parseFunctionScope(body, arg, classmember);
|
||||||
parseFunctionScope(tok, tok1, classmember);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -2450,7 +2428,7 @@ void CheckMemoryLeakInClass::check()
|
||||||
|
|
||||||
void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarname)
|
void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarname)
|
||||||
{
|
{
|
||||||
const std::string varname = tokVarname->strAt(0);
|
const std::string varname = tokVarname->str();
|
||||||
const std::string classname = scope->className;
|
const std::string classname = scope->className;
|
||||||
|
|
||||||
// Check if member variable has been allocated and deallocated..
|
// Check if member variable has been allocated and deallocated..
|
||||||
|
@ -2654,11 +2632,13 @@ bool CheckMemoryLeakStructMember::isMalloc(const Token *vartok)
|
||||||
void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok)
|
void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok)
|
||||||
{
|
{
|
||||||
// This should be in the CheckMemoryLeak base class
|
// This should be in the CheckMemoryLeak base class
|
||||||
std::set<std::string> ignoredFunctions;
|
static std::set<std::string> ignoredFunctions;
|
||||||
ignoredFunctions.insert("if");
|
if (ignoredFunctions.empty()) {
|
||||||
ignoredFunctions.insert("for");
|
ignoredFunctions.insert("if");
|
||||||
ignoredFunctions.insert("while");
|
ignoredFunctions.insert("for");
|
||||||
ignoredFunctions.insert("malloc");
|
ignoredFunctions.insert("while");
|
||||||
|
ignoredFunctions.insert("malloc");
|
||||||
|
}
|
||||||
|
|
||||||
if (vartok->varId() == 0)
|
if (vartok->varId() == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -2668,12 +2648,9 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok
|
||||||
// Check that variable is allocated with malloc
|
// Check that variable is allocated with malloc
|
||||||
if (!isMalloc(vartok))
|
if (!isMalloc(vartok))
|
||||||
return;
|
return;
|
||||||
} else {
|
} else if (!_tokenizer->isC()) {
|
||||||
// If file extension is not .c then a destructor might cleanup
|
// For non-C code a destructor might cleanup members
|
||||||
// members
|
return;
|
||||||
const std::string &fname = _tokenizer->getFiles()->at(0);
|
|
||||||
if (fname.find(".c") != fname.size() - 2U)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check struct..
|
// Check struct..
|
||||||
|
|
|
@ -140,10 +140,10 @@ void CheckStl::iterators()
|
||||||
dereferenceErasedError(tok2, tok2->strAt(1));
|
dereferenceErasedError(tok2, tok2->strAt(1));
|
||||||
tok2 = tok2->next();
|
tok2 = tok2->next();
|
||||||
} else if (!validIterator && Token::Match(tok2, "%varid% . %var%", iteratorId)) {
|
} else if (!validIterator && Token::Match(tok2, "%varid% . %var%", iteratorId)) {
|
||||||
dereferenceErasedError(tok2, tok2->strAt(0));
|
dereferenceErasedError(tok2, tok2->str());
|
||||||
tok2 = tok2->tokAt(2);
|
tok2 = tok2->tokAt(2);
|
||||||
} else if (Token::Match(tok2, "%var% . erase ( * %varid%", iteratorId) && tok2->varId() == containerId) {
|
} else if (Token::Match(tok2, "%var% . erase ( * %varid%", iteratorId) && tok2->varId() == containerId) {
|
||||||
// eraseByValueError(tok2, tok2->strAt(0), tok2->strAt(5));
|
// eraseByValueError(tok2, tok2->str(), tok2->strAt(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
// bailout handling. Assume that the iterator becomes valid if we see return/break.
|
// bailout handling. Assume that the iterator becomes valid if we see return/break.
|
||||||
|
@ -605,7 +605,7 @@ void CheckStl::pushback()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pushbackTok)
|
if (pushbackTok)
|
||||||
invalidIteratorError(pushbackTok, pushbackTok->str(), tok2->strAt(0));
|
invalidIteratorError(pushbackTok, pushbackTok->str(), tok2->str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assigning iterator..
|
// Assigning iterator..
|
||||||
|
|
Loading…
Reference in New Issue