diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index 7d41cb30f..e6b371e8c 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -607,6 +607,12 @@ void CheckCondition::checkIncorrectLogicOperator() } else if (Token::Match(tok, "&&|%oror%")) { + if (tok->str() == "||" && tok->astOperand1() && tok->astOperand2() && tok->astOperand2()->str() == "&&") { + const Token* tok2 = tok->astOperand2()->astOperand1(); + if (isOppositeCond(tok->astOperand1(), tok2, _settings->library.functionpure)) { + redundantConditionError(tok, tok2->expressionString() + ". 'A && (!A || B)' is equivalent to 'A || B'"); + } + } // Comparison #1 (LHS) const Token *comp1 = tok->astOperand1(); if (comp1 && comp1->str() == tok->str()) diff --git a/lib/token.cpp b/lib/token.cpp index 18a18f600..f8f652090 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1178,7 +1178,7 @@ std::string Token::expressionString() const { const Token * const top = this; const Token *start = top; - while (start->astOperand1() && start->astOperand2()) + while (start->astOperand1() && (start->astOperand2() || Token::simpleMatch(start, "( )"))) start = start->astOperand1(); const Token *end = top; while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) { diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 37af7b22d..81fb0845a 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -1281,6 +1281,60 @@ private: " if (x = b < 0 ? 1 : 2) {}\n" // don't simplify and verify this code "}", false); ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " int y = rand(), z = rand();\n" + " if (y || (!y && z));\n" + "}", false); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'A && (!A || B)' is equivalent to 'A || B'\n", errout.str()); + + check("void f() {\n" + " int y = rand(), z = rand();\n" + " if (y || !y && z);\n" + "}", false); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'A && (!A || B)' is equivalent to 'A || B'\n", errout.str()); + + check("void f() {\n" + " if (!a || a && b) {}\n" + "}", false); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. 'A && (!A || B)' is equivalent to 'A || B'\n", errout.str()); + + + check("void f() {\n" + " if (!tok->next()->function() || \n" + " (tok->next()->function() && tok->next()->function()->isConstructor()));\n" + "}", false); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok.next().function(). 'A && (!A || B)' is equivalent to 'A || B'\n", errout.str()); + + check("void f() {\n" + " if (!tok->next()->function() || \n" + " (!tok->next()->function() && tok->next()->function()->isConstructor()));\n" + "}", false); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " if (!tok->next()->function() || \n" + " (!tok2->next()->function() && tok->next()->function()->isConstructor()));\n" + "}", false); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " if (!tok->next(1)->function(1) || \n" + " (tok->next(1)->function(1) && tok->next(1)->function(1)->isConstructor()));\n" + "}", false); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok.next(1).function(1). 'A && (!A || B)' is equivalent to 'A || B'\n", errout.str()); + + check("void f() {\n" + " if (!tok->next()->function(1) || \n" + " (tok->next()->function(2) && tok->next()->function()->isConstructor()));\n" + "}", false); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " int y = rand(), z = rand();\n" + " if (y==0 || y!=0 && z);\n" + "}", false); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: y. 'A && (!A || B)' is equivalent to 'A || B'\n", errout.str()); } // clarify conditions with bitwise operator and comparison