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:
parent
13cf982b77
commit
b839ad60dd
|
@ -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)
|
||||||
|
|
|
@ -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, ¬_, &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, ¬1, &op1, &value1, &expr1, &inconclusive))
|
parseable &= (parseComparison(comp1, ¬1, &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, ¬2, &op2, &value2, &expr2, &inconclusive))
|
parseable &= (parseComparison(comp2, ¬2, &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);
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue