Improve leak detections in if-statements. This is done by checking for leaks every time a scope is left. This allows cppcheck to catch more memory leaks, as well as improve some error messages which now contain the line where the variable goes out of scope, instead of the end of the function.
This commit is contained in:
parent
ebf54ac53e
commit
c3eb37972d
|
@ -195,22 +195,6 @@ void CheckLeakAutoVar::check()
|
||||||
VarInfo varInfo;
|
VarInfo varInfo;
|
||||||
|
|
||||||
checkScope(scope->bodyStart, &varInfo, notzero, 0);
|
checkScope(scope->bodyStart, &varInfo, notzero, 0);
|
||||||
|
|
||||||
varInfo.conditionalAlloc.clear();
|
|
||||||
|
|
||||||
// Clear reference arguments from varInfo..
|
|
||||||
std::map<int, VarInfo::AllocInfo>::iterator it = varInfo.alloctype.begin();
|
|
||||||
while (it != varInfo.alloctype.end()) {
|
|
||||||
const Variable *var = symbolDatabase->getVariableFromVarId(it->first);
|
|
||||||
if (!var ||
|
|
||||||
(var->isArgument() && var->isReference()) ||
|
|
||||||
(!var->isArgument() && !var->isLocal()))
|
|
||||||
varInfo.alloctype.erase(it++);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret(scope->bodyEnd, varInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -741,6 +725,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
changeAllocStatus(varInfo, allocation, vtok, vtok);
|
changeAllocStatus(varInfo, allocation, vtok, vtok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ret(endToken, *varInfo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -973,15 +958,16 @@ void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
|
void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope)
|
||||||
{
|
{
|
||||||
const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
|
const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
|
||||||
const std::map<int, std::string> &possibleUsage = varInfo.possibleUsage;
|
const std::map<int, std::string> &possibleUsage = varInfo.possibleUsage;
|
||||||
|
std::vector<int> toRemove;
|
||||||
|
|
||||||
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||||
for (std::map<int, VarInfo::AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
|
for (std::map<int, VarInfo::AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
|
||||||
// don't warn if variable is conditionally allocated
|
// don't warn if variable is conditionally allocated, unless it leaves the scope
|
||||||
if (!it->second.managed() && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
|
if (!isEndOfScope && !it->second.managed() && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// don't warn if there is a reference of the variable
|
// don't warn if there is a reference of the variable
|
||||||
|
@ -991,6 +977,9 @@ void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
|
||||||
const int varid = it->first;
|
const int varid = it->first;
|
||||||
const Variable *var = symbolDatabase->getVariableFromVarId(varid);
|
const Variable *var = symbolDatabase->getVariableFromVarId(varid);
|
||||||
if (var) {
|
if (var) {
|
||||||
|
// don't warn if we leave an inner scope
|
||||||
|
if (isEndOfScope && var->scope() && tok != var->scope()->bodyEnd)
|
||||||
|
continue;
|
||||||
bool used = false;
|
bool used = false;
|
||||||
for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
|
for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
|
||||||
if (tok2->str() == ";")
|
if (tok2->str() == ";")
|
||||||
|
@ -1028,6 +1017,9 @@ void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
|
||||||
configurationInfo(tok, use->second);
|
configurationInfo(tok, use->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
toRemove.push_back(varid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int varId : toRemove)
|
||||||
|
varInfo.erase(varId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,7 @@ private:
|
||||||
void changeAllocStatusIfRealloc(std::map<int, VarInfo::AllocInfo> &alloctype, const Token *fTok, const Token *retTok);
|
void changeAllocStatusIfRealloc(std::map<int, VarInfo::AllocInfo> &alloctype, const Token *fTok, const Token *retTok);
|
||||||
|
|
||||||
/** return. either "return" or end of variable scope is seen */
|
/** return. either "return" or end of variable scope is seen */
|
||||||
void ret(const Token *tok, const VarInfo &varInfo);
|
void ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope = false);
|
||||||
|
|
||||||
/** if variable is allocated then there is a leak */
|
/** if variable is allocated then there is a leak */
|
||||||
void leakIfAllocated(const Token *vartok, const VarInfo &varInfo);
|
void leakIfAllocated(const Token *vartok, const VarInfo &varInfo);
|
||||||
|
|
|
@ -145,6 +145,8 @@ private:
|
||||||
TEST_CASE(ifelse17); // if (!!(!p))
|
TEST_CASE(ifelse17); // if (!!(!p))
|
||||||
TEST_CASE(ifelse18);
|
TEST_CASE(ifelse18);
|
||||||
TEST_CASE(ifelse19);
|
TEST_CASE(ifelse19);
|
||||||
|
TEST_CASE(ifelse20); // #10182
|
||||||
|
TEST_CASE(ifelse21);
|
||||||
|
|
||||||
// switch
|
// switch
|
||||||
TEST_CASE(switch1);
|
TEST_CASE(switch1);
|
||||||
|
@ -1611,6 +1613,39 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ifelse20() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" if (x > 0)\n"
|
||||||
|
" void * p1 = malloc(5);\n"
|
||||||
|
" else\n"
|
||||||
|
" void * p2 = malloc(2);\n"
|
||||||
|
" return;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n"
|
||||||
|
"[test.c:5]: (error) Memory leak: p2\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" if (x > 0)\n"
|
||||||
|
" void * p1 = malloc(5);\n"
|
||||||
|
" else\n"
|
||||||
|
" void * p2 = malloc(2);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n"
|
||||||
|
"[test.c:5]: (error) Memory leak: p2\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ifelse21() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" if (y) {\n"
|
||||||
|
" void * p;\n"
|
||||||
|
" if (x > 0)\n"
|
||||||
|
" p = malloc(5);\n"
|
||||||
|
" }\n"
|
||||||
|
" return;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:6]: (error) Memory leak: p\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void switch1() {
|
void switch1() {
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
" char *p = 0;\n"
|
" char *p = 0;\n"
|
||||||
|
|
Loading…
Reference in New Issue