Add support for Adaptative matchers on the dynamic registry.
authorSamuel Benzaquen <sbenza@google.com>
Wed, 24 Jul 2013 14:48:01 +0000 (14:48 +0000)
committerSamuel Benzaquen <sbenza@google.com>
Wed, 24 Jul 2013 14:48:01 +0000 (14:48 +0000)
Summary:
Add support for Adaptative matchers on the dynamic registry.
Each adaptative matcher is created with a function template. We instantiate the function N times, one for each possible From type and apply the techniques used on argument overloaded and polymorphic matchers to add them to the registry.

Reviewers: klimek

CC: cfe-commits, revane
Differential Revision: http://llvm-reviews.chandlerc.com/D1201

llvm-svn: 187044

clang/include/clang/ASTMatchers/ASTMatchers.h
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
clang/lib/ASTMatchers/Dynamic/Registry.cpp
clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp

index b151985..fc36227 100644 (file)
@@ -1708,11 +1708,13 @@ findAll(const internal::Matcher<T> &Matcher) {
 ///
 /// Usable as: Any Matcher
 template <typename ParentT>
-internal::ArgumentAdaptingMatcher<internal::HasParentMatcher, ParentT>
+internal::ArgumentAdaptingMatcher<internal::HasParentMatcher, ParentT,
+                                  internal::TypeList<Decl, Stmt>,
+                                  internal::TypeList<Decl, Stmt> >
 hasParent(const internal::Matcher<ParentT> &ParentMatcher) {
   return internal::ArgumentAdaptingMatcher<
-    internal::HasParentMatcher,
-    ParentT>(ParentMatcher);
+      internal::HasParentMatcher, ParentT, internal::TypeList<Decl, Stmt>,
+      internal::TypeList<Decl, Stmt> >(ParentMatcher);
 }
 
 /// \brief Matches AST nodes that have an ancestor that matches the provided
@@ -1727,11 +1729,13 @@ hasParent(const internal::Matcher<ParentT> &ParentMatcher) {
 ///
 /// Usable as: Any Matcher
 template <typename AncestorT>
-internal::ArgumentAdaptingMatcher<internal::HasAncestorMatcher, AncestorT>
+internal::ArgumentAdaptingMatcher<internal::HasAncestorMatcher, AncestorT,
+                                  internal::TypeList<Decl, Stmt>,
+                                  internal::TypeList<Decl, Stmt> >
 hasAncestor(const internal::Matcher<AncestorT> &AncestorMatcher) {
   return internal::ArgumentAdaptingMatcher<
-    internal::HasAncestorMatcher,
-    AncestorT>(AncestorMatcher);
+      internal::HasAncestorMatcher, AncestorT, internal::TypeList<Decl, Stmt>,
+      internal::TypeList<Decl, Stmt> >(AncestorMatcher);
 }
 
 /// \brief Matches if the provided matcher does not match.
index 915f1bd..711e67d 100644 (file)
@@ -732,35 +732,6 @@ protected:
                                  AncestorMatchMode MatchMode) = 0;
 };
 
-/// \brief Converts a \c Matcher<T> to a matcher of desired type \c To by
-/// "adapting" a \c To into a \c T.
-///
-/// The \c ArgumentAdapterT argument specifies how the adaptation is done.
-///
-/// For example:
-///   \c ArgumentAdaptingMatcher<HasMatcher, T>(InnerMatcher);
-/// Given that \c InnerMatcher is of type \c Matcher<T>, this returns a matcher
-/// that is convertible into any matcher of type \c To by constructing
-/// \c HasMatcher<To, T>(InnerMatcher).
-///
-/// If a matcher does not need knowledge about the inner type, prefer to use
-/// PolymorphicMatcherWithParam1.
-template <template <typename ToArg, typename FromArg> class ArgumentAdapterT,
-          typename T>
-class ArgumentAdaptingMatcher {
-public:
-  explicit ArgumentAdaptingMatcher(const Matcher<T> &InnerMatcher)
-      : InnerMatcher(InnerMatcher) {}
-
-  template <typename To>
-  operator Matcher<To>() const {
-    return Matcher<To>(new ArgumentAdapterT<To, T>(InnerMatcher));
-  }
-
-private:
-  const Matcher<T> InnerMatcher;
-};
-
 /// \brief A simple type-list implementation.
 ///
 /// It is implemented as a flat struct with a maximum number of arguments to
