Fix issue 9404: False positive: Either the condition 'if(x)' is redundant or there is possible null pointer dereference: a->x (#2322)
* Fix issue 9404: False positive: Either the condition 'if(x)' is redundant or there is possible null pointer dereference: a->x * Use simpleMatch * Add a test case for the FP * Check if expression is changed * Check for no return scope * Use simpleMatch
This commit is contained in:
parent
ba217815cb
commit
c75bbbe253
|
@ -952,7 +952,7 @@ static bool isEscapedOrJump(const Token* tok, bool functionsScope)
|
||||||
return Token::Match(tok, "return|goto|throw|continue|break");
|
return Token::Match(tok, "return|goto|throw|continue|break");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isReturnScope(const Token * const endToken, const Settings * settings, bool functionScope)
|
bool isReturnScope(const Token * const endToken, const Library * library, bool functionScope)
|
||||||
{
|
{
|
||||||
if (!endToken || endToken->str() != "}")
|
if (!endToken || endToken->str() != "}")
|
||||||
return false;
|
return false;
|
||||||
|
@ -965,7 +965,7 @@ bool isReturnScope(const Token * const endToken, const Settings * settings, bool
|
||||||
|
|
||||||
if (Token::simpleMatch(prev, "}")) {
|
if (Token::simpleMatch(prev, "}")) {
|
||||||
if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {"))
|
if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {"))
|
||||||
return isReturnScope(prev, settings, functionScope) && isReturnScope(prev->link()->tokAt(-2), settings, functionScope);
|
return isReturnScope(prev, library, functionScope) && isReturnScope(prev->link()->tokAt(-2), library, functionScope);
|
||||||
if (Token::simpleMatch(prev->link()->previous(), ") {") &&
|
if (Token::simpleMatch(prev->link()->previous(), ") {") &&
|
||||||
Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") &&
|
Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") &&
|
||||||
!Token::findsimplematch(prev->link(), "break", prev)) {
|
!Token::findsimplematch(prev->link(), "break", prev)) {
|
||||||
|
@ -974,7 +974,7 @@ bool isReturnScope(const Token * const endToken, const Settings * settings, bool
|
||||||
if (isEscaped(prev->link()->astTop(), functionScope))
|
if (isEscaped(prev->link()->astTop(), functionScope))
|
||||||
return true;
|
return true;
|
||||||
if (Token::Match(prev->link()->previous(), "[;{}] {"))
|
if (Token::Match(prev->link()->previous(), "[;{}] {"))
|
||||||
return isReturnScope(prev, settings, functionScope);
|
return isReturnScope(prev, library, functionScope);
|
||||||
} else if (Token::simpleMatch(prev, ";")) {
|
} else if (Token::simpleMatch(prev, ";")) {
|
||||||
if (Token::simpleMatch(prev->previous(), ") ;") && Token::Match(prev->linkAt(-1)->tokAt(-2), "[;{}] %name% (")) {
|
if (Token::simpleMatch(prev->previous(), ") ;") && Token::Match(prev->linkAt(-1)->tokAt(-2), "[;{}] %name% (")) {
|
||||||
const Token * ftok = prev->linkAt(-1)->previous();
|
const Token * ftok = prev->linkAt(-1)->previous();
|
||||||
|
@ -984,8 +984,8 @@ bool isReturnScope(const Token * const endToken, const Settings * settings, bool
|
||||||
return true;
|
return true;
|
||||||
if (function->isAttributeNoreturn())
|
if (function->isAttributeNoreturn())
|
||||||
return true;
|
return true;
|
||||||
} else if (settings) {
|
} else if (library) {
|
||||||
if (settings->library.isnoreturn(ftok))
|
if (library->isnoreturn(ftok))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1642,6 +1642,40 @@ struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const
|
||||||
return Result(Result::Type::BAILOUT);
|
return Result(Result::Type::BAILOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mWhat == What::ValueFlow && Token::simpleMatch(tok, "if (") && Token::simpleMatch(tok->linkAt(1), ") {")) {
|
||||||
|
const Token *bodyStart = tok->linkAt(1)->next();
|
||||||
|
const Token *conditionStart = tok->next();
|
||||||
|
const Token *condTok = conditionStart->astOperand2();
|
||||||
|
if (condTok->hasKnownIntValue()) {
|
||||||
|
bool cond = condTok->values().front().intvalue;
|
||||||
|
if (cond) {
|
||||||
|
FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth);
|
||||||
|
if (result.type != Result::Type::NONE)
|
||||||
|
return result;
|
||||||
|
} else if (Token::simpleMatch(bodyStart->link(), "} else {")) {
|
||||||
|
bodyStart = bodyStart->tokAt(2);
|
||||||
|
FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth);
|
||||||
|
if (result.type != Result::Type::NONE)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tok = bodyStart->link();
|
||||||
|
if (isReturnScope(tok, &mLibrary))
|
||||||
|
return Result(Result::Type::BAILOUT);
|
||||||
|
if (Token::simpleMatch(tok, "} else {"))
|
||||||
|
tok = tok->linkAt(2);
|
||||||
|
if (!tok)
|
||||||
|
return Result(Result::Type::BAILOUT);
|
||||||
|
|
||||||
|
// Is expr changed in condition?
|
||||||
|
if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local))
|
||||||
|
return Result(Result::Type::BAILOUT);
|
||||||
|
|
||||||
|
// Is expr changed in condition body?
|
||||||
|
if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local))
|
||||||
|
return Result(Result::Type::BAILOUT);
|
||||||
|
}
|
||||||
|
|
||||||
if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) {
|
if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) {
|
||||||
// TODO: this is a quick bailout
|
// TODO: this is a quick bailout
|
||||||
return Result(Result::Type::BAILOUT);
|
return Result(Result::Type::BAILOUT);
|
||||||
|
|
|
@ -126,7 +126,7 @@ bool isWithoutSideEffects(bool cpp, const Token* tok);
|
||||||
bool isUniqueExpression(const Token* tok);
|
bool isUniqueExpression(const Token* tok);
|
||||||
|
|
||||||
/** Is scope a return scope (scope will unconditionally return) */
|
/** Is scope a return scope (scope will unconditionally return) */
|
||||||
bool isReturnScope(const Token *endToken, const Settings * settings = nullptr, bool functionScope=false);
|
bool isReturnScope(const Token * const endToken, const Library * library=nullptr, bool functionScope=false);
|
||||||
|
|
||||||
/// Return the token to the function and the argument number
|
/// Return the token to the function and the argument number
|
||||||
const Token * getTokenArgumentFunction(const Token * tok, int& argn);
|
const Token * getTokenArgumentFunction(const Token * tok, int& argn);
|
||||||
|
|
|
@ -1401,7 +1401,7 @@ void SymbolDatabase::createSymbolDatabaseEscapeFunctions()
|
||||||
Function * function = scope.function;
|
Function * function = scope.function;
|
||||||
if (!function)
|
if (!function)
|
||||||
continue;
|
continue;
|
||||||
function->isEscapeFunction(isReturnScope(scope.bodyEnd, mSettings, true));
|
function->isEscapeFunction(isReturnScope(scope.bodyEnd, &mSettings->library, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -723,6 +723,8 @@ bool TemplateSimplifier::getTemplateDeclarations()
|
||||||
}
|
}
|
||||||
if (!tok1)
|
if (!tok1)
|
||||||
syntaxError(tok);
|
syntaxError(tok);
|
||||||
|
if (!tok1->next())
|
||||||
|
syntaxError(tok);
|
||||||
// Some syntax checks, see #6865
|
// Some syntax checks, see #6865
|
||||||
if (!tok->tokAt(2))
|
if (!tok->tokAt(2))
|
||||||
syntaxError(tok->next());
|
syntaxError(tok->next());
|
||||||
|
|
|
@ -1678,9 +1678,12 @@ static void valueFlowReverse(TokenList *tokenlist,
|
||||||
if (Token::Match(tok2->previous(), "!!* %name% =")) {
|
if (Token::Match(tok2->previous(), "!!* %name% =")) {
|
||||||
Token* assignTok = const_cast<Token*>(tok2->next()->astOperand2());
|
Token* assignTok = const_cast<Token*>(tok2->next()->astOperand2());
|
||||||
if (!assignTok->hasKnownValue()) {
|
if (!assignTok->hasKnownValue()) {
|
||||||
std::list<ValueFlow::Value> values = {val};
|
|
||||||
setTokenValue(assignTok, val, settings);
|
setTokenValue(assignTok, val, settings);
|
||||||
|
const std::string info = "Assignment from '" + assignTok->expressionString() + "'";
|
||||||
|
val.errorPath.emplace_back(assignTok, info);
|
||||||
|
std::list<ValueFlow::Value> values = {val};
|
||||||
if (val2.condition) {
|
if (val2.condition) {
|
||||||
|
val2.errorPath.emplace_back(assignTok, info);
|
||||||
setTokenValue(assignTok, val2, settings);
|
setTokenValue(assignTok, val2, settings);
|
||||||
values.push_back(val2);
|
values.push_back(val2);
|
||||||
}
|
}
|
||||||
|
@ -2190,7 +2193,7 @@ static bool valueFlowForwardVariable(Token* const startToken,
|
||||||
++indentlevel;
|
++indentlevel;
|
||||||
else if (indentlevel >= 0 && tok2->str() == "}") {
|
else if (indentlevel >= 0 && tok2->str() == "}") {
|
||||||
--indentlevel;
|
--indentlevel;
|
||||||
if (indentlevel <= 0 && isReturnScope(tok2, settings) && Token::Match(tok2->link()->previous(), "else|) {")) {
|
if (indentlevel <= 0 && isReturnScope(tok2, &settings->library) && Token::Match(tok2->link()->previous(), "else|) {")) {
|
||||||
const Token *condition = tok2->link();
|
const Token *condition = tok2->link();
|
||||||
const bool iselse = Token::simpleMatch(condition->tokAt(-2), "} else {");
|
const bool iselse = Token::simpleMatch(condition->tokAt(-2), "} else {");
|
||||||
if (iselse)
|
if (iselse)
|
||||||
|
@ -2240,7 +2243,7 @@ static bool valueFlowForwardVariable(Token* const startToken,
|
||||||
return true;
|
return true;
|
||||||
} else if (indentlevel <= 0 &&
|
} else if (indentlevel <= 0 &&
|
||||||
Token::simpleMatch(tok2->link()->previous(), "else {") &&
|
Token::simpleMatch(tok2->link()->previous(), "else {") &&
|
||||||
!isReturnScope(tok2->link()->tokAt(-2), settings) &&
|
!isReturnScope(tok2->link()->tokAt(-2), &settings->library) &&
|
||||||
isVariableChanged(tok2->link(), tok2, varid, var->isGlobal(), settings, tokenlist->isCPP())) {
|
isVariableChanged(tok2->link(), tok2, varid, var->isGlobal(), settings, tokenlist->isCPP())) {
|
||||||
lowerToPossible(values);
|
lowerToPossible(values);
|
||||||
}
|
}
|
||||||
|
@ -2506,7 +2509,7 @@ static bool valueFlowForwardVariable(Token* const startToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop after conditional return scopes that are executed
|
// stop after conditional return scopes that are executed
|
||||||
if (isReturnScope(end, settings)) {
|
if (isReturnScope(end, &settings->library)) {
|
||||||
std::list<ValueFlow::Value>::iterator it;
|
std::list<ValueFlow::Value>::iterator it;
|
||||||
for (it = values.begin(); it != values.end();) {
|
for (it = values.begin(); it != values.end();) {
|
||||||
if (conditionIsTrue(tok2->next()->astOperand2(), getProgramMemory(tok2, varid, *it)))
|
if (conditionIsTrue(tok2->next()->astOperand2(), getProgramMemory(tok2, varid, *it)))
|
||||||
|
@ -4375,7 +4378,7 @@ struct ValueFlowConditionHandler {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dead_if = isReturnScope(after, settings) ||
|
bool dead_if = isReturnScope(after, &settings->library) ||
|
||||||
(tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while (") &&
|
(tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while (") &&
|
||||||
!isBreakScope(after));
|
!isBreakScope(after));
|
||||||
bool dead_else = false;
|
bool dead_else = false;
|
||||||
|
@ -4387,7 +4390,7 @@ struct ValueFlowConditionHandler {
|
||||||
bailout(tokenlist, errorLogger, after, "possible noreturn scope");
|
bailout(tokenlist, errorLogger, after, "possible noreturn scope");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dead_else = isReturnScope(after, settings);
|
dead_else = isReturnScope(after, &settings->library);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dead_if && dead_else)
|
if (dead_if && dead_else)
|
||||||
|
|
|
@ -82,7 +82,9 @@ private:
|
||||||
TEST_CASE(nullpointer40);
|
TEST_CASE(nullpointer40);
|
||||||
TEST_CASE(nullpointer41);
|
TEST_CASE(nullpointer41);
|
||||||
TEST_CASE(nullpointer42);
|
TEST_CASE(nullpointer42);
|
||||||
|
TEST_CASE(nullpointer43); // #9404
|
||||||
TEST_CASE(nullpointer44); // #9395, #9423
|
TEST_CASE(nullpointer44); // #9395, #9423
|
||||||
|
TEST_CASE(nullpointer45);
|
||||||
TEST_CASE(nullpointer_addressOf); // address of
|
TEST_CASE(nullpointer_addressOf); // address of
|
||||||
TEST_CASE(nullpointerSwitch); // #2626
|
TEST_CASE(nullpointerSwitch); // #2626
|
||||||
TEST_CASE(nullpointer_cast); // #4692
|
TEST_CASE(nullpointer_cast); // #4692
|
||||||
|
@ -1538,6 +1540,17 @@ private:
|
||||||
errout.str());
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nullpointer43() {
|
||||||
|
check("struct A { int* x; };\n"
|
||||||
|
"void f(A* a) {\n"
|
||||||
|
" int * x = a->x;\n"
|
||||||
|
" if (x) {\n"
|
||||||
|
" (void)*a->x;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void nullpointer44() {
|
void nullpointer44() {
|
||||||
// #9395
|
// #9395
|
||||||
check("int foo( ) {\n"
|
check("int foo( ) {\n"
|
||||||
|
@ -1567,6 +1580,39 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nullpointer45() {
|
||||||
|
check("struct a {\n"
|
||||||
|
" a *b() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"void g() { throw 0; }\n"
|
||||||
|
"a h(a * c) {\n"
|
||||||
|
" if (c && c->b()) {}\n"
|
||||||
|
" if (!c)\n"
|
||||||
|
" g();\n"
|
||||||
|
" if (!c->b())\n"
|
||||||
|
" g();\n"
|
||||||
|
" a d = *c->b();\n"
|
||||||
|
" return d;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct a {\n"
|
||||||
|
" a *b() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"void e() { throw 0; }\n"
|
||||||
|
"a f() {\n"
|
||||||
|
" a *c = 0;\n"
|
||||||
|
" if (0 && c->b()) {}\n"
|
||||||
|
" if (!c)\n"
|
||||||
|
" e();\n"
|
||||||
|
" if (!c->b())\n"
|
||||||
|
" e();\n"
|
||||||
|
" a d = *c->b();\n"
|
||||||
|
" return d;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void nullpointer_addressOf() { // address of
|
void nullpointer_addressOf() { // address of
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
" struct X *x = 0;\n"
|
" struct X *x = 0;\n"
|
||||||
|
|
Loading…
Reference in New Issue