Skip to content

Commit 827e87a

Browse files
Fix #11579 FN knownConditionTrueFalse with non-bool as bool parameter / #9450 string literal to bool conversion in function call (#5338)
1 parent 5dbcea3 commit 827e87a

10 files changed

Lines changed: 64 additions & 7 deletions

lib/astutils.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,19 @@ static bool astIsBoolLike(const Token* tok)
14641464
return astIsBool(tok) || isUsedAsBool(tok);
14651465
}
14661466

1467+
bool isBooleanFuncArg(const Token* tok) {
1468+
if (tok->variable() && tok->variable()->valueType() && tok->variable()->valueType()->type == ValueType::BOOL) // skip trivial case: bool passed as bool
1469+
return false;
1470+
int argn{};
1471+
const Token* ftok = getTokenArgumentFunction(tok, argn);
1472+
if (!ftok)
1473+
return false;
1474+
std::vector<const Variable*> argvars = getArgumentVars(ftok, argn);
1475+
if (argvars.size() != 1)
1476+
return false;
1477+
return !argvars[0]->isReference() && argvars[0]->valueType() && argvars[0]->valueType()->type == ValueType::BOOL;
1478+
}
1479+
14671480
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors)
14681481
{
14691482
if (tok1 == nullptr && tok2 == nullptr)

lib/astutils.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,15 @@ bool isStructuredBindingVariable(const Variable* var);
252252
const Token* isInLoopCondition(const Token* tok);
253253

254254
/**
255-
* Is token used a boolean, that is to say cast to a bool, or used as a condition in a if/while/for
255+
* Is token used as boolean, that is to say cast to a bool, or used as a condition in a if/while/for
256256
*/
257257
CPPCHECKLIB bool isUsedAsBool(const Token * const tok);
258258

259+
/**
260+
* Is token passed to a function taking a bool argument
261+
*/
262+
CPPCHECKLIB bool isBooleanFuncArg(const Token* tok);
263+
259264
/**
260265
* Are two conditions opposite
261266
* @param isNot do you want to know if cond1 is !cond2 or if cond1 and cond2 are non-overlapping. true: cond1==!cond2 false: cond1==true => cond2==false

lib/checkcondition.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,7 +1466,7 @@ void CheckCondition::alwaysTrueFalse()
14661466
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
14671467
if (Token::simpleMatch(tok, "<") && tok->link()) // don't write false positives when templates are used
14681468
continue;
1469-
if (!tok->hasKnownIntValue())
1469+
if (!tok->hasKnownBoolValue())
14701470
continue;
14711471
if (Token::Match(tok->previous(), "%name% (") && tok->previous()->function()) {
14721472
const Function* f = tok->previous()->function();
@@ -1490,6 +1490,8 @@ void CheckCondition::alwaysTrueFalse()
14901490
else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() &&
14911491
Token::simpleMatch(parent->astParent()->astParent()->previous(), "for ("))
14921492
condition = parent->astParent()->astParent()->previous();
1493+
else if (isBooleanFuncArg(tok))
1494+
condition = tok;
14931495
else
14941496
continue;
14951497
}
@@ -1580,14 +1582,17 @@ void CheckCondition::alwaysTrueFalse()
15801582
if (hasSizeof)
15811583
continue;
15821584

1583-
alwaysTrueFalseError(tok, condition, &tok->values().front());
1585+
auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) {
1586+
return v.isIntValue();
1587+
});
1588+
alwaysTrueFalseError(tok, condition, &*it);
15841589
}
15851590
}
15861591
}
15871592

15881593
void CheckCondition::alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value)
15891594
{
1590-
const bool alwaysTrue = value && (value->intvalue != 0);
1595+
const bool alwaysTrue = value && (value->intvalue != 0 || value->isImpossible());
15911596
const std::string expr = tok ? tok->expressionString() : std::string("x");
15921597
const std::string conditionStr = (Token::simpleMatch(condition, "return") ? "Return value" : "Condition");
15931598
const std::string errmsg = conditionStr + " '" + expr + "' is always " + (alwaysTrue ? "true" : "false");

lib/checkstring.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,10 @@ void CheckString::checkIncorrectStringCompare()
292292
incorrectStringBooleanError(tok->next(), tok->strAt(1));
293293
} else if (Token::Match(tok, "if|while ( %str%|%char% )") && !tok->tokAt(2)->getValue(0)) {
294294
incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2));
295-
} else if (tok->str() == "?" && Token::Match(tok->astOperand1(), "%str%|%char%"))
295+
} else if (tok->str() == "?" && Token::Match(tok->astOperand1(), "%str%|%char%")) {
296296
incorrectStringBooleanError(tok->astOperand1(), tok->astOperand1()->str());
297+
} else if (Token::Match(tok, "%str%") && isBooleanFuncArg(tok))
298+
incorrectStringBooleanError(tok, tok->str());
297299
}
298300
}
299301
}

lib/forwardanalyzer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ struct ForwardTraversal {
488488
if (allAnalysis.isIncremental())
489489
return Break(Analyzer::Terminate::Bail);
490490
} else if (allAnalysis.isModified()) {
491-
std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, allAnalysis.isModified());
491+
std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, /*isModified*/ true);
492492
bool forkContinue = true;
493493
for (ForwardTraversal& ft : ftv) {
494494
if (condTok)

lib/token.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,6 +2371,15 @@ bool Token::hasKnownIntValue() const
23712371
});
23722372
}
23732373

