Fix issue 8413: Condition is always false 'i=expr; if (i != expr) {}' (#1295)

* Follow variables when comparing same expression

* Remove assert include

* Dont follow function arguments

* Improve the checking to check more cases

* Add more tests

* Check if the variable is used inside a loop

* Follow both variables

* Only skip loops when variable is modified in scope

* Fix FP when followed variable is modified

* Dont follow arrays

* Skip pointer indirection

* Make recursive

* Improve checking more variables

* Fix test with sizeof

* Skip following operators

* Fix test when using sizeof

* Dont check every step

* Use early returns

* Update test to use a loop instead of conditional

* Add static

* Check variables are global

* Check local variables in another scope

* Fix issue with const pointers

* Distinguish between pointer indirection and multiply

* Use simple match

* Prevent crash with uniform initialization

* Use unary op and ast to detect pointer indirection

* Expand error message when expression do not match exactly

* Add errorpath to issameexpression

* Revert "Clarify warning message for 'Same expression on both sides of operator'"

This reverts commit 0e491b41a8.

* Check if the tokens are the same

* Report the operator and not the expressions
This commit is contained in:
Paul Fultz II 2018-08-07 02:32:16 -05:00 committed by Daniel Marjamäki
parent 20da3d2b46
commit f603b529df
6 changed files with 352 additions and 35 deletions

View File

@ -133,9 +133,96 @@ const Token * astIsVariableComparison(const Token *tok, const std::string &comp,
return ret;
}
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure)
static const Token * getVariableInitExpression(const Variable * var)
{
if (tok1 == nullptr && tok2 == nullptr)
if(!var || !var->declEndToken())
return nullptr;
if(Token::Match(var->declEndToken(), "; %varid% =", var->declarationId()))
return var->declEndToken()->tokAt(2)->astOperand2();
return var->declEndToken()->astOperand2();
}
static bool isInLoopCondition(const Token * tok) {
return Token::Match(tok->astTop()->previous(), "for|while (");
}
/// This takes a token that refers to a variable and it will return the token
/// to the expression that the variable is assigned to. If its not valid to
/// make such substitution then it will return the original token.
static const Token * followVariableExpression(const Token * tok, bool cpp)
{
if(!tok)
return tok;
// Skip array access
if(Token::Match(tok, "%var% ["))
return tok;
// Skip pointer indirection
if(tok->astParent() && tok->isUnaryOp("*"))
return tok;
// Skip following variables if it is used in an assignment
if(Token::Match(tok->astParent(), "%assign%") || Token::Match(tok->next(), "%assign%"))
return tok;
const Variable * var = tok->variable();
const Token * varTok = getVariableInitExpression(var);
if(!varTok)
return tok;
// Skip array access
if(Token::simpleMatch(varTok, "["))
return tok;
if(!var->isLocal() && !var->isConst())
return tok;
if(var->isStatic() && !var->isConst())
return tok;
if(var->isArgument())
return tok;
// If this is in a loop then check if variables are modified in the entire scope
const Token * endToken = (isInLoopCondition(tok) || var->scope() != tok->scope()) ? var->scope()->bodyEnd : tok;
if (!var->isConst() && isVariableChanged(varTok, endToken, tok->varId(), false, nullptr, cpp))
return tok;
// Start at begining of initialization
const Token * startToken = varTok;
while(Token::Match(startToken, "%op%|.|(|{") && startToken->astOperand1())
startToken = startToken->astOperand1();
// Skip if the variable its referring to is modified
for(const Token * tok2 = startToken;tok2 != endToken;tok2 = tok2->next()) {
if(Token::simpleMatch(tok2, ";"))
break;
if(tok2->astParent() && tok2->isUnaryOp("*"))
return tok;
if (tok2->tokType() == Token::eIncDecOp ||
tok2->isAssignmentOp() ||
Token::Match(tok2, "%name% .|[|++|--|%assign%")) {
return tok;
}
if(const Variable * var2 = tok2->variable()) {
const Token * endToken2 = var2->scope() != tok->scope() ? var2->scope()->bodyEnd : endToken;
if(!var2->isLocal() && !var2->isConst() && !var2->isArgument())
return tok;
if(var2->isStatic() && !var2->isConst())
return tok;
if(!var2->isConst() && isVariableChanged(tok2, endToken2, tok2->varId(), false, nullptr, cpp))
return tok;
}
}
return varTok;
}
static void followVariableExpressionError(const Token *tok1, const Token *tok2, ErrorPath* errors)
{
if(!errors)
return;
if(!tok1)
return;
if(!tok2)
return;
errors->push_back(std::make_pair(tok2, "'" + tok1->str() + "' is assigned value '" + tok2->expressionString() + "' here."));
}
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, ErrorPath* errors)
{
if (tok1 == nullptr && tok2 == nullptr)
return true;
if (tok1 == nullptr || tok2 == nullptr)
return false;
@ -145,17 +232,36 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2
if (tok2->str() == "." && tok2->astOperand1() && tok2->astOperand1()->str() == "this")
tok2 = tok2->astOperand2();
}
// Skip double not
if (Token::simpleMatch(tok1, "!") && Token::simpleMatch(tok1->astOperand1(), "!") && !Token::simpleMatch(tok1->astParent(), "=")) {
return isSameExpression(cpp, macro, tok1->astOperand1()->astOperand1(), tok2, library, pure);
return isSameExpression(cpp, macro, tok1->astOperand1()->astOperand1(), tok2, library, pure, errors);
}
if (Token::simpleMatch(tok2, "!") && Token::simpleMatch(tok2->astOperand1(), "!") && !Token::simpleMatch(tok2->astParent(), "=")) {
return isSameExpression(cpp, macro, tok1, tok2->astOperand1()->astOperand1(), library, pure);
return isSameExpression(cpp, macro, tok1, tok2->astOperand1()->astOperand1(), library, pure, errors);
}
// Follow variables if possible
if(tok1->str() != tok2->str() && (Token::Match(tok1, "%var%") || Token::Match(tok2, "%var%"))) {
const Token * varTok1 = followVariableExpression(tok1, cpp);
if (varTok1->str() == tok2->str()) {
followVariableExpressionError(tok1, varTok1, errors);
return isSameExpression(cpp, macro, varTok1, tok2, library, pure, errors);
}
const Token * varTok2 = followVariableExpression(tok2, cpp);
if(tok1->str() == varTok2->str()) {
followVariableExpressionError(tok2, varTok2, errors);
return isSameExpression(cpp, macro, tok1, varTok2, library, pure, errors);
}
if(varTok1->str() == varTok2->str()) {
followVariableExpressionError(tok1, varTok1, errors);
followVariableExpressionError(tok2, varTok2, errors);
return isSameExpression(cpp, macro, varTok1, varTok2, library, pure, errors);
}
}
if (tok1->varId() != tok2->varId() || tok1->str() != tok2->str() || tok1->originalName() != tok2->originalName()) {
if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) ||
(Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) {
return isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure) &&
isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure);
return isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, errors) &&
isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, errors);
}
return false;
}
@ -233,9 +339,9 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2
return false;
}
bool noncommutativeEquals =
isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand1(), library, pure);
isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand1(), library, pure, errors);
noncommutativeEquals = noncommutativeEquals &&
isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand2(), library, pure);
isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand2(), library, pure, errors);
if (noncommutativeEquals)
return true;
@ -250,9 +356,9 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2
const bool commutative = tok1->isBinaryOp() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!=");
bool commutativeEquals = commutative &&
isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure);
isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, errors);
commutativeEquals = commutativeEquals &&
isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure);
isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, errors);
return commutativeEquals;
@ -626,7 +732,7 @@ bool isVariableChangedByFunctionCall(const Token *tok, const Settings *settings,
if (!tok->function()) {
// if the library says 0 is invalid
// => it is assumed that parameter is an in parameter (TODO: this is a bad heuristic)
if (!addressOf && settings->library.isnullargbad(tok, 1+argnr))
if (!addressOf && settings && settings->library.isnullargbad(tok, 1+argnr))
return false;
// addressOf => inconclusive
if (!addressOf) {
@ -639,8 +745,13 @@ bool isVariableChangedByFunctionCall(const Token *tok, const Settings *settings,
const Variable *arg = tok->function()->getArgumentVar(argnr);
if (addressOf && !(arg && arg->isConst()))
return true;
if (addressOf) {
if(!(arg && arg->isConst()))
return true;
// If const is applied to the pointer, then the value can still be modified
if(arg && Token::simpleMatch(arg->typeEndToken(), "* const"))
return true;
}
return arg && !arg->isConst() && arg->isReference();
}

View File

@ -25,6 +25,8 @@
#include <string>
#include <vector>
#include "errorlogger.h"
class Library;
class Settings;
class Token;
@ -54,7 +56,7 @@ std::string astCanonicalType(const Token *expr);
/** Is given syntax tree a variable comparison against value */
const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr);
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure);
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, ErrorPath* errors=nullptr);
bool isEqualKnownValue(const Token * const tok1, const Token * const tok2);

