[clang] e810e95 - [ASTMatchers] Add binaryOperation matcher

Stephen Kelly via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 16 05:44:21 PST 2021


Author: Stephen Kelly
Date: 2021-01-16T13:44:09Z
New Revision: e810e95e4bb908d1c8844e2c6f7da999732cabc9

URL: https://github.com/llvm/llvm-project/commit/e810e95e4bb908d1c8844e2c6f7da999732cabc9
DIFF: https://github.com/llvm/llvm-project/commit/e810e95e4bb908d1c8844e2c6f7da999732cabc9.diff

LOG: [ASTMatchers] Add binaryOperation matcher

This is a simple utility which allows matching on binaryOperator and
cxxOperatorCallExpr. It can also be extended to support
cxxRewrittenBinaryOperator.

Add generic support for MapAnyOfMatchers to auto-marshalling functions.

Differential Revision: https://reviews.llvm.org/D94129

Added: 
    

Modified: 
    clang/docs/LibASTMatchersReference.html
    clang/docs/tools/dump_ast_matchers.py
    clang/include/clang/ASTMatchers/ASTMatchers.h
    clang/lib/ASTMatchers/ASTMatchersInternal.cpp
    clang/lib/ASTMatchers/Dynamic/Marshallers.h
    clang/lib/ASTMatchers/Dynamic/Registry.cpp
    clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 497e2d4584a2..453d8f16c25a 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -1206,6 +1206,7 @@ <h2 id="decl-matchers">Node Matchers</h2>
 
 Example matches a || b
   !(a || b)
+See also the binaryOperation() matcher for more-general matching.
 </pre></td></tr>
 
 
@@ -1505,6 +1506,8 @@ <h2 id="decl-matchers">Node Matchers</h2>
   ostream &operator<< (ostream &out, int i) { };
   ostream &o; int b = 1, c = 1;
   o << b << c;
+See also the binaryOperation() matcher for more-general matching of binary
+uses of this AST node.
 </pre></td></tr>
 
 
@@ -5438,6 +5441,48 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
 <tr style="text-align:left"><th>Return type</th><th>Name</th><th>Parameters</th></tr>
 <!-- START_TRAVERSAL_MATCHERS -->
 
+<tr><td>Matcher<*></td><td class="name" onclick="toggle('binaryOperation0')"><a name="binaryOperation0Anchor">binaryOperation</a></td><td>Matcher<*>...Matcher<*></td></tr>
+<tr><td colspan="4" class="doc" id="binaryOperation0"><pre>Matches nodes which can be used with binary operators.
+
+The code
+  var1 != var2;
+might be represented in the clang AST as a binaryOperator or a
+cxxOperatorCallExpr, depending on
+
+* whether the types of var1 and var2 are fundamental (binaryOperator) or at
+  least one is a class type (cxxOperatorCallExpr)
+* whether the code appears in a template declaration, if at least one of the
+  vars is a dependent-type (binaryOperator)
+
+This matcher elides details in places where the matchers for the nodes are
+compatible.
+
+Given
+  binaryOperation(
+    hasOperatorName("!="),
+    hasLHS(expr().bind("lhs")),
+    hasRHS(expr().bind("rhs"))
+  )
+matches each use of "!=" in:
+  struct S{
+      bool operator!=(const S&) const;
+  };
+
+  void foo()
+  {
+     1 != 2;
+     S() != S();
+  }
+
+  template<typename T>
+  void templ()
+  {
+     1 != 2;
+     T() != S();
+  }
+</pre></td></tr>
+
+
 <tr><td>Matcher<*></td><td class="name" onclick="toggle('eachOf0')"><a name="eachOf0Anchor">eachOf</a></td><td>Matcher<*>, ..., Matcher<*></td></tr>
 <tr><td colspan="4" class="doc" id="eachOf0"><pre>Matches if any of the given matchers matches.
 

diff  --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py
index d3aff1284f60..18afbdd36c6e 100755
--- a/clang/docs/tools/dump_ast_matchers.py
+++ b/clang/docs/tools/dump_ast_matchers.py
@@ -379,6 +379,14 @@ def act_on_decl(declaration, comment, allowed_types):
         add_matcher('*', name, 'Matcher<*>, ..., Matcher<*>', comment)
         return
 
