Fixed and refactorized broken CheckNullPointer::CanFunctionAssignPointer():
- return true if parameter is passed by reference (fixes #4111) - Use symboldatabase - Improved handling of inconclusive
This commit is contained in:
parent
3eac886591
commit
31e7e41098
|
@ -372,34 +372,31 @@ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Sym
|
||||||
|
|
||||||
|
|
||||||
// check if function can assign pointer
|
// check if function can assign pointer
|
||||||
bool CheckNullPointer::CanFunctionAssignPointer(const Token *functiontoken, unsigned int varid) const
|
bool CheckNullPointer::CanFunctionAssignPointer(const Token *functiontoken, unsigned int varid, bool& unknown) const
|
||||||
{
|
{
|
||||||
if (Token::Match(functiontoken, "if|while|for|switch|sizeof|catch"))
|
if (Token::Match(functiontoken, "if|while|for|switch|sizeof|catch"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int argumentNumber = 0;
|
int argumentNumber = 0;
|
||||||
for (const Token *arg = functiontoken->tokAt(2); arg; arg = arg->nextArgument()) {
|
for (const Token *arg = functiontoken->tokAt(2); arg; arg = arg->nextArgument()) {
|
||||||
++argumentNumber;
|
|
||||||
if (Token::Match(arg, "%varid% [,)]", varid)) {
|
if (Token::Match(arg, "%varid% [,)]", varid)) {
|
||||||
const Token *ftok = _tokenizer->getFunctionTokenByName(functiontoken->str().c_str());
|
const Token *ftok = _tokenizer->getFunctionTokenByName(functiontoken->str().c_str());
|
||||||
if (!Token::Match(ftok, "%type% (")) {
|
const Function* func = _tokenizer->getSymbolDatabase()->findFunctionByToken(ftok);
|
||||||
// assume that the function might assign the pointer
|
if (!func) { // Unknown function
|
||||||
|
unknown = true;
|
||||||
|
return true; // assume that the function might assign the pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
const Variable* var = func->getArgumentVar(argumentNumber);
|
||||||
|
if (!var) { // Unknown variable
|
||||||
|
unknown = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (var->isReference()) // Assume every pointer passed by reference is assigned
|
||||||
|
return true;
|
||||||
ftok = ftok->tokAt(2);
|
else
|
||||||
while (ftok && argumentNumber > 1) {
|
|
||||||
ftok = ftok->nextArgument();
|
|
||||||
--argumentNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it's a pointer parameter..
|
|
||||||
while (ftok && ftok->isName())
|
|
||||||
ftok = ftok->next();
|
|
||||||
if (Token::Match(ftok, "* *| const| %var% [,)]"))
|
|
||||||
// parameter is passed by value
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
++argumentNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pointer is not passed
|
// pointer is not passed
|
||||||
|
@ -602,13 +599,15 @@ void CheckNullPointer::nullPointerStructByDeRefAndChec()
|
||||||
// count { and } using tok2
|
// count { and } using tok2
|
||||||
const Token* const end2 = tok1->scope()->classEnd;
|
const Token* const end2 = tok1->scope()->classEnd;
|
||||||
for (const Token *tok2 = tok1->tokAt(3); tok2 != end2; tok2 = tok2->next()) {
|
for (const Token *tok2 = tok1->tokAt(3); tok2 != end2; tok2 = tok2->next()) {
|
||||||
|
bool unknown = false;
|
||||||
|
|
||||||
// label / ?:
|
// label / ?:
|
||||||
if (tok2->str() == ":")
|
if (tok2->str() == ":")
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// function call..
|
// function call..
|
||||||
else if (Token::Match(tok2, "[;{}] %var% (") && CanFunctionAssignPointer(tok2->next(), varid1)) {
|
else if (Token::Match(tok2, "[;{}] %var% (") && CanFunctionAssignPointer(tok2->next(), varid1, unknown)) {
|
||||||
if (!_settings->inconclusive)
|
if (!_settings->inconclusive || !unknown)
|
||||||
break;
|
break;
|
||||||
inconclusive = true;
|
inconclusive = true;
|
||||||
}
|
}
|
||||||
|
@ -726,8 +725,9 @@ void CheckNullPointer::nullPointerByDeRefAndChec()
|
||||||
|
|
||||||
// Passing pointer as parameter..
|
// Passing pointer as parameter..
|
||||||
if (Token::Match(tok2->next(), "%type% (")) {
|
if (Token::Match(tok2->next(), "%type% (")) {
|
||||||
if (CanFunctionAssignPointer(tok2->next(), varid)) {
|
bool unknown = false;
|
||||||
if (!_settings->inconclusive)
|
if (CanFunctionAssignPointer(tok2->next(), varid, unknown)) {
|
||||||
|
if (!_settings->inconclusive || !unknown)
|
||||||
break;
|
break;
|
||||||
inconclusive = true;
|
inconclusive = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ private:
|
||||||
* @brief Investigate if function call can make pointer null. If
|
* @brief Investigate if function call can make pointer null. If
|
||||||
* the pointer is passed by value it can't be made a null pointer.
|
* the pointer is passed by value it can't be made a null pointer.
|
||||||
*/
|
*/
|
||||||
bool CanFunctionAssignPointer(const Token *functiontoken, unsigned int varid) const;
|
bool CanFunctionAssignPointer(const Token *functiontoken, unsigned int varid, bool& unknown) const;
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
|
@ -2038,6 +2038,16 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (error) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (error) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str());
|
||||||
|
|
||||||
|
// function seen (taking reference parameter)
|
||||||
|
check("void foo(int *&p) { }\n"
|
||||||
|
"\n"
|
||||||
|
"void f(int *p) {\n"
|
||||||
|
" *p = 0;\n"
|
||||||
|
" foo(p);\n"
|
||||||
|
" if (p) { }\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
// function implementation not seen
|
// function implementation not seen
|
||||||
check("void foo(int *p);\n"
|
check("void foo(int *p);\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
Loading…
Reference in New Issue