View File

@ -1981,10 +1981,11 @@ void CheckOther::checkDuplicateExpression()
}
}
}
ErrorPath errorPath;
if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>|+=|*=|<<=|>>=")) {
if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true))
continue;
if (isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true)) {
if (isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, &errorPath)) {
if (isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) {
const bool assignment = tok->str() == "=";
if (assignment && warningEnabled)
@ -1999,7 +2000,7 @@ void CheckOther::checkDuplicateExpression()
continue;
}
}
duplicateExpressionError(tok, tok, tok->str());
duplicateExpressionError(tok->astOperand1(), tok->astOperand2(), tok, errorPath);
}
}
} else if (styleEnabled &&
@ -2008,17 +2009,17 @@ void CheckOther::checkDuplicateExpression()
isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) {
oppositeExpressionError(tok, tok, tok->str());
} else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative
if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), tok->astOperand1()->astOperand2(), mSettings->library, true) && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2()))
duplicateExpressionError(tok->astOperand2(), tok->astOperand2(), tok->str());
if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), tok->astOperand1()->astOperand2(), mSettings->library, true, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2()))
duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath);
else if (tok->astOperand2()) {
const Token *ast1 = tok->astOperand1();
while (ast1 && tok->str() == ast1->str()) {
if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1()))
if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1()))
// TODO: warn if variables are unchanged. See #5683
// Probably the message should be changed to 'duplicate expressions X in condition or something like that'.
;//duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok->str());
else if (styleEnabled && isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand2(), tok->astOperand2(), mSettings->library, true) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2()))
duplicateExpressionError(ast1->astOperand2(), tok->astOperand2(), tok->str());
;//duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok, errorPath);
else if (styleEnabled && isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand2(), tok->astOperand2(), mSettings->library, true, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2()))
duplicateExpressionError(ast1->astOperand2(), tok->astOperand2(), tok, errorPath);
if (!isConstExpression(ast1->astOperand2(), mSettings->library, true))
break;
ast1 = ast1->astOperand1();
@ -2045,13 +2046,19 @@ void CheckOther::oppositeExpressionError(const Token *tok1, const Token *tok2, c
"determine if it is correct.", CWE398, false);
}
void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op)
void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors)
{
std::list<const Token *> toks = { tok1 };
if (tok1 != tok2)
toks.push_front(tok2);
errors.emplace_back(opTok, "");
reportError(toks, Severity::style, "duplicateExpression", "Same expression on both sides of \'" + op + "\'.\n"
const std::string& expr1 = tok1 ? tok1->expressionString() : "x";
const std::string& expr2 = tok2 ? tok2->expressionString() : "x";
std::string endingPhrase = expr1 == expr2 ? "." :
" because the value of '" + expr1 + "' and '" + expr2 + "' are the same.";
const std::string& op = opTok ? opTok->str() : "&&";
reportError(errors, Severity::style, "duplicateExpression", "Same expression on both sides of \'" + op + "\'" + endingPhrase + "\n"
"Finding the same expression on both sides of an operator is suspicious and might "
"indicate a cut and paste or logic error. Please examine this code carefully to "
"determine if it is correct.", CWE398, false);

View File

@ -240,7 +240,7 @@ private:
void duplicateBranchError(const Token *tok1, const Token *tok2);
void duplicateAssignExpressionError(const Token *tok1, const Token *tok2);
void oppositeExpressionError(const Token *tok1, const Token *tok2, const std::string &op);
void duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op);
void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors);
void duplicateValueTernaryError(const Token *tok);
void duplicateExpressionTernaryError(const Token *tok);
void duplicateBreakError(const Token *tok, bool inconclusive);
@ -267,6 +267,8 @@ private:
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override {
CheckOther c(nullptr, settings, errorLogger);
ErrorPath errorPath;
// error
c.zerodivError(nullptr, nullptr);
c.misusedScopeObjectError(nullptr, "varname");
@ -301,7 +303,7 @@ private:
c.clarifyStatementError(nullptr);
c.duplicateBranchError(nullptr, nullptr);
c.oppositeExpressionError(nullptr, nullptr, "&&");
c.duplicateExpressionError(nullptr, nullptr, "&&");
c.duplicateExpressionError(nullptr, nullptr, nullptr, errorPath);
c.duplicateValueTernaryError(nullptr);
c.duplicateExpressionTernaryError(nullptr);
c.duplicateBreakError(nullptr, false);

View File

@ -1555,7 +1555,7 @@ private:
" if(!b) {}\n"
" }\n"
"}");
TODO_ASSERT_EQUALS("error", "", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str());
check("void foo(unsigned u) {\n"
" if (u != 0) {\n"

View File

@ -130,6 +130,9 @@ private:
TEST_CASE(duplicateExpression4); // ticket #3354 (++)
TEST_CASE(duplicateExpression5); // ticket #3749 (macros with same values)
TEST_CASE(duplicateExpression6); // ticket #4639
TEST_CASE(duplicateExpression7);
TEST_CASE(duplicateExpression8);
TEST_CASE(duplicateExpressionLoop);
TEST_CASE(duplicateValueTernary);
TEST_CASE(duplicateExpressionTernary); // #6391
TEST_CASE(duplicateExpressionTemplate); // #6930
@ -483,7 +486,7 @@ private:
" double fStepHelp = 0;\n"
" if( (rOuterValue >>= fStepHelp) ) {\n"
" if( fStepHelp != 0.0) {\n"
" double fStepMain = 0;\n"
" double fStepMain = 1;\n"
" sal_Int32 nIntervalCount = static_cast< sal_Int32 >(fStepMain / fStepHelp);\n"
" }\n"
" }\n"
@ -3530,12 +3533,12 @@ private:
check("void foo(int a, int b) {\n"
" if ((a < b) && (b > a)) { }\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because the value of 'a<b' and 'b>a' are the same.\n", errout.str());
check("void foo(int a, int b) {\n"
" if ((a <= b) && (b >= a)) { }\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because the value of 'a<=b' and 'b>=a' are the same.\n", errout.str());
check("void foo() {\n"
" if (x!=2 || y!=3 || x!=2) {}\n"
@ -3659,7 +3662,7 @@ private:
check("void foo(int a, int b) {\n"
" if ((b + a) | (a + b)) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|' because the value of 'b+a' and 'a+b' are the same.\n", errout.str());
check("void foo(const std::string& a, const std::string& b) {\n"
" return a.find(b+\"&\") || a.find(\"&\"+b);\n"
@ -3674,7 +3677,7 @@ private:
check("void foo(double a, double b) {\n"
" if ((b + a) > (a + b)) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '>'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '>' because the value of 'b+a' and 'a+b' are the same.\n", errout.str());
check("void f(int x) {\n"
" if ((x == 1) && (x == 0x00000001))\n"
@ -3908,6 +3911,198 @@ private:
ASSERT_EQUALS("", errout.str());
}
void duplicateExpression7() {
check("void f() {\n"
" const int i = sizeof(int);\n"
" if ( i != sizeof (int)){}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '!=' because the value of 'i' and 'sizeof(int)' are the same.\n", errout.str());
check("void f() {\n"
" const int i = sizeof(int);\n"
" if ( sizeof (int) != i){}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '!=' because the value of 'sizeof(int)' and 'i' are the same.\n", errout.str());
check("void f(int a = 1) { if ( a != 1){}}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int a = 1;\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '!=' because the value of 'a' and '1' are the same.\n", errout.str());
check("void f() {\n"
" int a = 1;\n"
" int b = 1;\n"
" if ( a != b){} \n"
"}\n");
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:4]: (style) Same expression on both sides of '!=' because the value of 'a' and 'b' are the same.\n", errout.str());
check("void f() {\n"
" int a = 1;\n"
" int b = a;\n"
" if ( a != b){} \n"
"}\n");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Same expression on both sides of '!=' because the value of 'a' and 'b' are the same.\n", errout.str());
check("void use(int);\n"
"void f() {\n"
" int a = 1;\n"
" int b = 1;\n"
" use(b);\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Same expression on both sides of '!=' because the value of 'a' and '1' are the same.\n", errout.str());
check("void use(int);\n"
"void f() {\n"
" int a = 1;\n"
" use(a);\n"
" a = 2;\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void use(int);\n"
"void f() {\n"
" int a = 2;\n"
" use(a);\n"
" a = 1;\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("const int a = 1;\n"
" void f() {\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) Same expression on both sides of '!=' because the value of 'a' and '1' are the same.\n", errout.str());
check("int a = 1;\n"
" void f() {\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" static const int a = 1;\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '!=' because the value of 'a' and '1' are the same.\n", errout.str());
check("void f() {\n"
" static int a = 1;\n"
" if ( a != 1){} \n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int a = 1;\n"
" if ( a != 1){\n"
" a++;\n"
" }}\n");
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '!=' because the value of 'a' and '1' are the same.\n", errout.str());
check("void f(int b) {\n"
" int a = 1;\n"
" while (b) {\n"
" if ( a != 1){}\n"
" a++;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void duplicateExpression8() {
check("void f() {\n"
" int a = 1;\n"
" int b = a;\n"
" a = 2;\n"
" if ( b != a){}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(int * a, int i) { int b = a[i]; a[i] = 2; if ( b != a[i]){}}\n");
ASSERT_EQUALS("", errout.str());
check("void f(int * a, int i) { int b = *a; *a = 2; if ( b != *a){}}\n");
ASSERT_EQUALS("", errout.str());
check("struct A { int f() const; };\n"
"A g();\n"
"void foo() {\n"
" for (const A x = A();;) {\n"
" const int a = x.f();\n"
" x = g();\n"
" if (x.f() == a) break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("int f(int i);\n"
"struct A {\n"
" enum E { B, C };\n"
" bool f(E);\n"
"};\n"
"void foo() {\n"
" A a;\n"
" const bool x = a.f(A::B);\n"
" const bool y = a.f(A::C);\n"
" if(!x && !y) return;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void foo() { \n"
" const bool x = a.f(A::B);\n"
" const bool y = a.f(A::C);\n"
" if (!x && !y) return;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(bool * const b);\n"
"void foo() { \n"
" bool x = true;\n"
" bool y = true;\n"
" f(&x);\n"
" if (!x && !y) return;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" const int a = {};\n"
" if(a == 1) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void duplicateExpressionLoop() {
check("void f() {\n"
" int a = 1;\n"
" while ( a != 1){}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '!=' because the value of 'a' and '1' are the same.\n", errout.str());
check("void f() { int a = 1; while ( a != 1){ a++; }}\n");
ASSERT_EQUALS("", errout.str());
check("void f() { int a = 1; for ( int i=0; i < 3 && a != 1; i++){ a++; }}\n");
ASSERT_EQUALS("", errout.str());
check("void f(int b) { int a = 1; while (b) { if ( a != 1){} b++; } a++; } \n");
ASSERT_EQUALS("", errout.str());
check("void f(int b) {\n"
" while (b) {\n"
" int a = 1;\n"
" if ( a != 1){}\n"
" b++;\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Same expression on both sides of '!=' because the value of 'a' and '1' are the same.\n", errout.str());
}
void duplicateExpressionTernary() { // #6391
check("void f() {\n"
" return A ? x : x;\n"