Applied review comments.

Hi djasper, mdiamond,

http://llvm-reviews.chandlerc.com/D36

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D36?vs=97&id=98#toc

Files:
  include/clang/ASTMatchers/ASTMatchers.h
  include/clang/ASTMatchers/ASTMatchersInternal.h
  lib/ASTMatchers/ASTMatchFinder.cpp
  unittests/ASTMatchers/ASTMatchersTest.cpp
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -1179,7 +1179,6 @@
     DescendantT>(DescendantMatcher);
 }
 
-
 /// \brief Matches AST nodes that have child AST nodes that match the
 /// provided matcher.
 ///
@@ -1237,6 +1236,25 @@
     DescendantT>(DescendantMatcher);
 }
 
+/// \brief Matches AST nodes that have an ancestor that matches the provided
+/// matcher.
+///
+/// Given
+/// \code
+/// void f() { if (true) { int x = 42; } }
+/// void g() { for (;;) { int x = 43; } }
+/// \endcode
+/// \c expr(integerLiteral(hasAncsestor(ifStmt()))) matches \c 42, but not 43.
+///
+/// Usable as: Any Matcher
+template <typename AncestorT>
+internal::ArgumentAdaptingMatcher<internal::HasAncestorMatcher, AncestorT>
+hasAncestor(const internal::Matcher<AncestorT> &AncestorMatcher) {
+  return internal::ArgumentAdaptingMatcher<
+    internal::HasAncestorMatcher,
+    AncestorT>(AncestorMatcher);
+}
+
 /// \brief Matches if the provided matcher does not match.
 ///
 /// Example matches Y (matcher = recordDecl(unless(hasName("X"))))
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -388,13 +388,21 @@
 /// \brief Interface that allows matchers to traverse the AST.
 /// FIXME: Find a better name.
 ///
-/// This provides two entry methods for each base node type in the AST:
-/// - matchesChildOf:
+/// This provides three entry methods for each base node type in the AST:
+/// - \c matchesChildOf:
 ///   Matches a matcher on every child node of the given node. Returns true
 ///   if at least one child node could be matched.
-/// - matchesDescendantOf:
+/// - \c matchesDescendantOf:
 ///   Matches a matcher on all descendant nodes of the given node. Returns true
 ///   if at least one descendant matched.
+/// - \c matchesAncestorOf:
+///   Matches a matcher on all ancestors of the given node. Returns true if
+///   at least one ancestor matched.
+///
+/// FIXME: Currently we only allow Stmt and Decl nodes to start a traversal.
+/// In the future, we wan to implement this for all nodes for which it makes
+/// sense. In the case of matchesAncestorOf, we'll want to implement it for
+/// all nodes, as all nodes have ancestors.
 class ASTMatchFinder {
 public:
   /// \brief Defines how we descend a level in the AST when we pass
@@ -449,8 +457,19 @@
                                Matcher, Builder, Bind);
   }
 
+  // 
+  template <typename T>
+  bool matchesAncestorOf(const T &Node,
+                         const DynTypedMatcher &Matcher,
+                         BoundNodesTreeBuilder *Builder) {
+    TOOLING_COMPILE_ASSERT((llvm::is_base_of<Decl, T>::value ||
+                            llvm::is_base_of<Stmt, T>::value),
+                           only_Decl_or_Stmt_allowed_for_recursive_matching);
+    return matchesAncestorOf(ast_type_traits::DynTypedNode::create(Node),
+                             Matcher, Builder);
+  }
+
 protected:
-  // FIXME: Implement for other base nodes.
   virtual bool matchesChildOf(const ast_type_traits::DynTypedNode &Node,
                               const DynTypedMatcher &Matcher,
                               BoundNodesTreeBuilder *Builder,
@@ -461,6 +480,10 @@
                                    const DynTypedMatcher &Matcher,
                                    BoundNodesTreeBuilder *Builder,
                                    BindKind Bind) = 0;
