Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/astutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,21 @@ bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Se
return true;
if (tok1 == nullptr || tok2 == nullptr)
return false;
// An unknown string-prefix macro leaves the literal outside the AST.
// Compare the literal text as well so identical literals still compare equal.
const bool stringPrefix1 = !tok1->isKeyword() && Token::Match(tok1, "%name% %str%");
const bool stringPrefix2 = !tok2->isKeyword() && Token::Match(tok2, "%name% %str%");
Comment on lines +1612 to +1613

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an AI review. Take it with a grain of salt and feel free to reject it by resolving the comment.

Looks good to me. I built the PR and compared it with the merge base. The false positives are gone for n == SRCDIR "/a" || n == SRCDIR "/b", n != SRCDIR "/a" && n != SRCDIR "/b" and b ? PFX "x" : PFX "y" (duplicateExpression / duplicateExpressionTernary). Real duplicates (b ? PFX "x" : PFX "x") are still reported.

Minor nit: isSameExpression() is a hot function that recurses over whole ASTs, and this adds two Token::Match(..., "%name% %str%") calls to every invocation. A cheap pre-check would avoid the pattern matching in the common case, e.g. tok1->isName() && tok1->next() && tok1->next()->tokType() == Token::eString (and similar for tok2), or just Token::Match only after a tok->next()->isLiteral() check.

if (stringPrefix1 || stringPrefix2) {
if (!stringPrefix1 || !stringPrefix2)
return false;
for (const Token* str1 = tok1->next(), *str2 = tok2->next();
Token::Match(str1, "%str%") || Token::Match(str2, "%str%");
str1 = str1->next(), str2 = str2->next()) {
if (!Token::Match(str1, "%str%") || !Token::Match(str2, "%str%") ||
str1->str() != str2->str() || !compareTokenFlags(str1, str2, macro))
return false;
}
}
// tokens needs to be from the same TokenList so no need check standard on both of them
if (tok1->isCpp()) {
if (tok1->str() == "." && tok1->astOperand1() && tok1->astOperand1()->str() == "this")
Expand Down
11 changes: 11 additions & 0 deletions test/testastutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,22 @@ class TestAstUtils : public TestFixture {
ASSERT_EQUALS(!cpp, isSameExpression("void f() {double y = 1e1; (x + 10.0) < (y + x); } \n", "+", "+", cpp));
ASSERT_EQUALS(true, isSameExpression("void f() {double y = 1e1; double z = 10.0; (x + y) < (x + z); } \n", "+", "+", cpp));
ASSERT_EQUALS(true, isSameExpression("A + A\n", "A", "A", cpp));
// An unknown string-prefix macro leaves the literal outside the AST. #5738
ASSERT_EQUALS(false, isSameExpression("x == PREFIX \"/a\" || x == PREFIX \"/b\";\n", "==", "==", cpp));
ASSERT_EQUALS(false, isSameExpression("x == PREFIX \"/a\" || x == PREFIX;\n", "==", "==", cpp));
ASSERT_EQUALS(false, isSameExpression("x == PREFIX || x == PREFIX \"/a\";\n", "==", "==", cpp));
ASSERT_EQUALS(true, isSameExpression("x == PREFIX \"/a\" || x == PREFIX \"/a\";\n", "==", "==", cpp));
ASSERT_EQUALS(true, isSameExpression("x == PREFIX \"/a\" \"/b\" || x == PREFIX \"/a\" \"/b\";\n", "==", "==", cpp));
ASSERT_EQUALS(false, isSameExpression("x == PREFIX \"/a\" \"/b\" || x == PREFIX \"/a\" \"/c\";\n", "==", "==", cpp));
ASSERT_EQUALS(false, isSameExpression("x == FIRST \"/a\" || x == SECOND \"/a\";\n", "==", "==", cpp));
ASSERT_EQUALS(false, isSameExpression("x == PREFIX L\"/a\" || x == PREFIX \"/a\";\n", "==", "==", cpp));

// the remaining test cases are not valid C code
if (!cpp)
return;

ASSERT_EQUALS(true, isSameExpression("void f(int x) { x ? throw \"a\" : throw \"a\"; }\n", "throw", "throw", cpp));

//https://trac.cppcheck.net/ticket/9700
ASSERT_EQUALS(true, isSameExpression("A::B + A::B;\n", "::", "::", cpp));
ASSERT_EQUALS(false, isSameExpression("A::B + A::C;\n", "::", "::", cpp));
Expand Down
12 changes: 12 additions & 0 deletions test/testother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12163,6 +12163,18 @@ class TestOther : public TestFixture {
" return name.startswith(SRCDIR \"/com/\") || name.startswith(SRCDIR \"/uno/\");\n"
"};\n", dinit(CheckOptions, $.inconclusive = false));
ASSERT_EQUALS("", errout_str());

check("bool isInUnoIncludeFile(StringRef name) {\n"
" return isInMainFile()\n"
" ? (name == SRCDIR \"/cppu/compat.cxx\" || name == SRCDIR \"/sal/compat.cxx\")\n"
" : (name.startswith(SRCDIR \"/com/\") || name.startswith(SRCDIR \"/uno/\"));\n"
"}\n", dinit(CheckOptions, $.inconclusive = false));
ASSERT_EQUALS("", errout_str());

check("bool f(StringRef name) {\n"
" return name == SRCDIR \"/a\" || name == SRCDIR \"/a\";\n"
"}\n", dinit(CheckOptions, $.inconclusive = false));
ASSERT_EQUALS("[test.cpp:2:32]: (style) Same expression on both sides of '||'. [duplicateExpression]\n", errout_str());
}

void raceAfterInterlockedDecrement() {
Expand Down
Loading