2374+
bool Token::hasKnownBoolValue() const
2375+
{
2376+
if (!mImpl->mValues)
2377+
return false;
2378+
return std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value& value) {
2379+
return value.isIntValue() && (value.isKnown() || (value.intvalue == 0 && value.isImpossible()));
2380+
});
2381+
}
2382+
23742383
bool Token::hasKnownValue() const
23752384
{
23762385
return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown));

lib/token.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,7 @@ class CPPCHECKLIB Token {
12121212
}
12131213

12141214
bool hasKnownIntValue() const;
1215+
bool hasKnownBoolValue() const;
12151216
bool hasKnownValue() const;
12161217
bool hasKnownValue(ValueFlow::Value::ValueType t) const;
12171218
bool hasKnownSymbolicValue(const Token* tok) const;

lib/tokenlist.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ static void compilePrecedence2(Token *&tok, AST_state& state)
912912
}
913913
compileBinOp(tok, state, compileScope);
914914
} else if (tok->str() == "[") {
915-
if (state.cpp && isPrefixUnary(tok, state.cpp) && Token::Match(tok->link(), "] (|{|<")) { // Lambda
915+
if (state.cpp && isPrefixUnary(tok, /*cpp*/ true) && Token::Match(tok->link(), "] (|{|<")) { // Lambda
916916
// What we do here:
917917
// - Nest the round bracket under the square bracket.
918918
// - Nest what follows the lambda (if anything) with the lambda opening [

test/testcondition.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4514,6 +4514,21 @@ class TestCondition : public TestFixture {
45144514
" return a;\n"
45154515
"}\n");
45164516
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'a==\"x\"' is always true\n", errout.str());
4517+
4518+
check("void g(bool);\n"
4519+
"void f() {\n"
4520+
" int i = 5;\n"
4521+
" int* p = &i;\n"
4522+
" g(i == 7);\n"
4523+
" g(p == nullptr);\n"
4524+
" g(p);\n"
4525+
" g(&i);\n"
4526+
"}\n");
4527+
ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'i==7' is always false\n"
4528+
"[test.cpp:6]: (style) Condition 'p==nullptr' is always false\n"
4529+
"[test.cpp:7]: (style) Condition 'p' is always true\n"
4530+
"[test.cpp:8]: (style) Condition '&i' is always true\n",
4531+
errout.str());
45174532
}
45184533

45194534
void alwaysTrueSymbolic()

test/teststring.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,13 @@ class TestString : public TestFixture {
743743
"}");
744744
ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of char literal '\\0' to bool always evaluates to false.\n"
745745
"[test.cpp:3]: (warning) Conversion of char literal L'\\0' to bool always evaluates to false.\n", errout.str());
746+
747+
check("void f(bool b);\n" // #9450
748+
"void f(std::string s);\n"
749+
"void g() {\n"
750+
" f(\"abc\");\n"
751+
"}\n");
752+
ASSERT_EQUALS("[test.cpp:4]: (warning) Conversion of string literal \"abc\" to bool always evaluates to true.\n", errout.str());
746753
}
747754

748755
void deadStrcmp() {

0 commit comments

Comments
 (0)