+
+  virtual bool matchesAncestorOf(const ast_type_traits::DynTypedNode &Node,
+                                 const DynTypedMatcher &Matcher,
+                                 BoundNodesTreeBuilder *Builder) = 0;
 };
 
 /// \brief Converts a \c Matcher<T> to a matcher of desired type \c To by
@@ -801,6 +824,29 @@
   const Matcher<DescendantT> DescendantMatcher;
 };
 
+/// \brief Matches nodes of type \c T that have at least one ancestor node of
+/// type \c AncestorT for which the given inner matcher matches.
+///
+/// \c AncestorT must be an AST base type.
+template <typename T, typename AncestorT>
+class HasAncestorMatcher : public MatcherInterface<T> {
+  TOOLING_COMPILE_ASSERT(IsBaseType<AncestorT>::value,
+                         has_descendant_only_accepts_base_type_matcher);
+public:
+  explicit HasAncestorMatcher(const Matcher<AncestorT> &AncestorMatcher)
+      : AncestorMatcher(AncestorMatcher) {}
+
+  virtual bool matches(const T &Node,
+                       ASTMatchFinder *Finder,
+                       BoundNodesTreeBuilder *Builder) const {
+    return Finder->matchesAncestorOf(
+        Node, AncestorMatcher, Builder);
+  }
+
+ private:
+  const Matcher<AncestorT> AncestorMatcher;
+};
+
 /// \brief Matches nodes of type T that have at least one descendant node of
 /// type DescendantT for which the given inner matcher matches.
 ///
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -29,6 +29,58 @@
 
 typedef MatchFinder::MatchCallback MatchCallback;
 
+/// \brief A \c RecursiveASTVisitor that builds a map from nodes to their
+/// parents.
+///
+/// FIXME: Currently only builds up the map using \c Stmt and \c Decl nodes.
+class ParentMapASTVisitor : public RecursiveASTVisitor<ParentMapASTVisitor> {
+public:
+  /// \brief Maps from a node to its parent.
+  typedef llvm::DenseMap<const void*, ast_type_traits::DynTypedNode> ParentMap;
+
+  /// \brief Builds and returns the translation unit's parent map.
+  ///
+  ///  The caller takes ownership of the returned \c ParentMap.
+  static ParentMap *buildMap(TranslationUnitDecl &TU) {
+    ParentMapASTVisitor Visitor(new ParentMap);
+    Visitor.TraverseDecl(&TU);
+    return Visitor.Parents;
+  }
+
+private:
+  typedef RecursiveASTVisitor<ParentMapASTVisitor> VisitorBase;
+
+  ParentMapASTVisitor(ParentMap *Parents) : Parents(Parents) {}
+
+  bool shouldVisitTemplateInstantiations() const { return true; }
+  bool shouldVisitImplicitCode() const { return true; }
+
+  template <typename T>
+  bool TraverseNode(T *Node, bool (VisitorBase::*traverse)(T*)) {
+    if (Node == NULL)
+      return true;
+    if (ParentStack.size() > 0)
+      (*Parents)[Node] = ParentStack.back();
+    ParentStack.push_back(ast_type_traits::DynTypedNode::create(*Node));
+    bool Result = (this->*traverse)(Node);
+    ParentStack.pop_back();
+    return Result;
+  }
+
+  bool TraverseDecl(Decl *DeclNode) {
+    return TraverseNode(DeclNode, &VisitorBase::TraverseDecl);
+  }
+
+  bool TraverseStmt(Stmt *StmtNode) {
+    return TraverseNode(StmtNode, &VisitorBase::TraverseStmt);
+  }
+
+  ParentMap *Parents;
+  llvm::SmallVector<ast_type_traits::DynTypedNode, 16> ParentStack;
+
+  friend class RecursiveASTVisitor<ParentMapASTVisitor>;
+};
+
 // We use memoization to avoid running the same matcher on the same
 // AST node twice.  This pair is the key for looking up match
 // result.  It consists of an ID of the MatcherInterface (for
@@ -311,6 +363,35 @@
     return memoizedMatchesRecursively(Node, Matcher, Builder, INT_MAX,
                                       TK_AsIs, Bind);
   }
