diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 5eeaafb7dd0..7e8ac25d2fd 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1611,6 +1611,26 @@ static std::string getIncompleteNameID(const Token* tok) } namespace { + int getExprIdForOperand(const Token* tok) { + if (!tok) + return 0; + + int otherExprId = 0; + + // Look through all referenced tokens. + // If two exprIds are found and one matches tok->exprId(), return the other. + // Otherwise, default to returning tok->exprId(). + for (const auto& ref: followAllReferences(tok)) { + const int refExprId = ref.token->exprId(); + if (refExprId != 0 && refExprId != tok->exprId()) { + if (otherExprId != 0 && otherExprId != refExprId) + return tok->exprId(); + otherExprId = refExprId; + } + } + return otherExprId != 0 ? otherExprId : tok->exprId(); + } + struct ExprIdKey { std::string parentOp; nonneg int operand1; @@ -1645,8 +1665,8 @@ namespace { ExprIdKey key; key.parentOp = tok->astParent()->str(); - key.operand1 = op1 ? op1->exprId() : 0; - key.operand2 = op2 ? op2->exprId() : 0; + key.operand1 = getExprIdForOperand(op1); + key.operand2 = getExprIdForOperand(op2); if (tok->astParent()->isCast() && tok->astParent()->str() == "(") { const Token* typeStartToken; @@ -1665,19 +1685,6 @@ namespace { key.parentOp += type; } - for (const auto& ref: followAllReferences(op1)) { - if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm - key.operand1 = ref.token->exprId(); - break; - } - } - for (const auto& ref: followAllReferences(op2)) { - if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm - key.operand2 = ref.token->exprId(); - break; - } - } - if (key.operand1 > key.operand2 && key.operand2 && Token::Match(tok->astParent(), "%or%|%oror%|+|*|&|&&|^|==|!=")) { // In C++ the order of operands of + might matter diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 64b7b6664be..38dff92eebc 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -262,6 +262,7 @@ class TestVarID : public TestFixture { TEST_CASE(exprid12); TEST_CASE(exprid13); TEST_CASE(exprid14); + TEST_CASE(exprid15); TEST_CASE(structuredBindings); } @@ -4518,6 +4519,24 @@ class TestVarID : public TestFixture { ASSERT_EQUALS(exp, tokenize(code, s)); // don't crash } + void exprid15() + { + // #14717 + const char code[] = "#define MAX(a, b) (((a) > (b)) ? (a) : (b))\n" + "\n" + "void f(char *d) {\n" + " const char *p = &d[0];\n" + " int a = 1 / MAX(1, p[0]);\n" + " a = 1 / MAX(1, p[1]);\n" + "}\n"; + const char exp[] = "3: void f ( char * d ) {\n" + "4: const char * p@2 ; p@2 =@UNIQUE &@UNIQUE d@1 [@UNIQUE 0 ] ;\n" + "5: int a@3 ; a@3 =@UNIQUE 1 /@UNIQUE $( $( 1 $>@UNIQUE $( p@2 [@9 0 ] $) $) $?@UNIQUE $( 1 $) $:@UNIQUE $( p@2 [@9 0 ] $) $) ;\n" + "6: a@3 =@UNIQUE 1 /@UNIQUE $( $( 1 $>@UNIQUE $( p@2 [@15 1 ] $) $) $?@UNIQUE $( 1 $) $:@UNIQUE $( p@2 [@15 1 ] $) $) ;\n" + "7: }\n"; + ASSERT_EQUALS(exp, tokenizeExpr(code)); + } + void structuredBindings() { const char code[] = "int foo() { auto [x,y] = xy(); return x+y; }"; ASSERT_EQUALS("1: int foo ( ) { auto [ x@1 , y@2 ] = xy ( ) ; return x@1 + y@2 ; }\n",