Fixed #6019 (false negative: Expression is always true/false '!(v!=10) && !(v!=20)')

This commit is contained in:
Daniel Marjamäki 2015-07-13 20:53:49 +02:00
parent 08d6c244ee
commit 1a872a2c9f
2 changed files with 141 additions and 121 deletions

View File

@ -606,6 +606,36 @@ static inline T getvalue(const int test, const T value1, const T value2)
return 0; return 0;
} }
static bool parseComparison(const Token *comp, bool *not1, std::string *op, std::string *value, const Token **expr)
{
*not1 = false;
while (comp && comp->str() == "!") {
*not1 = !(*not1);
comp = comp->astOperand1();
}
if (!comp || !comp->isComparisonOp() || !comp->astOperand1() || !comp->astOperand2())
return false;
if (comp->astOperand1()->isLiteral()) {
*op = invertOperatorForOperandSwap(comp->str());
*value = comp->astOperand1()->str();
*expr = comp->astOperand2();
} else if (comp->astOperand2()->isLiteral()) {
*op = comp->str();
*value = comp->astOperand2()->str();
*expr = comp->astOperand1();
} else {
return false;
}
// Only float and int values are currently handled
if (!MathLib::isInt(*value) && !MathLib::isFloat(*value))
return false;
return true;
}
void CheckCondition::checkIncorrectLogicOperator() void CheckCondition::checkIncorrectLogicOperator()
{ {
const bool printStyle = _settings->isEnabled("style"); const bool printStyle = _settings->isEnabled("style");
@ -619,24 +649,28 @@ void CheckCondition::checkIncorrectLogicOperator()
const Scope * scope = symbolDatabase->functionScopes[ii]; const Scope * scope = symbolDatabase->functionScopes[ii];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
// Opposite comparisons if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2())
if (Token::Match(tok, "%oror%|&&") && continue;
tok->astOperand1() &&
tok->astOperand2() && // Opposite comparisons around || or && => always true or always false
(tok->astOperand1()->isName() || tok->astOperand2()->isName()) && if ((tok->astOperand1()->isName() || tok->astOperand2()->isName()) &&
isOppositeCond(true, tok->astOperand1(), tok->astOperand2(), _settings->library.functionpure)) { isOppositeCond(true, tok->astOperand1(), tok->astOperand2(), _settings->library.functionpure)) {
const bool alwaysTrue(tok->str() == "||"); const bool alwaysTrue(tok->str() == "||");
incorrectLogicOperatorError(tok, tok->expressionString(), alwaysTrue); incorrectLogicOperatorError(tok, tok->expressionString(), alwaysTrue);
continue;
} }
else if (Token::Match(tok, "&&|%oror%")) {
// 'A && (!A || B)' is equivalent with 'A || B'
if (printStyle && (tok->str() == "||") && tok->astOperand1() && tok->astOperand2() && tok->astOperand2()->str() == "&&") { if (printStyle && (tok->str() == "||") && tok->astOperand1() && tok->astOperand2() && tok->astOperand2()->str() == "&&") {
const Token* tok2 = tok->astOperand2()->astOperand1(); const Token* tok2 = tok->astOperand2()->astOperand1();
if (isOppositeCond(true, tok->astOperand1(), tok2, _settings->library.functionpure)) { if (isOppositeCond(true, tok->astOperand1(), tok2, _settings->library.functionpure)) {
redundantConditionError(tok, tok2->expressionString() + ". 'A && (!A || B)' is equivalent to 'A || B'"); redundantConditionError(tok, tok2->expressionString() + ". 'A && (!A || B)' is equivalent to 'A || B'");
continue;
} }
} }
// Comparison #1 (LHS) // Comparison #1 (LHS)
const Token *comp1 = tok->astOperand1(); const Token *comp1 = tok->astOperand1();
if (comp1 && comp1->str() == tok->str()) if (comp1 && comp1->str() == tok->str())
@ -645,43 +679,18 @@ void CheckCondition::checkIncorrectLogicOperator()
// Comparison #2 (RHS) // Comparison #2 (RHS)
const Token *comp2 = tok->astOperand2(); const Token *comp2 = tok->astOperand2();
if (!comp1 || !comp1->isComparisonOp() || !comp1->astOperand1() || !comp1->astOperand2()) // Parse LHS
continue; bool not1;
if (!comp2 || !comp2->isComparisonOp() || !comp2->astOperand1() || !comp2->astOperand2())
continue;
std::string op1, value1; std::string op1, value1;
const Token *expr1; const Token *expr1;
if (comp1->astOperand1()->isLiteral()) { if (!parseComparison(comp1, &not1, &op1, &value1, &expr1))
op1 = invertOperatorForOperandSwap(comp1->str());
value1 = comp1->astOperand1()->str();
expr1 = comp1->astOperand2();
} else if (comp1->astOperand2()->isLiteral()) {
op1 = comp1->str();
value1 = comp1->astOperand2()->str();
expr1 = comp1->astOperand1();
} else {
continue; continue;
}
// Parse RHS
bool not2;
std::string op2, value2; std::string op2, value2;
const Token *expr2; const Token *expr2;
if (comp2->astOperand1()->isLiteral()) { if (!parseComparison(comp2, &not2, &op2, &value2, &expr2))
op2 = invertOperatorForOperandSwap(comp2->str());
value2 = comp2->astOperand1()->str();
expr2 = comp2->astOperand2();
} else if (comp2->astOperand2()->isLiteral()) {
op2 = comp2->str();
value2 = comp2->astOperand2()->str();
expr2 = comp2->astOperand1();
} else {
continue;
}
// Only float and int values are currently handled
if (!MathLib::isInt(value1) && !MathLib::isFloat(value1))
continue;
if (!MathLib::isInt(value2) && !MathLib::isFloat(value2))
continue; continue;
if (isSameExpression(_tokenizer, comp1, comp2, _settings->library.functionpure)) if (isSameExpression(_tokenizer, comp1, comp2, _settings->library.functionpure))
@ -727,6 +736,10 @@ void CheckCondition::checkIncorrectLogicOperator()
result1 = checkIntRelation(op1, testvalue, i1); result1 = checkIntRelation(op1, testvalue, i1);
result2 = checkIntRelation(op2, testvalue, i2); result2 = checkIntRelation(op2, testvalue, i2);
} }
if (not1)
result1 = !result1;
if (not2)
result2 = !result2;
if (tok->str() == "&&") { if (tok->str() == "&&") {
alwaysTrue &= (result1 && result2); alwaysTrue &= (result1 && result2);
alwaysFalse &= !(result1 && result2); alwaysFalse &= !(result1 && result2);
@ -738,8 +751,8 @@ void CheckCondition::checkIncorrectLogicOperator()
secondTrue &= !(result1 && !result2); secondTrue &= !(result1 && !result2);
} }
const std::string cond1str = (expr1->isName() ? expr1->str() : "EXPR") + " " + op1 + " " + value1; const std::string cond1str = std::string(not1?"!(":"") + (expr1->isName() ? expr1->str() : "EXPR") + " " + op1 + " " + value1 + std::string(not1?")":"");
const std::string cond2str = (expr2->isName() ? expr2->str() : "EXPR") + " " + op2 + " " + value2; const std::string cond2str = std::string(not2?"!(":"") + (expr2->isName() ? expr2->str() : "EXPR") + " " + op2 + " " + value2 + std::string(not2?")":"");
if (printWarning && (alwaysTrue || alwaysFalse)) { if (printWarning && (alwaysTrue || alwaysFalse)) {
const std::string text = cond1str + " " + tok->str() + " " + cond2str; const std::string text = cond1str + " " + tok->str() + " " + cond2str;
incorrectLogicOperatorError(tok, text, alwaysTrue); incorrectLogicOperatorError(tok, text, alwaysTrue);
@ -758,7 +771,6 @@ void CheckCondition::checkIncorrectLogicOperator()
} }
} }
} }
}
void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always) void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always)
{ {

View File

@ -46,6 +46,7 @@ private:
TEST_CASE(incorrectLogicOperator5); // complex expressions TEST_CASE(incorrectLogicOperator5); // complex expressions
TEST_CASE(incorrectLogicOperator6); // char literals TEST_CASE(incorrectLogicOperator6); // char literals
TEST_CASE(incorrectLogicOperator7); // opposite expressions: (expr || !expr) TEST_CASE(incorrectLogicOperator7); // opposite expressions: (expr || !expr)
TEST_CASE(incorrectLogicOperator8); // !
TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError);
TEST_CASE(incorrectLogicOp_condSwapping); TEST_CASE(incorrectLogicOp_condSwapping);
TEST_CASE(testBug5895); TEST_CASE(testBug5895);
@ -926,6 +927,13 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void incorrectLogicOperator8() { // opposite expressions
check("void f(int i) {\n"
" if (!(i!=10) && !(i!=20)) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: !(i != 10) && !(i != 20).\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"