+  // Implements ASTMatchFinder::matchesAncestorOf.
+  virtual bool matchesAncestorOf(const ast_type_traits::DynTypedNode &Node,
+                                 const DynTypedMatcher &Matcher,
+                                 BoundNodesTreeBuilder *Builder) {
+    if (!Parents) {
+      // We always need to run over the whole translation unit, as
+      // \c hasAncestor can escape any subtree.
+      Parents.reset(ParentMapASTVisitor::buildMap(
+        *ActiveASTContext->getTranslationUnitDecl()));
+    }
+    ast_type_traits::DynTypedNode Ancestor = Node;
+    while (Ancestor.get<TranslationUnitDecl>() !=
+           ActiveASTContext->getTranslationUnitDecl()) {
+      assert(Ancestor.getMemoizationData() &&
+             "Invariant broken: only nodes that support memoization may be "
+             "used in the parent map.");
+      ParentMapASTVisitor::ParentMap::const_iterator I =
+        Parents->find(Ancestor.getMemoizationData());
+      if (I == Parents->end()) {
+        assert(false &&
+               "Found node that is not in the parent map.");
+        return false;
+      }
+      Ancestor = I->second;
+      if (Matcher.matches(Ancestor, this, Builder))
+        return true;
+    }
+    return false;
+  }
 
   bool shouldVisitTemplateInstantiations() const { return true; }
   bool shouldVisitImplicitCode() const { return true; }
@@ -378,6 +459,8 @@
   // Maps (matcher, node) -> the match result for memoization.
   typedef llvm::DenseMap<UntypedMatchInput, MemoizedMatchResult> MemoizationMap;
   MemoizationMap ResultCache;
+
+  llvm::OwningPtr<ParentMapASTVisitor::ParentMap> Parents;
 };
 
 // Returns true if the given class is directly or indirectly derived
Index: unittests/ASTMatchers/ASTMatchersTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -597,31 +597,41 @@
       matches("class A { public: A *a; class B {}; };", TypeAHasClassB));
 }
 
-// Returns from Run whether 'bound_nodes' contain a Decl bound to 'Id', which
-// can be dynamically casted to T.
+// Implements a run method that returns whether BoundNodes contains a
+// Decl bound to Id that can be dynamically cast to T.
 // Optionally checks that the check succeeded a specific number of times.
 template <typename T>
 class VerifyIdIsBoundToDecl : public BoundNodesCallback {
 public:
-  // Create an object that checks that a node of type 'T' was bound to 'Id'.
+  // Create an object that checks that a node of type \c T was bound to \c Id.
   // Does not check for a certain number of matches.
-  explicit VerifyIdIsBoundToDecl(const std::string& Id)
+  explicit VerifyIdIsBoundToDecl(llvm::StringRef Id)
     : Id(Id), ExpectedCount(-1), Count(0) {}
 
-  // Create an object that checks that a node of type 'T' was bound to 'Id'.
-  // Checks that there were exactly 'ExpectedCount' matches.
-  explicit VerifyIdIsBoundToDecl(const std::string& Id, int ExpectedCount)
+  // Create an object that checks that a node of type \c T was bound to \c Id.
+  // Checks that there were exactly \c ExpectedCount matches.
+  VerifyIdIsBoundToDecl(llvm::StringRef Id, int ExpectedCount)
     : Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
 
+  // Create an object that checks that a node of type \c T was bound to \c Id.
+  // Checks that there was exactly one match with the name \c ExpectedDeclName.
+  // Note that \c T must be a NamedDecl for this to work.
+  VerifyIdIsBoundToDecl(llvm::StringRef Id, llvm::StringRef ExpectedDeclName)
+    : Id(Id), ExpectedCount(1), Count(0), ExpectedDeclName(ExpectedDeclName) {}
+
   ~VerifyIdIsBoundToDecl() {
-    if (ExpectedCount != -1) {
+    if (ExpectedCount != -1)
       EXPECT_EQ(ExpectedCount, Count);
-    }
+    if (!ExpectedDeclName.empty())
+      EXPECT_EQ(ExpectedDeclName, DeclName);
   }
 
   virtual bool run(const BoundNodes *Nodes) {
-    if (Nodes->getDeclAs<T>(Id) != NULL) {
+    if (const Decl *Node = Nodes->getDeclAs<T>(Id)) {
       ++Count;
+      if (const NamedDecl *Named = llvm::dyn_cast<NamedDecl>(Node)) {
+        DeclName = Named->getNameAsString();
+      }
       return true;
     }
     return false;
@@ -631,6 +641,8 @@
   const std::string Id;
   const int ExpectedCount;
   int Count;
+  const std::string ExpectedDeclName;
+  std::string DeclName;
 };
 template <typename T>
 class VerifyIdIsBoundToStmt : public BoundNodesCallback {
@@ -2721,5 +2733,72 @@
       functionDecl(isExplicitTemplateSpecialization())));
 }
 
