Refactoring: Removed CheckNullPointer::nullPointerByCheckAndDeRef and implemented needed analysis in ValueFlow instead.

This commit is contained in:
Daniel Marjamäki 2014-06-22 10:02:14 +02:00
parent 8d796519d9
commit f78cbda2db
4 changed files with 30 additions and 233 deletions

View File

@ -560,218 +560,12 @@ void CheckNullPointer::nullPointerByDeRefAndChec()
} }
} }
void CheckNullPointer::nullPointerByCheckAndDeRef()
{
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
// Check if pointer is NULL and then dereference it..
for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
if (i->type != Scope::eIf && i->type != Scope::eElseIf && i->type != Scope::eWhile)
continue;
if (!i->classDef || i->classDef->isExpandedMacro())
continue;
const Token* const tok = i->type != Scope::eElseIf ? i->classDef->next() : i->classDef->tokAt(2);
// TODO: investigate false negatives:
// - handle "while"?
// - if there are logical operators
// - if (x) { } else { ... }
// If the if-body ends with a unknown macro then bailout
if (Token::Match(i->classEnd->tokAt(-3), "[;{}] %var% ;") && i->classEnd->tokAt(-2)->isUpperCaseName())
continue;
// vartok : token for the variable
const Token *vartok = nullptr;
const Token *checkConditionStart = nullptr;
if (Token::Match(tok, "( ! %var% )|&&")) {
vartok = tok->tokAt(2);
checkConditionStart = vartok->next();
} else if (Token::Match(tok, "( %var% )|&&")) {
vartok = tok->next();
} else if (Token::Match(tok, "( ! ( %var% =")) {
vartok = tok->tokAt(3);
if (Token::simpleMatch(tok->linkAt(2), ") &&"))
checkConditionStart = tok->linkAt(2);
} else
continue;
// Check if variable is a pointer
const Variable *var = vartok->variable();
if (!var || !var->isPointer())
continue;
// variable id for pointer
const unsigned int varid(vartok->varId());
const Scope* declScope = &*i;
while (declScope->nestedIn && var->scope() != declScope && declScope->type != Scope::eFunction)
declScope = declScope->nestedIn;
if (Token::Match(vartok->next(), "&& ( %varid% =", varid))
continue;
// Name and line of the pointer
const std::string &pointerName = vartok->str();
// Check the condition (eg. ( !x && x->i )
if (checkConditionStart) {
const Token * const conditionEnd = tok->link();
for (const Token *tok2 = checkConditionStart; tok2 != conditionEnd; tok2 = tok2->next()) {
// If we hit a || operator, abort
if (tok2->str() == "||")
break;
// Pointer is used
bool unknown = _settings->inconclusive;
if (tok2->varId() == varid && (isPointerDeRef(tok2, unknown) || unknown)) {
nullPointerError(tok2, pointerName, vartok, unknown);
break;
}
}
}
// start token = inside the if-body
const Token *tok1 = i->classStart;
if (Token::Match(tok, "( %var% )|&&")) {
// start token = first token after the if/while body
tok1 = i->classEnd->next();
if (!tok1)
continue;
}
int indentlevel = 0;
// Set to true if we would normally bail out the check.
bool inconclusive = false;
// Count { and } for tok2
for (const Token *tok2 = tok1; tok2 != declScope->classEnd; tok2 = tok2->next()) {
if (tok2->str() == "{")
++indentlevel;
else if (tok2->str() == "}") {
if (indentlevel == 0) {
if (_settings->inconclusive)
inconclusive = true;
else
break;
}
--indentlevel;
// calling exit function?
bool unknown = false;
if (_tokenizer->IsScopeNoReturn(tok2, &unknown)) {
if (_settings->inconclusive && unknown)
inconclusive = true;
else
break;
}
if (indentlevel <= 0) {
// skip all "else" blocks because they are not executed in this execution path
while (Token::simpleMatch(tok2, "} else if ("))
tok2 = tok2->linkAt(3)->linkAt(1);
if (Token::simpleMatch(tok2, "} else {"))
tok2 = tok2->linkAt(2);
}
}
if (tok2->str() == "return" || tok2->str() == "throw") {
bool unknown = _settings->inconclusive;
for (; tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
if (tok2->varId() == varid) {
if (CheckNullPointer::isPointerDeRef(tok2, unknown))
nullPointerError(tok2, pointerName, vartok, inconclusive);
else if (unknown)
nullPointerError(tok2, pointerName, vartok, true);
if (Token::Match(tok2, "%var% %oror%|&&|?"))
break;
}
}
break;
}
// Bailout for "if".
if (tok2->str() == "if") {
if (_settings->inconclusive)
inconclusive = true;
else
break;
}
if (Token::Match(tok2, "goto|continue|break|switch|for"))
break;
// parameters to sizeof are not dereferenced
if (Token::Match(tok2, "decltype|sizeof|typeof")) {
if (tok2->strAt(1) != "(")
tok2 = tok2->next();
else
tok2 = tok2->next()->link();
continue;
}
// function call, check if pointer is dereferenced
if (Token::Match(tok2, "%var% (") && !Token::Match(tok2, "if|while")) {
std::list<const Token *> vars;
parseFunctionCall(*tok2, vars, &_settings->library, 0);
for (std::list<const Token *>::const_iterator it = vars.begin(); it != vars.end(); ++it) {
if (Token::Match(*it, "%varid% [,)]", varid)) {
nullPointerError(*it, pointerName, vartok, inconclusive);
break;
}
}
}
// calling unknown function (abort/init)..
else if (Token::simpleMatch(tok2, ") ;") &&
(Token::Match(tok2->link()->tokAt(-2), "[;{}.] %var% (") ||
Token::Match(tok2->link()->tokAt(-5), "[;{}] ( * %var% ) ("))) {
// noreturn function?
bool unknown = false;
if (_tokenizer->IsScopeNoReturn(tok2->tokAt(2), &unknown)) {
if (!unknown || !_settings->inconclusive) {
break;
}
inconclusive = _settings->inconclusive;
}
// init function (global variables)
if (!(var->isLocal() || var->isArgument()))
break;
}
if (tok2->varId() == varid) {
// unknown: this is set to true by isPointerDeRef if
// the function fails to determine if there
// is a dereference or not
bool unknown = _settings->inconclusive;
if (Token::Match(tok2->previous(), "[;{}=] %var% = 0 ;"))
;
else if (CheckNullPointer::isPointerDeRef(tok2, unknown))
nullPointerError(tok2, pointerName, vartok, inconclusive);
else if (unknown && _settings->inconclusive)
nullPointerError(tok2, pointerName, vartok, true);
else
break;
}
}
}
}
void CheckNullPointer::nullPointer() void CheckNullPointer::nullPointer()
{ {
nullPointerLinkedList(); nullPointerLinkedList();
if (_settings->isEnabled("warning")) { if (_settings->isEnabled("warning")) {
nullPointerByDeRefAndChec(); nullPointerByDeRefAndChec();
nullPointerByCheckAndDeRef();
nullPointerDefaultArgument(); nullPointerDefaultArgument();
} }
} }

