ValueFlow: Set arrays to true when converting to a boolean

This sets it by checking the parent. It doesn't handle function parameters yet.
This commit is contained in:
Paul Fultz II 2019-01-21 20:05:35 +01:00 committed by Daniel Marjamäki
parent 63660fcc2b
commit 4b37f276c2
3 changed files with 112 additions and 9 deletions

View File

@ -1027,14 +1027,6 @@ static void valueFlowArray(TokenList *tokenlist)
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->varId() > 0U) {
// array
if (tok->variable() && tok->variable()->isArray() && !tok->variable()->isArgument() &&
!tok->variable()->isStlType()) {
ValueFlow::Value value{1};
value.setKnown();
// TODO : this leads to too many false positives so it is commented out.
// See for instance https://github.com/danmar/cppcheck/commit/025881cf35fdde1299d16a09059e7305f8c9bd13
// setTokenValue(tok, value, tokenlist->getSettings());
}
const std::map<unsigned int, const Token *>::const_iterator it = constantArrays.find(tok->varId());
if (it != constantArrays.end()) {
ValueFlow::Value value;
@ -1079,6 +1071,57 @@ static void valueFlowArray(TokenList *tokenlist)
}
}
static bool isNonZero(const Token *tok)
{
return tok && (!tok->hasKnownIntValue() || tok->values().front().intvalue != 0);
}
static const Token *getOtherOperand(const Token *tok)
{
if (!tok)
return nullptr;
if (!tok->astParent())
return nullptr;
if (tok->astParent()->astOperand1() != tok)
return tok->astParent()->astOperand1();
if (tok->astParent()->astOperand2() != tok)
return tok->astParent()->astOperand2();
return nullptr;
}
static void valueFlowArrayBool(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->hasKnownIntValue())
continue;
const Variable *var = nullptr;
bool known = false;
std::list<ValueFlow::Value>::const_iterator val =
std::find_if(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue));
if (val == tok->values().end()) {
var = tok->variable();
known = true;
} else {
var = val->tokvalue->variable();
known = val->isKnown();
}
if (!var)
continue;
if (!var->isArray() || var->isArgument() || var->isStlType())
continue;
if (isNonZero(getOtherOperand(tok)) && Token::Match(tok->astParent(), "%comp%"))
continue;
// TODO: Check for function argument
if ((astIsBool(tok->astParent()) && !Token::Match(tok->astParent(), "(|%name%")) ||
(tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|for ("))) {
ValueFlow::Value value{1};
if (known)
value.setKnown();
setTokenValue(tok, value, tokenlist->getSettings());
}
}
}
static void valueFlowPointerAlias(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
@ -4792,6 +4835,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
std::size_t values = 0;
while (std::time(0) < timeout && values < getTotalValues(tokenlist)) {
values = getTotalValues(tokenlist);
valueFlowArrayBool(tokenlist);
valueFlowRightShift(tokenlist, settings);
valueFlowOppositeCondition(symboldatabase, settings);
valueFlowTerminatingCondition(tokenlist, symboldatabase, settings);

View File

@ -2804,6 +2804,13 @@ private:
" p = (unsigned char *) buf + sizeof (buf);\n"
"}", false, "6350.c");
ASSERT_EQUALS("", errout.str());
check("int f() {\n"
" const char d[] = \"0123456789\";\n"
" char *cp = d + 3;\n"
" return cp - d;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void pointer_out_of_bounds_2() {

View File

@ -2592,7 +2592,59 @@ private:
" int buf[42];\n"
" if( buf != 0) {}\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf!=0' is always true\n", "", errout.str()); // #8924
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf!=0' is always true\n", errout.str()); // #8924
check("void f() {\n"
" int buf[42];\n"
" if( !buf ) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!buf' is always false\n", errout.str());
check("void f() {\n"
" int buf[42];\n"
" bool b = buf;\n"
" if( b ) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'b' is always true\n", errout.str());
check("void f() {\n"
" int buf[42];\n"
" bool b = buf;\n"
" if( !b ) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always false\n", errout.str());
check("void f() {\n"
" int buf[42];\n"
" int * p = nullptr;\n"
" if( buf == p ) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'buf==p' is always false\n", errout.str());
check("void f(bool x) {\n"
" int buf[42];\n"
" if( buf || x ) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf' is always true\n", errout.str());
check("void f(int * p) {\n"
" int buf[42];\n"
" if( buf == p ) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int buf[42];\n"
" int p[42];\n"
" if( buf == p ) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int buf[42];\n"
" if( buf == 1) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
// Avoid FP when condition comes from macro
check("#define NOT !\n"