checkleakautovar: do not miss 'throw' followed by ::
This commit is contained in:
parent
5631c765a7
commit
efe98883ab
|
@ -212,19 +212,20 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// parse statement, skip to last member
|
// parse statement, skip to last member
|
||||||
while (Token::Match(tok, "%name% ::|. %name% !!("))
|
const Token *varTok = tok;
|
||||||
tok = tok->tokAt(2);
|
while (Token::Match(varTok, "%name% ::|. %name% !!("))
|
||||||
|
varTok = varTok->tokAt(2);
|
||||||
|
|
||||||
// assignment..
|
// assignment..
|
||||||
if (Token::Match(tok, "%var% =")) {
|
if (Token::Match(varTok, "%var% =")) {
|
||||||
// taking address of another variable..
|
// taking address of another variable..
|
||||||
if (Token::Match(tok->next(), "= %var% [+;]")) {
|
if (Token::Match(varTok->next(), "= %var% [+;]")) {
|
||||||
if (tok->tokAt(2)->varId() != tok->varId()) {
|
if (varTok->tokAt(2)->varId() != varTok->varId()) {
|
||||||
// If variable points at allocated memory => error
|
// If variable points at allocated memory => error
|
||||||
leakIfAllocated(tok, *varInfo);
|
leakIfAllocated(varTok, *varInfo);
|
||||||
|
|
||||||
// no multivariable checking currently => bail out for rhs variables
|
// no multivariable checking currently => bail out for rhs variables
|
||||||
for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
|
for (const Token *tok2 = varTok; tok2; tok2 = tok2->next()) {
|
||||||
if (tok2->str() == ";") {
|
if (tok2->str() == ";") {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -237,11 +238,11 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
|
|
||||||
// is variable used in rhs?
|
// is variable used in rhs?
|
||||||
bool used_in_rhs = false;
|
bool used_in_rhs = false;
|
||||||
for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
|
for (const Token *tok2 = varTok->tokAt(2); tok2; tok2 = tok2->next()) {
|
||||||
if (tok2->str() == ";") {
|
if (tok2->str() == ";") {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (tok->varId() == tok2->varId()) {
|
if (varTok->varId() == tok2->varId()) {
|
||||||
used_in_rhs = true;
|
used_in_rhs = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -251,12 +252,12 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Variable has already been allocated => error
|
// Variable has already been allocated => error
|
||||||
if (conditionalAlloc.find(tok->varId()) == conditionalAlloc.end())
|
if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end())
|
||||||
leakIfAllocated(tok, *varInfo);
|
leakIfAllocated(varTok, *varInfo);
|
||||||
varInfo->erase(tok->varId());
|
varInfo->erase(varTok->varId());
|
||||||
|
|
||||||
// not a local variable nor argument?
|
// not a local variable nor argument?
|
||||||
const Variable *var = tok->variable();
|
const Variable *var = varTok->variable();
|
||||||
if (var && !var->isArgument() && (!var->isLocal() || var->isStatic()))
|
if (var && !var->isArgument() && (!var->isLocal() || var->isStatic()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -269,30 +270,30 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
if (!var)
|
if (!var)
|
||||||
continue;
|
continue;
|
||||||
// Possibly automatically deallocated memory
|
// Possibly automatically deallocated memory
|
||||||
if (!var->typeStartToken()->isStandardType() && Token::Match(tok, "%var% = new"))
|
if (!var->typeStartToken()->isStandardType() && Token::Match(varTok, "%var% = new"))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocation?
|
// allocation?
|
||||||
if (tok->next()->astOperand2() && Token::Match(tok->next()->astOperand2()->previous(), "%type% (")) {
|
if (varTok->next()->astOperand2() && Token::Match(varTok->next()->astOperand2()->previous(), "%type% (")) {
|
||||||
int i = _settings->library.alloc(tok->next()->astOperand2()->previous());
|
int i = _settings->library.alloc(varTok->next()->astOperand2()->previous());
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
alloctype[tok->varId()].type = i;
|
alloctype[varTok->varId()].type = i;
|
||||||
alloctype[tok->varId()].status = VarInfo::ALLOC;
|
alloctype[varTok->varId()].status = VarInfo::ALLOC;
|
||||||
}
|
}
|
||||||
} else if (_tokenizer->isCPP() && tok->strAt(2) == "new") {
|
} else if (_tokenizer->isCPP() && varTok->strAt(2) == "new") {
|
||||||
alloctype[tok->varId()].type = -1;
|
alloctype[varTok->varId()].type = -1;
|
||||||
alloctype[tok->varId()].status = VarInfo::ALLOC;
|
alloctype[varTok->varId()].status = VarInfo::ALLOC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assigning non-zero value variable. It might be used to
|
// Assigning non-zero value variable. It might be used to
|
||||||
// track the execution for a later if condition.
|
// track the execution for a later if condition.
|
||||||
if (Token::Match(tok->tokAt(2), "%num% ;") && MathLib::toLongNumber(tok->strAt(2)) != 0)
|
if (Token::Match(varTok->tokAt(2), "%num% ;") && MathLib::toLongNumber(varTok->strAt(2)) != 0)
|
||||||
notzero.insert(tok->varId());
|
notzero.insert(varTok->varId());
|
||||||
else if (Token::Match(tok->tokAt(2), "- %type% ;") && tok->tokAt(3)->isUpperCaseName())
|
else if (Token::Match(varTok->tokAt(2), "- %type% ;") && varTok->tokAt(3)->isUpperCaseName())
|
||||||
notzero.insert(tok->varId());
|
notzero.insert(varTok->varId());
|
||||||
else
|
else
|
||||||
notzero.erase(tok->varId());
|
notzero.erase(varTok->varId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// if/else
|
// if/else
|
||||||
|
|
|
@ -108,6 +108,7 @@ private:
|
||||||
|
|
||||||
// Execution reaches a 'throw'
|
// Execution reaches a 'throw'
|
||||||
TEST_CASE(throw1);
|
TEST_CASE(throw1);
|
||||||
|
TEST_CASE(throw2);
|
||||||
|
|
||||||
// Possible leak => Further configuration is needed for complete analysis
|
// Possible leak => Further configuration is needed for complete analysis
|
||||||
TEST_CASE(configuration1);
|
TEST_CASE(configuration1);
|
||||||
|
@ -1103,6 +1104,23 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void throw2() { // do not miss ::NS::Except()
|
||||||
|
check("namespace NS {\n"
|
||||||
|
" class Except {\n"
|
||||||
|
" };\n"
|
||||||
|
"}\n"
|
||||||
|
"void foo(int i)\n"
|
||||||
|
"{\n"
|
||||||
|
" int *pi = new int;\n"
|
||||||
|
" if (i == 42) {\n"
|
||||||
|
" delete pi;\n"
|
||||||
|
" throw ::NS::Except();\n"
|
||||||
|
" }\n"
|
||||||
|
" delete pi;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void configuration1() {
|
void configuration1() {
|
||||||
// Possible leak => configuration is required for complete analysis
|
// Possible leak => configuration is required for complete analysis
|
||||||
// The user should be able to "white list" and "black list" functions.
|
// The user should be able to "white list" and "black list" functions.
|
||||||
|
|
Loading…
Reference in New Issue