+    m = re.match(
+        r"""^.*MapAnyOfMatcher<.*>\s*
+              ([a-zA-Z]*);$""",
+        declaration, flags=re.X)
+    if m:
+      name = m.groups()[0]
+      add_matcher('*', name, 'Matcher<*>...Matcher<*>', comment)
+      return
 
     # Parse free standing matcher functions, like:
     #   Matcher<ResultType> Name(Matcher<ArgumentType> InnerMatcher) {

diff  --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index e81ac4cb7bf8..8140e20c9cad 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -1971,6 +1971,8 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDefaultArgExpr>
 ///   ostream &o; int b = 1, c = 1;
 ///   o << b << c;
 /// \endcode
+/// See also the binaryOperation() matcher for more-general matching of binary
+/// uses of this AST node.
 extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
     cxxOperatorCallExpr;
 
@@ -2393,6 +2395,7 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, StmtExpr> stmtExpr;
 /// \code
 ///   !(a || b)
 /// \endcode
+/// See also the binaryOperation() matcher for more-general matching.
 extern const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>
     binaryOperator;
 
@@ -2729,6 +2732,53 @@ auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) {
   return internal::MapAnyOfHelper<U...>();
 }
 
+/// Matches nodes which can be used with binary operators.
+///
+/// The code
+/// \code
+///   var1 != var2;
+/// \endcode
+/// might be represented in the clang AST as a binaryOperator or a
+/// cxxOperatorCallExpr, depending on
+///
+/// * whether the types of var1 and var2 are fundamental (binaryOperator) or at
+///   least one is a class type (cxxOperatorCallExpr)
+/// * whether the code appears in a template declaration, if at least one of the
+///   vars is a dependent-type (binaryOperator)
+///
+/// This matcher elides details in places where the matchers for the nodes are
+/// compatible.
+///
+/// Given
+/// \code
+///   binaryOperation(
+///     hasOperatorName("!="),
+///     hasLHS(expr().bind("lhs")),
+///     hasRHS(expr().bind("rhs"))
+///   )
+/// \endcode
+/// matches each use of "!=" in:
+/// \code
+///   struct S{
+///       bool operator!=(const S&) const;
+///   };
+///
+///   void foo()
+///   {
+///      1 != 2;
+///      S() != S();
+///   }
+///
+///   template<typename T>
+///   void templ()
+///   {
+///      1 != 2;
+///      T() != S();
+///   }
+/// \endcode
+extern const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr>
+    binaryOperation;
+
 /// Matches unary expressions that have a specific type of argument.
 ///
 /// Given

diff  --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index eb0fffcc3c37..36917cf0db46 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -919,6 +919,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, AtomicExpr> atomicExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, StmtExpr> stmtExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>
     binaryOperator;
+const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr>
+    binaryOperation;
 const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryOperator> unaryOperator;
 const internal::VariadicDynCastAllOfMatcher<Stmt, ConditionalOperator>
     conditionalOperator;

diff  --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.h b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
index f53df1f6528a..23e26dcd9db6 100644
--- a/clang/lib/ASTMatchers/Dynamic/Marshallers.h
+++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
@@ -925,6 +925,50 @@ class VariadicOperatorMatcherDescriptor : public MatcherDescriptor {
   const StringRef MatcherName;
 };
 
