From f9e2a86b2e852dbed027e6aea5b48f32f2415b13 Mon Sep 17 00:00:00 2001 From: Nicolas van Kempen Date: Tue, 20 Aug 2024 11:44:13 -0400 Subject: [PATCH] [clang][ASTMatcher] Fix execution order of hasOperands submatchers (#104148) `hasOperands` does not always execute matchers in the order they are written. This can cause issue in code using bindings when one operand matcher is relying on a binding set by the other. With this change, the first matcher present in the code is always executed first and any binding it sets are available to the second matcher. Simple example with current version (1 match) and new version (2 matches): ```bash > cat tmp.cpp int a = 13; int b = ((int) a) - a; int c = a - ((int) a); > clang-query tmp.cpp clang-query> set traversal IgnoreUnlessSpelledInSource clang-query> m binaryOperator(hasOperands(cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))), declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d")))))) Match #1: tmp.cpp:1:1: note: "d" binds here int a = 13; ^~~~~~~~~~ tmp.cpp:2:9: note: "root" binds here int b = ((int)a) - a; ^~~~~~~~~~~~ 1 match. > ./build/bin/clang-query tmp.cpp clang-query> set traversal IgnoreUnlessSpelledInSource clang-query> m binaryOperator(hasOperands(cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))), declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d")))))) Match #1: tmp.cpp:1:1: note: "d" binds here 1 | int a = 13; | ^~~~~~~~~~ tmp.cpp:2:9: note: "root" binds here 2 | int b = ((int)a) - a; | ^~~~~~~~~~~~ Match #2: tmp.cpp:1:1: note: "d" binds here 1 | int a = 13; | ^~~~~~~~~~ tmp.cpp:3:9: note: "root" binds here 3 | int c = a - ((int)a); | ^~~~~~~~~~~~ 2 matches. ``` If this should be documented or regression tested anywhere please let me know where. --- clang/docs/ReleaseNotes.rst | 3 +++ clang/include/clang/ASTMatchers/ASTMatchers.h | 2 +- .../ASTMatchers/ASTMatchersTraversalTest.cpp | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e3fac712de4662..0167e7f1e8e3de 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -388,6 +388,9 @@ AST Matchers - Fixed an issue with the `hasName` and `hasAnyName` matcher when matching inline namespaces with an enclosing namespace of the same name. +- Fixed an ordering issue with the `hasOperands` matcher occuring when setting a + binding in the first matcher and using it in the second matcher. + clang-format ------------ diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index ca44c3ee085654..f1c72efc238784 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -6027,7 +6027,7 @@ AST_POLYMORPHIC_MATCHER_P2( internal::Matcher, Matcher1, internal::Matcher, Matcher2) { return internal::VariadicDynCastAllOfMatcher()( anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)), - allOf(hasLHS(Matcher2), hasRHS(Matcher1)))) + allOf(hasRHS(Matcher1), hasLHS(Matcher2)))) .matches(Node, Finder, Builder); } diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index 47a71134d50273..028392f499da3b 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1745,6 +1745,18 @@ TEST(MatchBinaryOperator, HasOperands) { EXPECT_TRUE(notMatches("void x() { 0 + 1; }", HasOperands)); } +TEST(MatchBinaryOperator, HasOperandsEnsureOrdering) { + StatementMatcher HasOperandsWithBindings = binaryOperator(hasOperands( + cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))), + declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d")))))); + EXPECT_TRUE(matches( + "int a; int b = ((int) a) + a;", + traverse(TK_IgnoreUnlessSpelledInSource, HasOperandsWithBindings))); + EXPECT_TRUE(matches( + "int a; int b = a + ((int) a);", + traverse(TK_IgnoreUnlessSpelledInSource, HasOperandsWithBindings))); +} + TEST(Matcher, BinaryOperatorTypes) { // Integration test that verifies the AST provides all binary operators in // a way we expect.