Fix #10569 FN: duplicateExpression with multiple strings compared (#4087)

This commit is contained in:
chrchr-github 2022-05-09 21:05:35 +02:00 committed by GitHub
parent 38bdece3fe
commit 54f832a2fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 18 deletions

View File

@ -1556,7 +1556,7 @@ void CheckOther::checkConstPointer()
const ValueType* const vt = tok->valueType(); const ValueType* const vt = tok->valueType();
if (!vt) if (!vt)
continue; continue;
if (vt->pointer != 1 && !(vt->pointer == 2 && var->isArray()) || (vt->constness & 1) || vt->reference != Reference::None) if ((vt->pointer != 1 && !(vt->pointer == 2 && var->isArray())) || (vt->constness & 1) || vt->reference != Reference::None)
continue; continue;
if (std::find(nonConstPointers.begin(), nonConstPointers.end(), var) != nonConstPointers.end()) if (std::find(nonConstPointers.begin(), nonConstPointers.end(), var) != nonConstPointers.end())
continue; continue;
@ -2417,13 +2417,17 @@ void CheckOther::checkDuplicateExpression()
isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2())) isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2()))
duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath); duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath);
else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library, true, mTokenizer->isCPP())) { else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library, true, mTokenizer->isCPP())) {
const Token *ast1 = tok->astOperand1(); auto checkDuplicate = [&](const Token* exp1, const Token* exp2, const Token* ast1) {
while (ast1 && tok->str() == ast1->str()) { if (isSameExpression(mTokenizer->isCPP(), true, exp1, exp2, mSettings->library, true, true, &errorPath) &&
if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), exp1) &&
isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1()) &&
isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2())) isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2()))
// Probably the message should be changed to 'duplicate expressions X in condition or something like that'. duplicateExpressionError(exp1, exp2, tok, errorPath, /*hasMultipleExpr*/ true);
duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok, errorPath); };
const Token *ast1 = tok->astOperand1();
while (ast1 && tok->str() == ast1->str()) { // chain of identical operators
checkDuplicate(ast1->astOperand2(), tok->astOperand2(), ast1);
if (ast1->astOperand1() && ast1->astOperand1()->str() != tok->str()) // check first condition in the chain
checkDuplicate(ast1->astOperand1(), tok->astOperand2(), ast1);
ast1 = ast1->astOperand1(); ast1 = ast1->astOperand1();
} }
} }
@ -2452,7 +2456,7 @@ void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors)
"determine if it is correct.", CWE398, Certainty::normal); "determine if it is correct.", CWE398, Certainty::normal);
} }
void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors) void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr)
{ {
errors.emplace_back(opTok, ""); errors.emplace_back(opTok, "");
@ -2460,7 +2464,7 @@ void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2,
const std::string& expr2 = tok2 ? tok2->expressionString() : "x"; const std::string& expr2 = tok2 ? tok2->expressionString() : "x";
const std::string& op = opTok ? opTok->str() : "&&"; const std::string& op = opTok ? opTok->str() : "&&";
std::string msg = "Same expression on both sides of \'" + op + "\'"; std::string msg = "Same expression " + (hasMultipleExpr ? "\'" + expr1 + "\'" + " found multiple times in chain of \'" + op + "\' operators" : "on both sides of \'" + op + "\'");
const char *id = "duplicateExpression"; const char *id = "duplicateExpression";
if (expr1 != expr2 && (!opTok || !opTok->isArithmeticalOp())) { if (expr1 != expr2 && (!opTok || !opTok->isArithmeticalOp())) {
id = "knownConditionTrueFalse"; id = "knownConditionTrueFalse";
@ -2473,9 +2477,9 @@ void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2,
msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value"; msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value";
} }
reportError(errors, Severity::style, id, msg + ".\n" reportError(errors, Severity::style, id, msg +
"Finding the same expression on both sides of an operator is suspicious and might " (std::string(".\nFinding the same expression ") + (hasMultipleExpr ? "more than once in a condition" : "on both sides of an operator")) +
"indicate a cut and paste or logic error. Please examine this code carefully to " " is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to "
"determine if it is correct.", CWE398, Certainty::normal); "determine if it is correct.", CWE398, Certainty::normal);
} }

