[clang] f9e2a86 - [clang][ASTMatcher] Fix execution order of hasOperands submatchers (#104148)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 20 08:44:17 PDT 2024
Author: Nicolas van Kempen
Date: 2024-08-20T11:44:13-04:00
New Revision: f9e2a86b2e852dbed027e6aea5b48f32f2415b13
URL: https://github.com/llvm/llvm-project/commit/f9e2a86b2e852dbed027e6aea5b48f32f2415b13
DIFF: https://github.com/llvm/llvm-project/commit/f9e2a86b2e852dbed027e6aea5b48f32f2415b13.diff
LOG: [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.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
################################################################################
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<Expr>, Matcher1, internal::Matcher<Expr>, Matcher2) {
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
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.
More information about the cfe-commits
mailing list