Skip to content

Commit 54f832a

Browse files
Fix #10569 FN: duplicateExpression with multiple strings compared (#4087)
1 parent 38bdece commit 54f832a

4 files changed

Lines changed: 47 additions & 18 deletions

File tree

lib/checkother.cpp

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,7 @@ void CheckOther::checkConstPointer()
15561556
const ValueType* const vt = tok->valueType();
15571557
if (!vt)
15581558
continue;
1559-
if (vt->pointer != 1 && !(vt->pointer == 2 && var->isArray()) || (vt->constness & 1) || vt->reference != Reference::None)
1559+
if ((vt->pointer != 1 && !(vt->pointer == 2 && var->isArray())) || (vt->constness & 1) || vt->reference != Reference::None)
15601560
continue;
15611561
if (std::find(nonConstPointers.begin(), nonConstPointers.end(), var) != nonConstPointers.end())
15621562
continue;
@@ -2417,13 +2417,17 @@ void CheckOther::checkDuplicateExpression()
24172417
isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2()))
24182418
duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath);
24192419
else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library, true, mTokenizer->isCPP())) {
2420-
const Token *ast1 = tok->astOperand1();
2421-
while (ast1 && tok->str() == ast1->str()) {
2422-
if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath) &&
2423-
isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1()) &&
2420+
auto checkDuplicate = [&](const Token* exp1, const Token* exp2, const Token* ast1) {
2421+
if (isSameExpression(mTokenizer->isCPP(), true, exp1, exp2, mSettings->library, true, true, &errorPath) &&
2422+
isWithoutSideEffects(mTokenizer->isCPP(), exp1) &&
24242423
isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2()))
2425-
// Probably the message should be changed to 'duplicate expressions X in condition or something like that'.
2426-
duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok, errorPath);
2424+
duplicateExpressionError(exp1, exp2, tok, errorPath, /*hasMultipleExpr*/ true);
2425+
};
2426+
const Token *ast1 = tok->astOperand1();
2427+
while (ast1 && tok->str() == ast1->str()) { // chain of identical operators
2428+
checkDuplicate(ast1->astOperand2(), tok->astOperand2(), ast1);
2429+
if (ast1->astOperand1() && ast1->astOperand1()->str() != tok->str()) // check first condition in the chain
2430+
checkDuplicate(ast1->astOperand1(), tok->astOperand2(), ast1);
24272431
ast1 = ast1->astOperand1();
24282432
}
24292433
}
@@ -2452,15 +2456,15 @@ void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors)
24522456
"determine if it is correct.", CWE398, Certainty::normal);
24532457
}
24542458

2455-
void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors)
2459+
void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr)
24562460
{
24572461
errors.emplace_back(opTok, "");
24582462

24592463
const std::string& expr1 = tok1 ? tok1->expressionString() : "x";
24602464
const std::string& expr2 = tok2 ? tok2->expressionString() : "x";
24612465

24622466
const std::string& op = opTok ? opTok->str() : "&&";
2463-
std::string msg = "Same expression on both sides of \'" + op + "\'";
2467+
std::string msg = "Same expression " + (hasMultipleExpr ? "\'" + expr1 + "\'" + " found multiple times in chain of \'" + op + "\' operators" : "on both sides of \'" + op + "\'");
24642468
const char *id = "duplicateExpression";
24652469
if (expr1 != expr2 && (!opTok || !opTok->isArithmeticalOp())) {
24662470
id = "knownConditionTrueFalse";
@@ -2473,9 +2477,9 @@ void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2,
24732477
msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value";
24742478
}
24752479

2476-
reportError(errors, Severity::style, id, msg + ".\n"
2477-
"Finding the same expression on both sides of an operator is suspicious and might "
2478-
"indicate a cut and paste or logic error. Please examine this code carefully to "
2480+
reportError(errors, Severity::style, id, msg +
2481+
(std::string(".\nFinding the same expression ") + (hasMultipleExpr ? "more than once in a condition" : "on both sides of an operator")) +
2482+
" is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to "
24792483
"determine if it is correct.", CWE398, Certainty::normal);
24802484
}
24812485

lib/checkother.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ class CPPCHECKLIB CheckOther : public Check {
260260
void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors);
261261
void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive);
262262
void oppositeExpressionError(const Token *opTok, ErrorPath errors);
263-
void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors);
263+
void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr = false);
264264
void duplicateValueTernaryError(const Token *tok);
265265
void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors);
266266
void duplicateBreakError(const Token *tok, bool inconclusive);