View File

@ -82,12 +82,6 @@ public:
/** @brief possible null pointer dereference */ /** @brief possible null pointer dereference */
void nullPointer(); void nullPointer();
/**
* @brief Does one part of the check for nullPointer().
* Checking if pointer is NULL and then dereferencing it..
*/
void nullPointerByCheckAndDeRef();
/** @brief dereferencing null constant (after Tokenizer::simplifyKnownVariables) */ /** @brief dereferencing null constant (after Tokenizer::simplifyKnownVariables) */
void nullConstantDereference(); void nullConstantDereference();

View File

@ -868,9 +868,22 @@ static void valueFlowAfterCondition(TokenList *tokenlist, ErrorLogger *errorLogg
if (ok && !scopeEnd.empty() && Token::simpleMatch(top->link(), ") {")) { if (ok && !scopeEnd.empty() && Token::simpleMatch(top->link(), ") {")) {
Token *after = top->link()->linkAt(1); Token *after = top->link()->linkAt(1);
if (Token::simpleMatch(after->tokAt(-2), ") ; }")) { if (Token::simpleMatch(after->tokAt(-2), ") ; }")) {
if (settings->debugwarnings) const Token *funcname = after->linkAt(-2)->previous();
bailout(tokenlist, errorLogger, after, "possible noreturn scope"); const Token *start = funcname;
continue; if (funcname && Token::Match(funcname->tokAt(-3),"( * %var% )")) {
funcname = funcname->previous();
start = funcname->tokAt(-3);
} else {
while (Token::Match(start, "%var%|.|::"))
start = start->previous();
}
if (Token::Match(start,"[;{}]") && Token::Match(funcname, "%var% )| (")) {
if (!settings->library.isnotnoreturn(funcname->str())) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, after, "possible noreturn scope");
continue;
}
}
} }
if (Token::simpleMatch(after, "} else {")) { if (Token::simpleMatch(after, "} else {")) {
after = after->linkAt(2); after = after->linkAt(2);

View File

@ -207,20 +207,13 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// dereference in outer scope.. // dereference in outer scope..
{ check("void foo(int x, const Token *tok) {\n"
const char code[] = "void foo(int x, const Token *tok) {\n" " if (x == 123) {\n"
" if (x == 123) {\n" " while (tok) tok = tok->next();\n"
" while (tok) tok = tok->next();\n" " }\n"
" }\n" " tok->str();\n"
" tok->str();\n" "}\n");
"}\n"; TODO_ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning, inconclusive) Possible null pointer dereference: tok - otherwise it is redundant to check it against null.\n", "", errout.str());
check(code, false);
ASSERT_EQUALS("", errout.str());
check(code, true);
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning, inconclusive) Possible null pointer dereference: tok - otherwise it is redundant to check it against null.\n", errout.str());
}
check("int foo(const Token *tok)\n" check("int foo(const Token *tok)\n"
"{\n" "{\n"
@ -2109,10 +2102,13 @@ private:
" std::cout << abc << p;\n" " std::cout << abc << p;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n"
"[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n"
"[test.cpp:5] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:5] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n"
"[test.cpp:6] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); "[test.cpp:6] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n",
"[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n"
"[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n",
errout.str());
check("void f() {\n" check("void f() {\n"
" void* p1 = 0;\n" " void* p1 = 0;\n"