Refactorizations in checkmemoryleak.cpp:
- Use symbolDatabase more often to increase performance and accuracy. - Replaced indendation counter - Replaced custom stringify implementation Benchmark results (sqlite checking): 4% complete, 7% on "Memory leaks (function variables)", 9% on "Memory leaks (address not taken)" and 82% on "Memory leaks (struct members)"
This commit is contained in:
parent
f01c244212
commit
04704ac5fa
|
@ -492,63 +492,42 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Token *tok,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char *CheckMemoryLeak::functionArgAlloc(const Token *tok, unsigned int targetpar, AllocType &allocType) const
|
const char *CheckMemoryLeak::functionArgAlloc(const Function *func, unsigned int targetpar, AllocType &allocType) const
|
||||||
{
|
{
|
||||||
// Find the varid of targetpar, then locate the start of the function..
|
|
||||||
unsigned int varid = 0;
|
|
||||||
allocType = No;
|
allocType = No;
|
||||||
|
|
||||||
// Locate start of arguments
|
if (!func || !func->start)
|
||||||
tok = Token::findsimplematch(tok, "(");
|
|
||||||
if (!tok)
|
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
// Is this the start of a function?
|
std::list<Variable>::const_iterator arg = func->argumentList.begin();
|
||||||
const Token* const fstart = tok->link();
|
for (; arg != func->argumentList.end(); ++arg) {
|
||||||
if (!Token::Match(fstart, ") const| {"))
|
if (arg->index() == targetpar-1)
|
||||||
return "";
|
|
||||||
|
|
||||||
// Locate targetpar
|
|
||||||
tok = tok->next();
|
|
||||||
for (unsigned int par = 1; par < targetpar; par++) {
|
|
||||||
tok = tok->nextArgument();
|
|
||||||
if (tok == 0)
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
while (tok) {
|
|
||||||
if (tok->str() == "(")
|
|
||||||
tok = tok->link();
|
|
||||||
else if (tok->str() == ",")
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (Token::Match(tok, "%type% * * %var%")) {
|
|
||||||
varid = tok->tokAt(3)->varId();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (arg == func->argumentList.end())
|
||||||
tok = tok->next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (varid == 0)
|
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
// continue in function body
|
// Is **
|
||||||
tok = fstart;
|
if (!arg->isPointer())
|
||||||
while (tok->str() != "{")
|
return "";
|
||||||
tok = tok->next();
|
const Token* tok = arg->typeEndToken();
|
||||||
|
if (tok->str() == "const")
|
||||||
|
tok = tok->previous();
|
||||||
|
tok = tok->previous();
|
||||||
|
if (tok->str() != "*")
|
||||||
|
return "";
|
||||||
|
|
||||||
// Check if pointer is allocated.
|
// Check if pointer is allocated.
|
||||||
int realloc = 0;
|
int realloc = 0;
|
||||||
for (const Token* const end = tok->link(); tok && tok != end; tok = tok->next()) {
|
for (const Token* tok = func->start; tok && tok != func->start->link(); tok = tok->next()) {
|
||||||
if (tok->varId() == varid) {
|
if (tok->varId() == arg->varId()) {
|
||||||
if (Token::Match(tok->tokAt(-3), "free ( * %varid% )", varid)) {
|
if (Token::Match(tok->tokAt(-3), "free ( * %var% )")) {
|
||||||
realloc = 1;
|
realloc = 1;
|
||||||
allocType = No;
|
allocType = No;
|
||||||
} else if (Token::Match(tok->previous(), "* %varid% =", varid)) {
|
} else if (Token::Match(tok->previous(), "* %var% =")) {
|
||||||
allocType = getAllocationType(tok->tokAt(2), varid);
|
allocType = getAllocationType(tok->tokAt(2), arg->varId());
|
||||||
if (allocType == No) {
|
if (allocType == No) {
|
||||||
allocType = getReallocationType(tok->tokAt(2), varid);
|
allocType = getReallocationType(tok->tokAt(2), arg->varId());
|
||||||
}
|
}
|
||||||
if (allocType != No) {
|
if (allocType != No) {
|
||||||
if (realloc)
|
if (realloc)
|
||||||
|
@ -652,7 +631,7 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
|
||||||
else if (tok2->strAt(1) == "=")
|
else if (tok2->strAt(1) == "=")
|
||||||
return "assign";
|
return "assign";
|
||||||
else
|
else
|
||||||
return"use_";
|
return "use_";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,7 +647,7 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
|
||||||
if (callstack.size() > 2)
|
if (callstack.size() > 2)
|
||||||
return "dealloc_";
|
return "dealloc_";
|
||||||
|
|
||||||
const std::string funcname(tok->str());
|
const std::string& funcname(tok->str());
|
||||||
for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) {
|
for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) {
|
||||||
if ((*it) && (*it)->str() == funcname)
|
if ((*it) && (*it)->str() == funcname)
|
||||||
return "recursive";
|
return "recursive";
|
||||||
|
@ -705,7 +684,7 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
|
||||||
if (notnoreturn.find(funcname) != notnoreturn.end())
|
if (notnoreturn.find(funcname) != notnoreturn.end())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return (tok->previous()->str() != "=") ? "callfunc" : NULL;
|
return "callfunc";
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int par = 0;
|
unsigned int par = 0;
|
||||||
|
@ -731,24 +710,17 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
|
||||||
if (numpar != countParameters(ftok))
|
if (numpar != countParameters(ftok))
|
||||||
return "recursive";
|
return "recursive";
|
||||||
|
|
||||||
const char *parname = Tokenizer::getParameterName(ftok, par);
|
const Function* function = _tokenizer->getSymbolDatabase()->findFunctionByToken(ftok);
|
||||||
if (! parname)
|
if (!function)
|
||||||
return "recursive";
|
return "recursive";
|
||||||
unsigned int parameterVarid = 0;
|
|
||||||
{
|
const Variable* param = function->getArgumentVar(par-1);
|
||||||
const Token *partok = Token::findmatch(ftok, parname);
|
if (!param || !param->nameToken())
|
||||||
if (partok)
|
return "use";
|
||||||
parameterVarid = partok->varId();
|
if (!function->start)
|
||||||
}
|
return "use";
|
||||||
if (parameterVarid == 0)
|
Token *func = getcode(function->start->next(), callstack, param->varId(), alloctype, dealloctype, false, sz);
|
||||||
return "recursive";
|
//simplifycode(func);
|
||||||
// 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;
|
const Token *func_ = func;
|
||||||
while (func_ && func_->str() == ";")
|
while (func_ && func_->str() == ";")
|
||||||
func_ = func_->next();
|
func_ = func_->next();
|
||||||
|
@ -767,8 +739,11 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
|
||||||
}
|
}
|
||||||
if (varid > 0 && Token::Match(tok, "& %varid% [,()]", varid)) {
|
if (varid > 0 && Token::Match(tok, "& %varid% [,()]", varid)) {
|
||||||
const Token *ftok = _tokenizer->getFunctionTokenByName(funcname.c_str());
|
const Token *ftok = _tokenizer->getFunctionTokenByName(funcname.c_str());
|
||||||
|
if (ftok == 0)
|
||||||
|
continue;
|
||||||
|
const Function* func = _tokenizer->getSymbolDatabase()->findFunctionByToken(ftok);
|
||||||
AllocType a;
|
AllocType a;
|
||||||
const char *ret = functionArgAlloc(ftok, par, a);
|
const char *ret = functionArgAlloc(func, par, a);
|
||||||
|
|
||||||
if (a != No) {
|
if (a != No) {
|
||||||
if (alloctype == No)
|
if (alloctype == No)
|
||||||
|
@ -1079,31 +1054,21 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
|
||||||
} else {
|
} else {
|
||||||
// Check if the condition depends on var or extravar somehow..
|
// Check if the condition depends on var or extravar somehow..
|
||||||
bool dep = false;
|
bool dep = false;
|
||||||
int innerParlevel = 0;
|
const Token* const end = tok->linkAt(1);
|
||||||
for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
|
for (const Token *tok2 = tok->next(); tok2 != end; tok2 = tok2->next()) {
|
||||||
if (tok2->str() == "(")
|
if (Token::Match(tok2, "close|pclose|fclose|closedir ( %varid% )", varid)) {
|
||||||
++innerParlevel;
|
|
||||||
else if (tok2->str() == ")") {
|
|
||||||
--innerParlevel;
|
|
||||||
if (innerParlevel <= 0)
|
|
||||||
break;
|
|
||||||
} else if (Token::Match(tok2, "close|pclose|fclose|closedir ( %varid% )", varid)) {
|
|
||||||
addtoken(&rettail, tok, "dealloc");
|
addtoken(&rettail, tok, "dealloc");
|
||||||
addtoken(&rettail, tok, ";");
|
addtoken(&rettail, tok, ";");
|
||||||
dep = true;
|
dep = true;
|
||||||
break;
|
break;
|
||||||
} else if (alloctype == Fd && Token::Match(tok2, "%varid% !=|>=", varid)) {
|
} else if (alloctype == Fd && Token::Match(tok2, "%varid% !=|>=", varid)) {
|
||||||
dep = true;
|
dep = true;
|
||||||
} else if (innerParlevel > 0 && Token::Match(tok2, "! %varid%", varid)) {
|
} else if (Token::Match(tok2, "! %varid%", varid)) {
|
||||||
dep = true;
|
dep = true;
|
||||||
} else if (innerParlevel > 0 && Token::Match(tok2, "%var% (") && !test_white_list(tok2->str())) {
|
} else if (Token::Match(tok2, "%var% (") && !test_white_list(tok2->str())) {
|
||||||
bool use = false;
|
bool use = false;
|
||||||
for (const Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->next()) {
|
for (const Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->nextArgument()) {
|
||||||
if (tok3->str() == "(")
|
if (Token::Match(tok3->previous(), "(|, &| %varid% ,|)", varid)) {
|
||||||
tok3 = tok3->link();
|
|
||||||
else if (tok3->str() == ")")
|
|
||||||
break;
|
|
||||||
else if (Token::Match(tok3->previous(), "(|, &| %varid% ,|)", varid)) {
|
|
||||||
use = true;
|
use = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2159,8 +2124,7 @@ void CheckMemoryLeakInFunction::checkScope(const Token *Tok1, const std::string
|
||||||
if (! noerr) {
|
if (! noerr) {
|
||||||
std::ostringstream errmsg;
|
std::ostringstream errmsg;
|
||||||
errmsg << "inconclusive leak of " << varname << ": ";
|
errmsg << "inconclusive leak of " << varname << ": ";
|
||||||
for (const Token *tok2 = tok; tok2; tok2 = tok2->next())
|
errmsg << tok->stringifyList(false, false, false, false, false, 0, 0);
|
||||||
errmsg << " " << tok2->str();
|
|
||||||
reportError(first, Severity::debug, "debug", errmsg.str());
|
reportError(first, Severity::debug, "debug", errmsg.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2201,8 +2165,8 @@ void CheckMemoryLeakInFunction::checkReallocUsage()
|
||||||
tok->varId() == tok->tokAt(4)->varId() &&
|
tok->varId() == tok->tokAt(4)->varId() &&
|
||||||
isNoArgument(symbolDatabase, tok->varId())) {
|
isNoArgument(symbolDatabase, tok->varId())) {
|
||||||
// Check that another copy of the pointer wasn't saved earlier in the function
|
// Check that another copy of the pointer wasn't saved earlier in the function
|
||||||
if (Token::findmatch(scope->classStart, "%var% = %varid% ;", tok->varId()) ||
|
if (Token::findmatch(scope->classStart, "%var% = %varid% ;", tok, tok->varId()) ||
|
||||||
Token::findmatch(scope->classStart, "[{};] %varid% = %var% [;=]", tok->varId()))
|
Token::findmatch(scope->classStart, "[{};] %varid% = %var% [;=]", tok, tok->varId()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Token* tokEndRealloc = tok->linkAt(3);
|
const Token* tokEndRealloc = tok->linkAt(3);
|
||||||
|
@ -2220,8 +2184,8 @@ void CheckMemoryLeakInFunction::checkReallocUsage()
|
||||||
tok->next()->varId() == tok->tokAt(6)->varId()) &&
|
tok->next()->varId() == tok->tokAt(6)->varId()) &&
|
||||||
isNoArgument(symbolDatabase, tok->next()->varId())) {
|
isNoArgument(symbolDatabase, tok->next()->varId())) {
|
||||||
// Check that another copy of the pointer wasn't saved earlier in the function
|
// Check that another copy of the pointer wasn't saved earlier in the function
|
||||||
if (Token::findmatch(scope->classStart, "%var% = * %varid% ;", tok->next()->varId()) ||
|
if (Token::findmatch(scope->classStart, "%var% = * %varid% ;", tok, tok->next()->varId()) ||
|
||||||
Token::findmatch(scope->classStart, "[{};] * %varid% = %var% [;=]", tok->next()->varId()))
|
Token::findmatch(scope->classStart, "[{};] * %varid% = %var% [;=]", tok, tok->next()->varId()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Token* tokEndRealloc = tok->linkAt(4);
|
const Token* tokEndRealloc = tok->linkAt(4);
|
||||||
|
@ -2248,62 +2212,12 @@ void CheckMemoryLeakInFunction::checkReallocUsage()
|
||||||
// Checks for memory leaks inside function..
|
// Checks for memory leaks inside function..
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void CheckMemoryLeakInFunction::parseFunctionScope(const Token *body, const Token *arg, const bool classmember)
|
static bool isInMemberFunc(const Scope* scope)
|
||||||
{
|
{
|
||||||
// Check locking/unlocking of global resources..
|
while (scope->nestedIn && !scope->functionOf)
|
||||||
checkScope(body->next(), "", 0, classmember, 1);
|
scope = scope->nestedIn;
|
||||||
|
|
||||||
// Locate parameters and check their usage..
|
return (scope->functionOf != 0);
|
||||||
for (const Token *tok2 = arg->next(); tok2; tok2 = tok2->nextArgument()) {
|
|
||||||
if (Token::Match(tok2, "%type% * %var% [,)]") && tok2->isStandardType()) {
|
|
||||||
const std::string varname(tok2->strAt(2));
|
|
||||||
const unsigned int varid = tok2->tokAt(2)->varId();
|
|
||||||
const unsigned int sz = _tokenizer->sizeOfType(tok2);
|
|
||||||
checkScope(body->next(), varname, varid, classmember, sz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locate variable declarations and check their usage..
|
|
||||||
for (const Token* tok = body; tok && tok != body->link(); tok = tok->next()) {
|
|
||||||
// Skip these weird blocks... "( { ... } )"
|
|
||||||
if (Token::simpleMatch(tok, "( {")) {
|
|
||||||
tok = tok->link();
|
|
||||||
if (!tok)
|
|
||||||
break;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Token::Match(tok, "[{};:] %type%"))
|
|
||||||
continue;
|
|
||||||
tok = tok->next();
|
|
||||||
|
|
||||||
// Don't check static/extern variables
|
|
||||||
if (Token::Match(tok, "static|extern"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// return/else is not part of a variable declaration..
|
|
||||||
if (Token::Match(tok, "return|else"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
unsigned int sz = _tokenizer->sizeOfType(tok);
|
|
||||||
if (sz < 1)
|
|
||||||
sz = 1;
|
|
||||||
|
|
||||||
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);
|
|
||||||
checkScope(tok, vartok->str(), vartok->varId(), classmember, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (Token::Match(tok, "int %var% [;=]")) {
|
|
||||||
const Token *vartok = tok->next();
|
|
||||||
checkScope(tok, vartok->str(), vartok->varId(), classmember, sz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckMemoryLeakInFunction::check()
|
void CheckMemoryLeakInFunction::check()
|
||||||
|
@ -2311,15 +2225,32 @@ void CheckMemoryLeakInFunction::check()
|
||||||
// fill the "noreturn"
|
// fill the "noreturn"
|
||||||
parse_noreturn();
|
parse_noreturn();
|
||||||
|
|
||||||
|
// Check locking/unlocking of global resources..
|
||||||
for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
|
for (std::list<Scope>::const_iterator 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 *body = scope->classStart;
|
checkScope(scope->classStart->next(), "", 0, scope->functionOf != NULL, 1);
|
||||||
const Token *arg = scope->classDef->next();
|
}
|
||||||
bool classmember = scope->functionOf != NULL;
|
|
||||||
parseFunctionScope(body, arg, classmember);
|
// Check variables..
|
||||||
|
for (unsigned int i = 0; i < symbolDatabase->getVariableListSize(); i++) {
|
||||||
|
const Variable* var = symbolDatabase->getVariableFromVarId(i);
|
||||||
|
if (!var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || !var->scope())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!var->isPointer() && var->typeStartToken()->str() != "int")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
unsigned int sz = _tokenizer->sizeOfType(var->typeStartToken());
|
||||||
|
if (sz < 1)
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
if (var->isArgument())
|
||||||
|
checkScope(var->scope()->classStart->next(), var->name(), i, isInMemberFunc(var->scope()), sz);
|
||||||
|
else
|
||||||
|
checkScope(var->nameToken(), var->name(), i, isInMemberFunc(var->scope()), sz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -2558,38 +2489,23 @@ void CheckMemoryLeakInClass::publicAllocationError(const Token *tok, const std::
|
||||||
|
|
||||||
void CheckMemoryLeakStructMember::check()
|
void CheckMemoryLeakStructMember::check()
|
||||||
{
|
{
|
||||||
unsigned int indentlevel1 = 0;
|
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
for (unsigned int i = 0; i < symbolDatabase->getVariableListSize(); i++) {
|
||||||
if (tok->str() == "{")
|
const Variable* var = symbolDatabase->getVariableFromVarId(i);
|
||||||
++indentlevel1;
|
if (!var || !var->isLocal() || var->isStatic())
|
||||||
else if (tok->str() == "}")
|
|
||||||
--indentlevel1;
|
|
||||||
|
|
||||||
if (indentlevel1 == 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
if (var->typeEndToken()->isStandardType())
|
||||||
// check struct variables..
|
continue;
|
||||||
if (Token::Match(tok, "struct|;|{|} %type% * %var% [=;]")) {
|
checkStructVariable(var);
|
||||||
checkStructVariable(tok->tokAt(3));
|
|
||||||
} else if (Token::Match(tok, "struct|;|{|} %type% %var% ;")) {
|
|
||||||
checkStructVariable(tok->tokAt(2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckMemoryLeakStructMember::isMalloc(const Token *vartok)
|
bool CheckMemoryLeakStructMember::isMalloc(const Variable *variable)
|
||||||
{
|
{
|
||||||
const unsigned int varid(vartok->varId());
|
const unsigned int varid(variable->varId());
|
||||||
bool alloc = false;
|
bool alloc = false;
|
||||||
unsigned int indentlevel2 = 0;
|
for (const Token *tok2 = variable->nameToken(); tok2 != variable->scope()->classEnd; tok2 = tok2->next()) {
|
||||||
for (const Token *tok2 = vartok; tok2; tok2 = tok2->next()) {
|
if (Token::Match(tok2, "= %varid% [;=]", varid)) {
|
||||||
if (tok2->str() == "{")
|
|
||||||
++indentlevel2;
|
|
||||||
else if (tok2->str() == "}") {
|
|
||||||
if (indentlevel2 == 0)
|
|
||||||
break;
|
|
||||||
--indentlevel2;
|
|
||||||
} else if (Token::Match(tok2, "= %varid% [;=]", varid)) {
|
|
||||||
return false;
|
return false;
|
||||||
} else if (Token::Match(tok2, "%varid% = malloc|kmalloc (", varid)) {
|
} else if (Token::Match(tok2, "%varid% = malloc|kmalloc (", varid)) {
|
||||||
alloc = true;
|
alloc = true;
|
||||||
|
@ -2599,7 +2515,7 @@ bool CheckMemoryLeakStructMember::isMalloc(const Token *vartok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok)
|
void CheckMemoryLeakStructMember::checkStructVariable(const Variable * const variable)
|
||||||
{
|
{
|
||||||
// This should be in the CheckMemoryLeak base class
|
// This should be in the CheckMemoryLeak base class
|
||||||
static std::set<std::string> ignoredFunctions;
|
static std::set<std::string> ignoredFunctions;
|
||||||
|
@ -2610,13 +2526,10 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok
|
||||||
ignoredFunctions.insert("malloc");
|
ignoredFunctions.insert("malloc");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vartok->varId() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Is struct variable a pointer?
|
// Is struct variable a pointer?
|
||||||
if (vartok->strAt(-1) == "*") {
|
if (variable->isPointer()) {
|
||||||
// Check that variable is allocated with malloc
|
// Check that variable is allocated with malloc
|
||||||
if (!isMalloc(vartok))
|
if (!isMalloc(variable))
|
||||||
return;
|
return;
|
||||||
} else if (!_tokenizer->isC()) {
|
} else if (!_tokenizer->isC()) {
|
||||||
// For non-C code a destructor might cleanup members
|
// For non-C code a destructor might cleanup members
|
||||||
|
@ -2625,7 +2538,7 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok
|
||||||
|
|
||||||
// Check struct..
|
// Check struct..
|
||||||
unsigned int indentlevel2 = 0;
|
unsigned int indentlevel2 = 0;
|
||||||
for (const Token *tok2 = vartok; tok2; tok2 = tok2->next()) {
|
for (const Token *tok2 = variable->nameToken(); tok2 != variable->scope()->classEnd; tok2 = tok2->next()) {
|
||||||
if (tok2->str() == "{")
|
if (tok2->str() == "{")
|
||||||
++indentlevel2;
|
++indentlevel2;
|
||||||
|
|
||||||
|
@ -2637,12 +2550,12 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok
|
||||||
|
|
||||||
// Unknown usage of struct
|
// Unknown usage of struct
|
||||||
/** @todo Check how the struct is used. Only bail out if necessary */
|
/** @todo Check how the struct is used. Only bail out if necessary */
|
||||||
else if (Token::Match(tok2, "[(,] %varid% [,)]", vartok->varId()))
|
else if (Token::Match(tok2, "[(,] %varid% [,)]", variable->varId()))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Struct member is allocated => check if it is also properly deallocated..
|
// Struct member is allocated => check if it is also properly deallocated..
|
||||||
else if (Token::Match(tok2->previous(), "[;{}] %varid% . %var% = malloc|strdup|kmalloc (", vartok->varId())) {
|
else if (Token::Match(tok2->previous(), "[;{}] %varid% . %var% = malloc|strdup|kmalloc (", variable->varId())) {
|
||||||
const unsigned int structid(vartok->varId());
|
const unsigned int structid(variable->varId());
|
||||||
const unsigned int structmemberid(tok2->tokAt(2)->varId());
|
const unsigned int structmemberid(tok2->tokAt(2)->varId());
|
||||||
|
|
||||||
// This struct member is allocated.. check that it is deallocated
|
// This struct member is allocated.. check that it is deallocated
|
||||||
|
@ -2653,7 +2566,7 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok
|
||||||
|
|
||||||
else if (tok3->str() == "}") {
|
else if (tok3->str() == "}") {
|
||||||
if (indentlevel3 == 0) {
|
if (indentlevel3 == 0) {
|
||||||
memoryLeak(tok3, vartok->str() + "." + tok2->strAt(2), Malloc);
|
memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
--indentlevel3;
|
--indentlevel3;
|
||||||
|
@ -2683,7 +2596,7 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok
|
||||||
|
|
||||||
// Deallocating the struct..
|
// Deallocating the struct..
|
||||||
else if (indentlevel2 == 0 && Token::Match(tok3, "free|kfree ( %varid% )", structid)) {
|
else if (indentlevel2 == 0 && Token::Match(tok3, "free|kfree ( %varid% )", structid)) {
|
||||||
memoryLeak(tok3, vartok->str() + "." + tok2->strAt(2), Malloc);
|
memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2729,7 +2642,7 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Token * const vartok
|
||||||
// Returning from function without deallocating struct member?
|
// Returning from function without deallocating struct member?
|
||||||
if (!Token::Match(tok3, "return %varid% ;", structid) &&
|
if (!Token::Match(tok3, "return %varid% ;", structid) &&
|
||||||
!Token::Match(tok3, "return & %varid% .", structid)) {
|
!Token::Match(tok3, "return & %varid% .", structid)) {
|
||||||
memoryLeak(tok3, vartok->str() + "." + tok2->strAt(2), Malloc);
|
memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
|
|
||||||
class Token;
|
class Token;
|
||||||
class Scope;
|
class Scope;
|
||||||
|
class Function;
|
||||||
|
class Variable;
|
||||||
|
|
||||||
/// @addtogroup Core
|
/// @addtogroup Core
|
||||||
/// @{
|
/// @{
|
||||||
|
@ -158,7 +160,7 @@ public:
|
||||||
AllocType functionReturnType(const Token *tok, std::list<const Token *> *callstack = NULL) const;
|
AllocType functionReturnType(const Token *tok, std::list<const Token *> *callstack = NULL) const;
|
||||||
|
|
||||||
/** Function allocates pointed-to argument (a la asprintf)? */
|
/** Function allocates pointed-to argument (a la asprintf)? */
|
||||||
const char *functionArgAlloc(const Token *tok, unsigned int targetpar, AllocType &allocType) const;
|
const char *functionArgAlloc(const Function *func, unsigned int targetpar, AllocType &allocType) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
@ -217,14 +219,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void checkReallocUsage();
|
void checkReallocUsage();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief %Check all variables in function scope
|
|
||||||
* @param tok The first '{' token of the function body
|
|
||||||
* @param tok1 The '(' token in the function declaration
|
|
||||||
* @param classmember Is this function a class member?
|
|
||||||
*/
|
|
||||||
void parseFunctionScope(const Token *tok, const Token *tok1, const bool classmember);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief %Check if there is a "!var" match inside a condition
|
* @brief %Check if there is a "!var" match inside a condition
|
||||||
* @param tok first token to match
|
* @param tok first token to match
|
||||||
|
@ -419,9 +413,9 @@ public:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** Is local variable allocated with malloc? */
|
/** Is local variable allocated with malloc? */
|
||||||
static bool isMalloc(const Token *vartok);
|
static bool isMalloc(const Variable *variable);
|
||||||
|
|
||||||
void checkStructVariable(const Token * const vartok);
|
void checkStructVariable(const Variable * const variable);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const
|
void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const
|
||||||
{ }
|
{ }
|
||||||
|
|
|
@ -2751,7 +2751,8 @@ private:
|
||||||
" return false;\n"
|
" return false;\n"
|
||||||
" return true;\n"
|
" return true;\n"
|
||||||
"}\n", false);
|
"}\n", false);
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Common realloc mistake: \'m_options\' nulled but not freed upon failure\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (error) Common realloc mistake: \'m_options\' nulled but not freed upon failure\n"
|
||||||
|
"[test.cpp:6]: (error) Memory leak: m_options\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void assign1() {
|
void assign1() {
|
||||||
|
|
Loading…
Reference in New Issue