test/testautovariables.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3382,15 +3382,13 @@ class TestAutoVariables : public TestFixture {
33823382
"[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n",
33833383
errout.str());
33843384

3385-
// TODO: Ast is missing for this case
33863385
check("std::vector<int*> f() {\n"
33873386
" int i = 0;\n"
33883387
" std::vector<int*> v{&i, &i};\n"
33893388
" return v;\n"
33903389
"}");
3391-
TODO_ASSERT_EQUALS(
3390+
ASSERT_EQUALS(
33923391
"[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n",
3393-
"",
33943392
errout.str());
33953393

33963394
check("std::vector<int*> f() {\n"

test/testother.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ class TestOther : public TestFixture {
160160
TEST_CASE(duplicateExpression13); // #7899
161161
TEST_CASE(duplicateExpression14); // #9871
162162
TEST_CASE(duplicateExpression15); // #10650
163+
TEST_CASE(duplicateExpression16); // #10569
163164
TEST_CASE(duplicateExpressionLoop);
164165
TEST_CASE(duplicateValueTernary);
165166
TEST_CASE(duplicateExpressionTernary); // #6391
@@ -5125,7 +5126,7 @@ class TestOther : public TestFixture {
51255126
check("void foo() {\n"
51265127
" if (x!=2 || y!=3 || x!=2) {}\n"
51275128
"}");
5128-
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str());
5129+
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression 'x!=2' found multiple times in chain of '||' operators.\n", errout.str());
51295130

51305131
check("void foo() {\n"
51315132
" if (x!=2 && (x=y) && x!=2) {}\n"
@@ -5601,7 +5602,8 @@ class TestOther : public TestFixture {
56015602
" const bool c = a;\n"
56025603
" return a && b && c;\n"
56035604
"}");
5604-
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '&&' because 'a' and 'c' represent the same value.\n", errout.str());
5605+
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression 'a' found multiple times in chain of '&&' operators because 'a' and 'c' represent the same value.\n",
5606+
errout.str());
56055607

56065608
// 6906
56075609
check("void f(const bool b) {\n"
@@ -5785,6 +5787,31 @@ class TestOther : public TestFixture {
57855787
errout.str());
57865788
}
57875789

5790+
void duplicateExpression16() { //#10569
5791+
check("void f(const std::string& a) {\n"
5792+
" if ((a == \"x\") ||\n"
5793+
" (a == \"42\") ||\n"
5794+
" (a == \"y\") ||\n"
5795+
" (a == \"42\")) {}\n"
5796+
"}\n"
5797+
"void g(const std::string& a) {\n"
5798+
" if ((a == \"42\") ||\n"
5799+
" (a == \"x\") ||\n"
5800+
" (a == \"42\") ||\n"
5801+
" (a == \"y\")) {}\n"
5802+
"}\n"
5803+
"void h(const std::string& a) {\n"
5804+
" if ((a == \"42\") ||\n"
5805+
" (a == \"x\") ||\n"
5806+
" (a == \"y\") ||\n"
5807+
" (a == \"42\")) {}\n"
5808+
"}\n");
5809+
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n"
5810+
"[test.cpp:7] -> [test.cpp:9]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n"
5811+
"[test.cpp:13] -> [test.cpp:16]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n",
5812+
errout.str());
5813+
}
5814+
57885815
void duplicateExpressionLoop() {
57895816
check("void f() {\n"
57905817
" int a = 1;\n"

0 commit comments

Comments
 (0)