Fix issue 9578: false negative: (style) Condition '...' is always false
This commit is contained in:
parent
0e736e0c29
commit
d5b6d49d96
|
@ -20,6 +20,7 @@
|
|||
//---------------------------------------------------------------------------
|
||||
#include "astutils.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "library.h"
|
||||
#include "mathlib.h"
|
||||
#include "settings.h"
|
||||
|
@ -32,13 +33,13 @@
|
|||
#include <list>
|
||||
#include <stack>
|
||||
|
||||
|
||||
void visitAstNodes(const Token *ast, std::function<ChildrenToVisit(const Token *)> visitor)
|
||||
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
|
||||
void visitAstNodesGeneric(T *ast, std::function<ChildrenToVisit(T *)> visitor)
|
||||
{
|
||||
std::stack<const Token *> tokens;
|
||||
std::stack<T *> tokens;
|
||||
tokens.push(ast);
|
||||
while (!tokens.empty()) {
|
||||
const Token *tok = tokens.top();
|
||||
T *tok = tokens.top();
|
||||
tokens.pop();
|
||||
if (!tok)
|
||||
continue;
|
||||
|
@ -54,6 +55,16 @@ void visitAstNodes(const Token *ast, std::function<ChildrenToVisit(const Token *
|
|||
}
|
||||
}
|
||||
|
||||
void visitAstNodes(const Token *ast, std::function<ChildrenToVisit(const Token *)> visitor)
|
||||
{
|
||||
visitAstNodesGeneric(ast, std::move(visitor));
|
||||
}
|
||||
|
||||
void visitAstNodes(Token *ast, std::function<ChildrenToVisit(Token *)> visitor)
|
||||
{
|
||||
visitAstNodesGeneric(ast, std::move(visitor));
|
||||
}
|
||||
|
||||
static void astFlattenRecursive(const Token *tok, std::vector<const Token *> *result, const char* op, nonneg int depth = 0)
|
||||
{
|
||||
++depth;
|
||||
|
|
|
@ -47,6 +47,7 @@ enum class ChildrenToVisit {
|
|||
* Visit AST nodes recursively. The order is not "well defined"
|
||||
*/
|
||||
void visitAstNodes(const Token *ast, std::function<ChildrenToVisit(const Token *)> visitor);
|
||||
void visitAstNodes(Token *ast, std::function<ChildrenToVisit(Token *)> visitor);
|
||||
|
||||
std::vector<const Token*> astFlatten(const Token* tok, const char* op);
|
||||
|
||||
|
|
|
@ -1401,6 +1401,8 @@ void CheckCondition::alwaysTrueFalse()
|
|||
condition = parent->astOperand1();
|
||||
else if (Token::Match(parent->previous(), "if|while ("))
|
||||
condition = parent->astOperand2();
|
||||
else if (Token::simpleMatch(parent, "return"))
|
||||
condition = parent->astOperand1();
|
||||
else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() && Token::simpleMatch(parent->astParent()->astParent()->previous(), "for ("))
|
||||
condition = parent->astOperand1();
|
||||
else
|
||||
|
@ -1427,8 +1429,9 @@ void CheckCondition::alwaysTrueFalse()
|
|||
const bool constValExpr = tok->isNumber() && Token::Match(tok->astParent(),"%oror%|&&|?"); // just one number in boolean expression
|
||||
const bool compExpr = Token::Match(tok, "%comp%|!"); // a compare expression
|
||||
const bool ternaryExpression = Token::simpleMatch(tok->astParent(), "?");
|
||||
const bool returnExpression = Token::simpleMatch(tok->astTop(), "return") && (tok->isComparisonOp() || Token::Match(tok, "&&|%oror%"));
|
||||
|
||||
if (!(constIfWhileExpression || constValExpr || compExpr || ternaryExpression))
|
||||
if (!(constIfWhileExpression || constValExpr || compExpr || ternaryExpression || returnExpression))
|
||||
continue;
|
||||
|
||||
// Don't warn when there are expanded macros..
|
||||
|
|
|
@ -4071,39 +4071,36 @@ struct ValueFlowConditionHandler {
|
|||
|
||||
if (Token::Match(tok->astParent(), "%oror%|&&")) {
|
||||
Token *parent = tok->astParent();
|
||||
const std::string &op(parent->str());
|
||||
|
||||
if (parent->astOperand1() == tok && ((op == "&&" && Token::Match(tok, "==|>=|<=|!")) ||
|
||||
(op == "||" && Token::Match(tok, "%name%|!=")))) {
|
||||
for (; parent && parent->str() == op; parent = parent->astParent()) {
|
||||
std::stack<Token *> tokens;
|
||||
tokens.push(parent->astOperand2());
|
||||
bool assign = false;
|
||||
while (!tokens.empty()) {
|
||||
Token *rhstok = tokens.top();
|
||||
tokens.pop();
|
||||
if (!rhstok)
|
||||
continue;
|
||||
tokens.push(rhstok->astOperand1());
|
||||
tokens.push(rhstok->astOperand2());
|
||||
if (isSameExpression(
|
||||
tokenlist->isCPP(), false, cond.vartok, rhstok, settings->library, true, false))
|
||||
setTokenValue(rhstok, cond.true_values.front(), settings);
|
||||
else if (Token::Match(rhstok, "++|--|=") && isSameExpression(tokenlist->isCPP(),
|
||||
if (astIsRHS(tok) && parent->astParent() && parent->str() == parent->astParent()->str())
|
||||
parent = parent->astParent();
|
||||
else if (!astIsLHS(tok)) {
|
||||
parent = nullptr;
|
||||
}
|
||||
if (parent) {
|
||||
const std::string &op(parent->str());
|
||||
bool assign = false;
|
||||
std::list<ValueFlow::Value> values = cond.true_values;
|
||||
if (Token::Match(tok, "==|!="))
|
||||
changePossibleToKnown(values);
|
||||
if ((op == "&&" && Token::Match(tok, "==|>=|<=|!")) ||
|
||||
(op == "||" && Token::Match(tok, "%name%|!="))) {
|
||||
visitAstNodes(parent->astOperand2(), [&](Token* tok2) {
|
||||
if (isSameExpression(tokenlist->isCPP(), false, cond.vartok, tok2, settings->library, true, false))
|
||||
setTokenValue(tok2, values.front(), settings);
|
||||
else if (Token::Match(tok2, "++|--|=") && isSameExpression(tokenlist->isCPP(),
|
||||
false,
|
||||
cond.vartok,
|
||||
rhstok->astOperand1(),
|
||||
tok2->astOperand1(),
|
||||
settings->library,
|
||||
true,
|
||||
false)) {
|
||||
assign = true;
|
||||
break;
|
||||
return ChildrenToVisit::done;
|
||||
}
|
||||
}
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
});
|
||||
if (assign)
|
||||
break;
|
||||
while (parent->astParent() && parent == parent->astParent()->astOperand2())
|
||||
parent = parent->astParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -835,14 +835,16 @@ private:
|
|||
" a++;\n"
|
||||
"}\n"
|
||||
);
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=3' is always true\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if (1 != x || 3 != x)\n"
|
||||
" a++;\n"
|
||||
"}\n"
|
||||
);
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition '3!=x' is always true\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if (x<0 && !x) {}\n"
|
||||
|
@ -852,12 +854,14 @@ private:
|
|||
check("void f(int x) {\n"
|
||||
" if (x==0 && x) {}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 0 && x.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 0 && x.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x' is always false\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n" // ast..
|
||||
" if (y == 1 && x == 1 && x == 7) { }\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 7.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 7.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==7' is always false\n", errout.str());
|
||||
|
||||
check("void f(int x, int y) {\n"
|
||||
" if (x != 1 || y != 1)\n"
|
||||
|
@ -878,7 +882,7 @@ private:
|
|||
" a++;\n"
|
||||
"}\n"
|
||||
);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=3' is always true\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if ((x != 1) && (x != 3))\n"
|
||||
|
@ -921,7 +925,8 @@ private:
|
|||
" a++;\n"
|
||||
"}\n"
|
||||
);
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 5 || x != 6.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 5 || x != 6.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=6' is always true\n", errout.str());
|
||||
|
||||
check("void f(unsigned int a, unsigned int b, unsigned int c) {\n"
|
||||
" if((a != b) || (c != b) || (c != a))\n"
|
||||
|
@ -945,13 +950,14 @@ private:
|
|||
" if ((x == 1) && (x == 0x00000001))\n"
|
||||
" a++;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==0x00000001' is always true\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if (x == 1 && x == 3)\n"
|
||||
" a++;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 3.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 3.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==3' is always false\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if (x == 1.0 && x == 3.0)\n"
|
||||
|
@ -1074,7 +1080,8 @@ private:
|
|||
" a++;\n"
|
||||
"}");
|
||||
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=4' is always true\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if ((x!=4) && (x==3))\n"
|
||||
|
@ -1092,13 +1099,15 @@ private:
|
|||
" if ((x!=4) || (x==3))\n"
|
||||
" a++;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==3' is always false\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if ((x==3) && (x!=3))\n"
|
||||
" a++;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 3 && x != 3.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 3 && x != 3.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=3' is always false\n", errout.str());
|
||||
|
||||
check("void f(int x) {\n"
|
||||
" if ((x==6) || (x!=6))\n"
|
||||
|
@ -1190,7 +1199,8 @@ private:
|
|||
check("void f(char x) {\n"
|
||||
" if (x == '1' && x == '2') {}\n"
|
||||
"}", "test.cpp", true);
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == '1' && x == '2'.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == '1' && x == '2'.\n"
|
||||
"[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x=='2'' is always false\n", errout.str());
|
||||
|
||||
check("int f(char c) {\n"
|
||||
" return (c >= 'a' && c <= 'z');\n"
|
||||
|
@ -1264,7 +1274,8 @@ private:
|
|||
" if ((t == A) && (t == B))\n"
|
||||
" {}\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: t == 0 && t == 1.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: t == 0 && t == 1.\n"
|
||||
"[test.cpp:3] -> [test.cpp:3]: (style) Condition 't==B' is always false\n", errout.str());
|
||||
}
|
||||
|
||||
void incorrectLogicOperator11() {
|
||||
|
@ -2864,7 +2875,7 @@ private:
|
|||
" if(x == 0) { x++; return x == 0; } \n"
|
||||
" return false;\n"
|
||||
"}");
|
||||
TODO_ASSERT_EQUALS("return value is always true?", "", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==0' is always false\n", errout.str());
|
||||
|
||||
check("void f() {\n" // #6898 (Token::expressionString)
|
||||
" int x = 0;\n"
|
||||
|
@ -3757,6 +3768,12 @@ private:
|
|||
check("bool f();\n"
|
||||
"void foo() { bool x = true; if(x&&f()) {}}\n");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str());
|
||||
|
||||
// #9578
|
||||
check("bool f(const std::string &s) {\n"
|
||||
" return s.size()>2U && s[0]=='4' && s[0]=='2';\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str());
|
||||
}
|
||||
|
||||
void pointerAdditionResultNotNull() {
|
||||
|
|
Loading…
Reference in New Issue