@@ -813,6 +784,43 @@ template <class T> struct ExtractFunctionArgMeta<void(T)> {
   typedef T type;
 };
 
+/// \brief Default type lists for ArgumentAdaptingMatcher matchers.
+typedef AllNodeBaseTypes AdaptativeDefaultFromTypes;
+typedef TypeList<Decl, Stmt, NestedNameSpecifier, NestedNameSpecifierLoc,
+                 TypeLoc, QualType> AdaptativeDefaultToTypes;
+
+/// \brief Converts a \c Matcher<T> to a matcher of desired type \c To by
+/// "adapting" a \c To into a \c T.
+///
+/// The \c ArgumentAdapterT argument specifies how the adaptation is done.
+///
+/// For example:
+///   \c ArgumentAdaptingMatcher<HasMatcher, T>(InnerMatcher);
+/// Given that \c InnerMatcher is of type \c Matcher<T>, this returns a matcher
+/// that is convertible into any matcher of type \c To by constructing
+/// \c HasMatcher<To, T>(InnerMatcher).
+///
+/// If a matcher does not need knowledge about the inner type, prefer to use
+/// PolymorphicMatcherWithParam1.
+template <template <typename ToArg, typename FromArg> class ArgumentAdapterT,
+          typename T, typename FromTypes = AdaptativeDefaultFromTypes,
+          typename ToTypes = AdaptativeDefaultToTypes>
+class ArgumentAdaptingMatcher {
+public:
+  explicit ArgumentAdaptingMatcher(const Matcher<T> &InnerMatcher)
+      : InnerMatcher(InnerMatcher) {}
+
+  typedef ToTypes ReturnTypes;
+
+  template <typename To>
+  operator Matcher<To>() const {
+    return Matcher<To>(new ArgumentAdapterT<To, T>(InnerMatcher));
+  }
+
+private:
+  const Matcher<T> InnerMatcher;
+};
+
 /// \brief A PolymorphicMatcherWithParamN<MatcherT, P1, ..., PN> object can be
 /// created from N parameters p1, ..., pN (of type P1, ..., PN) and
 /// used as a Matcher<T> where a MatcherT<T, P1, ..., PN>(p1, ..., pN)
