Fix issue 6856: add checks in isOppositeCond when using == and < or > (#1298)

* Fix issue 6856: add checks in isOppositeCond when using == and < or >

* Move tests to testcondition

* Fix some more tests

* Fix test messages

* Remove the float check
This commit is contained in:
Paul Fultz II 2018-08-05 15:39:40 -05:00 committed by Daniel Marjamäki
parent 13cf982b77
commit b839ad60dd
3 changed files with 62 additions and 18 deletions

View File

@ -395,7 +395,10 @@ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token
(comp1 == ">" && comp2 == "<=") || (comp1 == ">" && comp2 == "<=") ||
(comp1 == ">=" && comp2 == "<") || (comp1 == ">=" && comp2 == "<") ||
(!isNot && ((comp1 == "<" && comp2 == ">") || (!isNot && ((comp1 == "<" && comp2 == ">") ||
(comp1 == ">" && comp2 == "<")))); (comp1 == ">" && comp2 == "<") ||
(comp1 == "==" && (comp2 == "!=" || comp2 == ">" || comp2 == "<")) ||
((comp1 == "!=" || comp1 == ">" || comp1 == "<") && comp2 == "==")
)));
} }
bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure) bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure)

View File

@ -861,6 +861,28 @@ static std::string conditionString(bool not1, const Token *expr1, const std::str
(expr1->isName() ? expr1->str() : std::string("EXPR")); (expr1->isName() ? expr1->str() : std::string("EXPR"));
} }
static std::string conditionString(const Token * tok)
{
if(!tok)
return "";
if(tok->isComparisonOp()) {
bool inconclusive = false;
bool not_;
std::string op, value;
const Token *expr;
if(parseComparison(tok, &not_, &op, &value, &expr, &inconclusive) && expr->isName()) {
return conditionString(not_, expr, op, value);
}
}
if(Token::Match(tok, "%cop%|&&|%oror%")) {
if(tok->astOperand2())
return conditionString(tok->astOperand1()) + " " + tok->str() + " " + conditionString(tok->astOperand2());
return tok->str() + "(" + conditionString(tok->astOperand1()) + ")";
}
return tok->expressionString();
}
void CheckCondition::checkIncorrectLogicOperator() void CheckCondition::checkIncorrectLogicOperator()
{ {
const bool printStyle = mSettings->isEnabled(Settings::STYLE); const bool printStyle = mSettings->isEnabled(Settings::STYLE);
@ -876,14 +898,6 @@ void CheckCondition::checkIncorrectLogicOperator()
if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2()) if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2())
continue; continue;
// Opposite comparisons around || or && => always true or always false
if ((tok->astOperand1()->isName() || tok->astOperand2()->isName()) &&
isOppositeCond(true, mTokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), mSettings->library, true)) {
const bool alwaysTrue(tok->str() == "||");
incorrectLogicOperatorError(tok, tok->expressionString(), alwaysTrue, false);
continue;
}
// 'A && (!A || B)' is equivalent to 'A && B' // 'A && (!A || B)' is equivalent to 'A && B'
// 'A || (!A && B)' is equivalent to 'A || B' // 'A || (!A && B)' is equivalent to 'A || B'
@ -934,36 +948,48 @@ void CheckCondition::checkIncorrectLogicOperator()
const Token *comp2 = tok->astOperand2(); const Token *comp2 = tok->astOperand2();
bool inconclusive = false; bool inconclusive = false;
bool parseable = true;
// Parse LHS // Parse LHS
bool not1; bool not1;
std::string op1, value1; std::string op1, value1;
const Token *expr1; const Token *expr1 = nullptr;
if (!parseComparison(comp1, &not1, &op1, &value1, &expr1, &inconclusive)) parseable &= (parseComparison(comp1, &not1, &op1, &value1, &expr1, &inconclusive));
continue;
// Parse RHS // Parse RHS
bool not2; bool not2;
std::string op2, value2; std::string op2, value2;
const Token *expr2; const Token *expr2 = nullptr;
if (!parseComparison(comp2, &not2, &op2, &value2, &expr2, &inconclusive)) parseable &= (parseComparison(comp2, &not2, &op2, &value2, &expr2, &inconclusive));
continue;
if (inconclusive && !printInconclusive) if (inconclusive && !printInconclusive)
continue; continue;
const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2);
// Opposite comparisons around || or && => always true or always false
if (!isfloat && isOppositeCond(tok->str() == "||", mTokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), mSettings->library, true)) {
const bool alwaysTrue(tok->str() == "||");
incorrectLogicOperatorError(tok, conditionString(tok), alwaysTrue, inconclusive);
continue;
}
if(!parseable)
continue;
if (isSameExpression(mTokenizer->isCPP(), true, comp1, comp2, mSettings->library, true)) if (isSameExpression(mTokenizer->isCPP(), true, comp1, comp2, mSettings->library, true))
continue; // same expressions => only report that there are same expressions continue; // same expressions => only report that there are same expressions
if (!isSameExpression(mTokenizer->isCPP(), true, expr1, expr2, mSettings->library, true)) if (!isSameExpression(mTokenizer->isCPP(), true, expr1, expr2, mSettings->library, true))
continue; continue;
const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2);
// don't check floating point equality comparisons. that is bad // don't check floating point equality comparisons. that is bad
// and deserves different warnings. // and deserves different warnings.
if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!=")) if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!="))
continue; continue;
const double d1 = (isfloat) ? MathLib::toDoubleNumber(value1) : 0; const double d1 = (isfloat) ? MathLib::toDoubleNumber(value1) : 0;
const double d2 = (isfloat) ? MathLib::toDoubleNumber(value2) : 0; const double d2 = (isfloat) ? MathLib::toDoubleNumber(value2) : 0;
const MathLib::bigint i1 = (isfloat) ? 0 : MathLib::toLongNumber(value1); const MathLib::bigint i1 = (isfloat) ? 0 : MathLib::toLongNumber(value1);