+template <typename CladeType, typename... MatcherT>
+class MapAnyOfMatcherDescriptor : public MatcherDescriptor {
+  std::vector<DynCastAllOfMatcherDescriptor> Funcs;
+
+public:
+  MapAnyOfMatcherDescriptor(StringRef MatcherName)
+      : Funcs{DynCastAllOfMatcherDescriptor(
+            ast_matchers::internal::VariadicDynCastAllOfMatcher<CladeType,
+                                                                MatcherT>{},
+            MatcherName)...} {}
+
+  VariantMatcher create(SourceRange NameRange, ArrayRef<ParserValue> Args,
+                        Diagnostics *Error) const override {
+    std::vector<VariantMatcher> InnerArgs;
+
+    for (auto const &F : Funcs) {
+      InnerArgs.push_back(F.create(NameRange, Args, Error));
+      if (!Error->errors().empty())
+        return {};
+    }
+    return VariantMatcher::SingleMatcher(
+        ast_matchers::internal::BindableMatcher<CladeType>(
+            VariantMatcher::VariadicOperatorMatcher(
+                ast_matchers::internal::DynTypedMatcher::VO_AnyOf,
+                std::move(InnerArgs))
+                .getTypedMatcher<CladeType>()));
+  }
+
+  bool isVariadic() const override { return true; }
+  unsigned getNumArgs() const override { return 0; }
+
+  void getArgKinds(ASTNodeKind ThisKind, unsigned,
+                   std::vector<ArgKind> &Kinds) const override {
+    Kinds.push_back(ThisKind);
+  }
+
+  bool isConvertibleTo(ASTNodeKind Kind, unsigned *Specificity,
+                       ASTNodeKind *LeastDerivedKind) const override {
+    return llvm::all_of(Funcs, [=](const auto &F) {
+      return F.isConvertibleTo(Kind, Specificity, LeastDerivedKind);
+    });
+  }
+};
+
 /// Helper functions to select the appropriate marshaller functions.
 /// They detect the number of arguments, arguments types and return type.
 
@@ -1029,6 +1073,14 @@ std::unique_ptr<MatcherDescriptor> makeMatcherAutoMarshall(
       MinCount, MaxCount, Func.Op, MatcherName);
 }
 
+template <typename CladeType, typename... MatcherT>
+std::unique_ptr<MatcherDescriptor> makeMatcherAutoMarshall(
+    ast_matchers::internal::MapAnyOfMatcherImpl<CladeType, MatcherT...>,
+    StringRef MatcherName) {
+  return std::make_unique<MapAnyOfMatcherDescriptor<CladeType, MatcherT...>>(
+      MatcherName);
+}
+
 } // namespace internal
 } // namespace dynamic
 } // namespace ast_matchers

diff  --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 390d883dc5e3..9dc92a881cbc 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -143,6 +143,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(autoreleasePoolStmt)
   REGISTER_MATCHER(binaryConditionalOperator);
   REGISTER_MATCHER(binaryOperator);
+  REGISTER_MATCHER(binaryOperation);
   REGISTER_MATCHER(blockDecl);
   REGISTER_MATCHER(blockExpr);
   REGISTER_MATCHER(blockPointerType);

diff  --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 9e011892f6e5..ae57383273e7 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -470,6 +470,10 @@ TEST_P(ASTMatchersTest, MapAnyOf) {
     return;
   }
 
+  if (GetParam().hasDelayedTemplateParsing()) {
+    return;
+  }
+
   StringRef Code = R"cpp(
 void F() {
   if (true) {}
@@ -556,6 +560,15 @@ void opFree()
     if (s1 != s2)
         return;
 }
+
+template<typename T>
+void templ()
+{
+    T s1;
+    T s2;
+    if (s1 != s2)
+        return;
+}
 )cpp";
 
   EXPECT_TRUE(matches(
@@ -669,6 +682,38 @@ void opFree()
                          .with(hasAnyOperatorName("==", "!="),
                                forFunction(functionDecl(hasName("opFree")))))));
 
+  EXPECT_TRUE(matches(
+      Code, traverse(TK_IgnoreUnlessSpelledInSource,
+                     binaryOperation(
+                         hasOperatorName("!="),
+                         forFunction(functionDecl(hasName("binop"))),
+                         hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+                         hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+
+  EXPECT_TRUE(matches(
+      Code, traverse(TK_IgnoreUnlessSpelledInSource,
+                     binaryOperation(
+                         hasOperatorName("!="),
+                         forFunction(functionDecl(hasName("opMem"))),
+                         hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+                         hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+
+  EXPECT_TRUE(matches(
+      Code, traverse(TK_IgnoreUnlessSpelledInSource,
+                     binaryOperation(
+                         hasOperatorName("!="),
+                         forFunction(functionDecl(hasName("opFree"))),
+                         hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+                         hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+
+  EXPECT_TRUE(matches(
+      Code, traverse(TK_IgnoreUnlessSpelledInSource,
+                     binaryOperation(
+                         hasOperatorName("!="),
+                         forFunction(functionDecl(hasName("templ"))),
+                         hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+                         hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+
   Code = R"cpp(
 struct HasOpBangMem
 {


        


More information about the cfe-commits mailing list