index 2daaa88..ac3241c 100644 (file)
@@ -109,6 +109,64 @@ class OverloadedMatcherCreateCallback : public MatcherCreateCallback {
     registerMatcher(#name, new OverloadedMatcherCreateCallback(Callbacks));    \
   } while (0)
 
+/// \brief Class that allows us to bind to the constructor of an
+///   \c ArgumentAdaptingMatcher.
+/// This class, together with \c collectAdaptativeMatcherOverloads below, help
+/// us detect the Adapter class and create overload functions for the
+/// appropriate To/From types.
+/// We instantiate the \c createAdatingMatcher function for every type in
+/// \c FromTypes. \c ToTypes is handled on the marshaller side by using the
+/// \c ReturnTypes typedef in \c ArgumentAdaptingMatcher.
+template <template <typename ToArg, typename FromArg> class ArgumentAdapterT,
+          typename FromTypes, typename ToTypes>
+struct AdaptativeMatcherWrapper {
+  template <typename FromArg>
+  static ast_matchers::internal::ArgumentAdaptingMatcher<
+      ArgumentAdapterT, FromArg, FromTypes, ToTypes>
+  createAdatingMatcher(
+      const ast_matchers::internal::Matcher<FromArg> &InnerMatcher) {
+    return ast_matchers::internal::ArgumentAdaptingMatcher<
+        ArgumentAdapterT, FromArg, FromTypes, ToTypes>(InnerMatcher);
+  }
+
+  static void collectOverloads(StringRef Name,
+                               std::vector<MatcherCreateCallback *> &Out,
+                               ast_matchers::internal::EmptyTypeList) {}
+
+  template <typename FromTypeList>
+  static void collectOverloads(StringRef Name,
+                               std::vector<MatcherCreateCallback *> &Out,
+                               FromTypeList TypeList) {
+    Out.push_back(internal::makeMatcherAutoMarshall(
+        &createAdatingMatcher<typename FromTypeList::head>, Name));
+    collectOverloads(Name, Out, typename FromTypeList::tail());
+  }
+
+  static void collectOverloads(StringRef Name,
+                               std::vector<MatcherCreateCallback *> &Out) {
+    collectOverloads(Name, Out, FromTypes());
+  }
+};
+
+template <template <typename ToArg, typename FromArg> class ArgumentAdapterT,
+          typename DummyArg, typename FromTypes, typename ToTypes>
+void collectAdaptativeMatcherOverloads(
+    StringRef Name,
+    ast_matchers::internal::ArgumentAdaptingMatcher<ArgumentAdapterT, DummyArg,
+                                                    FromTypes, ToTypes>(
+        *func)(const ast_matchers::internal::Matcher<DummyArg> &),
+    std::vector<MatcherCreateCallback *> &Out) {
+  AdaptativeMatcherWrapper<ArgumentAdapterT, FromTypes,
+                           ToTypes>::collectOverloads(Name, Out);
+}
+
+#define REGISTER_ADAPTATIVE(name)                                              \
+  do {                                                                         \
+    std::vector<MatcherCreateCallback *> Overloads;                            \
+    collectAdaptativeMatcherOverloads(#name, &name<Decl>, Overloads);          \
+    registerMatcher(#name, new OverloadedMatcherCreateCallback(Overloads));    \
+  } while (0)
+
 /// \brief Generate a registry map with all the known matchers.
 RegistryMaps::RegistryMaps() {
   // TODO: Here is the list of the missing matchers, grouped by reason.
@@ -123,14 +181,6 @@ RegistryMaps::RegistryMaps() {
   // allOf
   // findAll
   //
-  // Adaptative matcher (similar to polymorphic matcher):
-  // has
-  // forEach
-  // forEachDescendant
-  // hasDescendant
-  // hasParent
-  // hasAncestor
-  //
   // Other:
   // loc
   // equals
@@ -146,6 +196,13 @@ RegistryMaps::RegistryMaps() {
   REGISTER_OVERLOADED_2(references);
   REGISTER_OVERLOADED_2(thisPointerType);
 
+  REGISTER_ADAPTATIVE(forEach);
+  REGISTER_ADAPTATIVE(forEachDescendant);
+  REGISTER_ADAPTATIVE(has);
+  REGISTER_ADAPTATIVE(hasAncestor);
+  REGISTER_ADAPTATIVE(hasDescendant);
+  REGISTER_ADAPTATIVE(hasParent);
+
   REGISTER_MATCHER(accessSpecDecl);
   REGISTER_MATCHER(alignOfExpr);
   REGISTER_MATCHER(anything);
index e69017e..7f49b6a 100644 (file)
@@ -222,6 +222,37 @@ TEST_F(RegistryTest, CXXCtorInitializer) {
   EXPECT_FALSE(matches("struct Foo { Foo() : bar(1) {} int bar; };", CtorDecl));
 }
 
+TEST_F(RegistryTest, Adaptative) {
+  Matcher<Decl> D = constructMatcher(
+      "recordDecl",
+      constructMatcher(
+          "has",
+          constructMatcher("recordDecl",
+                           constructMatcher("hasName", std::string("X")))))
+      .getTypedMatcher<Decl>();
+  EXPECT_TRUE(matches("class X {};", D));
+  EXPECT_TRUE(matches("class Y { class X {}; };", D));
+  EXPECT_FALSE(matches("class Y { class Z {}; };", D));
+
+  Matcher<Stmt> S = constructMatcher(
+      "forStmt",
+      constructMatcher(
+          "hasDescendant",
+          constructMatcher("varDecl",
+                           constructMatcher("hasName", std::string("X")))))
+      .getTypedMatcher<Stmt>();
+  EXPECT_TRUE(matches("void foo() { for(int X;;); }", S));
+  EXPECT_TRUE(matches("void foo() { for(;;) { int X; } }", S));
+  EXPECT_FALSE(matches("void foo() { for(;;); }", S));
+  EXPECT_FALSE(matches("void foo() { if (int X = 0){} }", S));
+
+  S = constructMatcher(
+      "compoundStmt", constructMatcher("hasParent", constructMatcher("ifStmt")))
+      .getTypedMatcher<Stmt>();
+  EXPECT_TRUE(matches("void foo() { if (true) { int x = 42; } }", S));
+  EXPECT_FALSE(matches("void foo() { if (true) return; }", S));
+}
+
 TEST_F(RegistryTest, Errors) {
   // Incorrect argument count.
   OwningPtr<Diagnostics> Error(new Diagnostics());