View File

@ -70,6 +70,7 @@ private:
TEST_CASE(incorrectLogicOperator8); // ! TEST_CASE(incorrectLogicOperator8); // !
TEST_CASE(incorrectLogicOperator9); TEST_CASE(incorrectLogicOperator9);
TEST_CASE(incorrectLogicOperator10); // enum TEST_CASE(incorrectLogicOperator10); // enum
TEST_CASE(incorrectLogicOperator11);
TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError);
TEST_CASE(incorrectLogicOp_condSwapping); TEST_CASE(incorrectLogicOp_condSwapping);
TEST_CASE(testBug5895); TEST_CASE(testBug5895);
@ -1162,12 +1163,12 @@ private:
check("void f(int i) {\n" check("void f(int i) {\n"
" if (i || !i) {}\n" " if (i || !i) {}\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: i||!i.\n", errout.str()); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: i || !(i).\n", errout.str());
check("void f(int a, int b) {\n" check("void f(int a, int b) {\n"
" if (a>b || a<=b) {}\n" " if (a>b || a<=b) {}\n"
"}"); "}");
TODO_ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: a>b||a<=b.\n", "", errout.str()); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: a > b || a <= b.\n", errout.str());
check("void f(int a, int b) {\n" check("void f(int a, int b) {\n"
" if (a>b || a<b) {}\n" " if (a>b || a<b) {}\n"
@ -1218,6 +1219,20 @@ private:
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", errout.str());
} }
void incorrectLogicOperator11() {
check("void foo(int i, const int n) { if ( i < n && i == n ) {} }");
ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i < n && i == n.\n", errout.str());
check("void foo(int i, const int n) { if ( i > n && i == n ) {} }");
ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i > n && i == n.\n", errout.str());
check("void foo(int i, const int n) { if ( i == n && i > n ) {} }");
ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i > n.\n", errout.str());
check("void foo(int i, const int n) { if ( i == n && i < n ) {} }");
ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i < n.\n", errout.str());
}
void secondAlwaysTrueFalseWhenFirstTrueError() { void secondAlwaysTrueFalseWhenFirstTrueError() {
check("void f(int x) {\n" check("void f(int x) {\n"
" if (x > 5 && x != 1)\n" " if (x > 5 && x != 1)\n"