+TEST(HasAncenstor, MatchesDeclarationAncestors) {
+  EXPECT_TRUE(matches(
+      "class A { class B { class C {}; }; };",
+      recordDecl(hasName("C"), hasAncestor(recordDecl(hasName("A"))))));
+}
+
+TEST(HasAncenstor, FailsIfNoAncestorMatches) {
+  EXPECT_TRUE(notMatches(
+      "class A { class B { class C {}; }; };",
+      recordDecl(hasName("C"), hasAncestor(recordDecl(hasName("X"))))));
+}
+
+TEST(HasAncestor, MatchesDeclarationsThatGetVisitedLater) {
+  EXPECT_TRUE(matches(
+      "class A { class B { void f() { C c; } class C {}; }; };",
+      varDecl(hasName("c"), hasType(recordDecl(hasName("C"), 
+          hasAncestor(recordDecl(hasName("A"))))))));
+}
+
+TEST(HasAncenstor, MatchesStatementAncestors) {
+  EXPECT_TRUE(matches(
+      "void f() { if (true) { while (false) { 42; } } }",
+      expr(integerLiteral(equals(42), hasAncestor(ifStmt())))));
+}
+
+TEST(HasAncestor, DrillsThroughDifferentHierarchies) {
+  EXPECT_TRUE(matches(
+      "void f() { if (true) { int x = 42; } }",
+      expr(integerLiteral(
+          equals(42), hasAncestor(functionDecl(hasName("f")))))));
+}
+
+TEST(HasAncestor, BindsRecursiveCombinations) {
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      "class C { class D { class E { class F { int y; }; }; }; };",
+      fieldDecl(hasAncestor(recordDecl(hasAncestor(recordDecl().bind("r"))))),
+      new VerifyIdIsBoundToDecl<CXXRecordDecl>("r", 1)));
+}
+
+TEST(HasAncestor, BindsCombinationsWithHasDescendant) {
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      "class C { class D { class E { class F { int y; }; }; }; };",
+      fieldDecl(hasAncestor(
+          decl(
+            hasDescendant(recordDecl(isDefinition(),
+                                     hasAncestor(recordDecl())))
+          ).bind("d")
+      )),
+      new VerifyIdIsBoundToDecl<CXXRecordDecl>("d", "E")));
+}
+
+TEST(HasAncestor, MatchesInTemplateInstantiations) {
+  EXPECT_TRUE(matches(
+      "template <typename T> struct A { struct B { struct C { T t; }; }; }; "
+      "A<int>::B::C a;",
+      fieldDecl(hasType(asString("int")),
+                hasAncestor(recordDecl(hasName("A"))))));
+}
+
+TEST(HasAncestor, MatchesInImplicitCode) {
+  EXPECT_TRUE(matches(
+      "struct X {}; struct A { A() {} X x; };",
+      constructorDecl(
+          hasAnyConstructorInitializer(withInitializer(expr(
+              hasAncestor(recordDecl(hasName("A")))))))));
+}
+
 } // end namespace ast_matchers
 } // end namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to