View File

@ -260,7 +260,7 @@ private:
void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors); void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors);
void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive); void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive);
void oppositeExpressionError(const Token *opTok, ErrorPath errors); void oppositeExpressionError(const Token *opTok, ErrorPath errors);
void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors); void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr = false);
void duplicateValueTernaryError(const Token *tok); void duplicateValueTernaryError(const Token *tok);
void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors); void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors);
void duplicateBreakError(const Token *tok, bool inconclusive); void duplicateBreakError(const Token *tok, bool inconclusive);

View File

@ -3382,15 +3382,13 @@ private:
"[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", "[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",
errout.str()); errout.str());
// TODO: Ast is missing for this case
check("std::vector<int*> f() {\n" check("std::vector<int*> f() {\n"
" int i = 0;\n" " int i = 0;\n"
" std::vector<int*> v{&i, &i};\n" " std::vector<int*> v{&i, &i};\n"
" return v;\n" " return v;\n"
"}"); "}");
TODO_ASSERT_EQUALS( ASSERT_EQUALS(
"[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", "[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",
"",
errout.str()); errout.str());
check("std::vector<int*> f() {\n" check("std::vector<int*> f() {\n"

View File

@ -160,6 +160,7 @@ private:
TEST_CASE(duplicateExpression13); // #7899 TEST_CASE(duplicateExpression13); // #7899
TEST_CASE(duplicateExpression14); // #9871 TEST_CASE(duplicateExpression14); // #9871
TEST_CASE(duplicateExpression15); // #10650 TEST_CASE(duplicateExpression15); // #10650
TEST_CASE(duplicateExpression16); // #10569
TEST_CASE(duplicateExpressionLoop); TEST_CASE(duplicateExpressionLoop);
TEST_CASE(duplicateValueTernary); TEST_CASE(duplicateValueTernary);
TEST_CASE(duplicateExpressionTernary); // #6391 TEST_CASE(duplicateExpressionTernary); // #6391
@ -5125,7 +5126,7 @@ private:
check("void foo() {\n" check("void foo() {\n"
" if (x!=2 || y!=3 || x!=2) {}\n" " if (x!=2 || y!=3 || x!=2) {}\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression 'x!=2' found multiple times in chain of '||' operators.\n", errout.str());
check("void foo() {\n" check("void foo() {\n"
" if (x!=2 && (x=y) && x!=2) {}\n" " if (x!=2 && (x=y) && x!=2) {}\n"
@ -5601,7 +5602,8 @@ private:
" const bool c = a;\n" " const bool c = a;\n"
" return a && b && c;\n" " return a && b && c;\n"
"}"); "}");
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()); 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",
errout.str());
// 6906 // 6906
check("void f(const bool b) {\n" check("void f(const bool b) {\n"
@ -5785,6 +5787,31 @@ private:
errout.str()); errout.str());
} }
void duplicateExpression16() { //#10569
check("void f(const std::string& a) {\n"
" if ((a == \"x\") ||\n"
" (a == \"42\") ||\n"
" (a == \"y\") ||\n"
" (a == \"42\")) {}\n"
"}\n"
"void g(const std::string& a) {\n"
" if ((a == \"42\") ||\n"
" (a == \"x\") ||\n"
" (a == \"42\") ||\n"
" (a == \"y\")) {}\n"
"}\n"
"void h(const std::string& a) {\n"
" if ((a == \"42\") ||\n"
" (a == \"x\") ||\n"
" (a == \"y\") ||\n"
" (a == \"42\")) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n"
"[test.cpp:7] -> [test.cpp:9]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n"
"[test.cpp:13] -> [test.cpp:16]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n",
errout.str());
}
void duplicateExpressionLoop() { void duplicateExpressionLoop() {
check("void f() {\n" check("void f() {\n"
" int a = 1;\n" " int a = 1;\n"