Moves the ASTTypeTraits to AST/, adds tests and comments.

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

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D267?vs=634&id=883#toc

Files:
  include/clang/AST/ASTContext.h
  include/clang/AST/ASTTypeTraits.h
  include/clang/ASTMatchers/ASTTypeTraits.h
  include/clang/ASTMatchers/ASTMatchersInternal.h
  lib/ASTMatchers/ASTMatchFinder.cpp
  unittests/AST/ASTContextParentMapTest.cpp
  unittests/AST/CMakeLists.txt
  unittests/AST/MatchVerifier.h
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -15,13 +15,15 @@
 #ifndef LLVM_CLANG_AST_ASTCONTEXT_H
 #define LLVM_CLANG_AST_ASTCONTEXT_H
 
+#include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/CanonicalType.h"
 #include "clang/AST/CommentCommandTraits.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/LambdaMangleContext.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/RawCommentList.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/TemplateName.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/AddressSpaces.h"
@@ -398,6 +400,58 @@
   OwningPtr<ExternalASTSource> ExternalSource;
   ASTMutationListener *Listener;
 
+  /// \brief Contains parents of a node.
+  typedef llvm::SmallVector<ast_type_traits::DynTypedNode, 1> ParentVector;
+
+  /// \brief Maps from a node to its parents.
+  typedef llvm::DenseMap<const void *, ParentVector> ParentMap;
+
+  /// \brief Returns the parents of the given node.
+  ///
+  /// Note that this will lazily compute the parents of all nodes
+  /// and store them for later retrieval. Thus, the first call is O(n)
+  /// in the number of AST nodes.
+  ///
+  /// Caveats and FIXMEs:
+  /// Calculating the parent map over all AST nodes will need to load the
+  /// full AST. This can be undesirable in the case where the full AST is
+  /// expensive to create (for example, when using precompiled header
+  /// preambles. Thus, there are good opportunities for optimization here.
+  /// One idea is to walk the given node downwards, looking for references
+  /// to declaration contexts - once a declaration context is found, compute
+  /// the parent map for the declaration context; if that can satisfy the
+  /// request, loading the whole AST can be avoided. Note that this is made
+  /// more complex by statements in templates having multiple parents - those
+  /// problems can be solved by building closure over the templated parts of
+  /// the AST, which also avoids touching large parts of the AST.
+  /// Additionally, we will want to add an interface to already give a hint
+  /// where to search for the parents, for example when looking at a statement
+  /// inside a certain function.
+  ///
+  /// 'NodeT' can be one of Decl, Stmt, Type, TypeLoc,
+  /// NestedNameSpecifier or NestedNameSpecifierLoc.
+  template <typename NodeT>
+  ParentVector getParents(const NodeT &Node) {
+    return getParents(ast_type_traits::DynTypedNode::create(Node));
+  }
+
+  ParentVector getParents(const ast_type_traits::DynTypedNode &Node) {
+    assert(Node.getMemoizationData() &&
+           "Invariant broken: only nodes that support memoization may be "
+           "used in the parent map.");
+    if (!AllParents) {
+      // We always need to run over the whole translation unit, as
+      // hasAncestor can escape any subtree.
+      AllParents.reset(
+          ParentMapASTVisitor::buildMap(*getTranslationUnitDecl()));
+    }
+    ParentMap::const_iterator I = AllParents->find(Node.getMemoizationData());
+    if (I == AllParents->end()) {
+      return ParentVector();
+    }
+    return I->second;
+  }
+
   const clang::PrintingPolicy &getPrintingPolicy() const {
     return PrintingPolicy;
   }
@@ -2151,8 +2205,81 @@
   friend class DeclContext;
   friend class DeclarationNameTable;
   void ReleaseDeclContextMaps();
+
+  /// \brief A \c RecursiveASTVisitor that builds a map from nodes to their
+  /// parents as defined by the \c RecursiveASTVisitor.
+  ///
+  /// Note that the relationship described here is purely in terms of AST
+  /// traversal - there are other relationships (for example declaration context)
+  /// in the AST that are better modeled by special matchers.
+  ///
+  /// FIXME: Currently only builds up the map using \c Stmt and \c Decl nodes.
+  class ParentMapASTVisitor : public RecursiveASTVisitor<ParentMapASTVisitor> {
+  public:
+    /// \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;
+    }
+    // Disables data recursion. We intercept Traverse* methods in the RAV, which
+    // are not triggered during data recursion.
+    bool shouldUseDataRecursionFor(clang::Stmt *S) const {
+      return false;
+    }
+
+    template <typename T>
+    bool TraverseNode(T *Node, bool(VisitorBase:: *traverse) (T *)) {
+      if (Node == NULL)
+        return true;
+      if (ParentStack.size() > 0)
+        // FIXME: Currently we add the same parent multiple times, for example
+        // when we visit all subexpressions of template instantiations; this is
+        // suboptimal, bug benign: the only way to visit those is with
+        // hasAncestor / hasParent, and those do not create new matches.
+        // The plan is to enable DynTypedNode to be storable in a map or hash
+        // map. The main problem there is to implement hash functions /
+        // comparison operators for all types that DynTypedNode supports that
+        // do not have pointer identity.
+        (*Parents)[Node].push_back(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>;
+  };
+
+  llvm::OwningPtr<ParentMap> AllParents;
 };
-  
+
 /// \brief Utility function for constructing a nullary selector.
 static inline Selector GetNullarySelector(StringRef name, ASTContext& Ctx) {
   IdentifierInfo* II = &Ctx.Idents.get(name);
Index: include/clang/AST/ASTTypeTraits.h
===================================================================
--- include/clang/AST/ASTTypeTraits.h
+++ include/clang/AST/ASTTypeTraits.h
@@ -88,8 +88,9 @@
   /// guaranteed to be unique pointers pointing to dedicated storage in the
   /// AST. \c QualTypes on the other hand do not have storage or unique
   /// pointers and thus need to be stored by value.
-  llvm::AlignedCharArrayUnion<Decl*, QualType, TypeLoc, NestedNameSpecifierLoc>
-    Storage;
+  llvm::AlignedCharArrayUnion<Decl *, Stmt *, NestedNameSpecifier,
+                              NestedNameSpecifierLoc, QualType, Type,
+                              TypeLoc> Storage;
 };
 
 // FIXME: Pull out abstraction for the following.
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -35,13 +35,13 @@
 #ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H
 #define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H
 
-#include "clang/AST/Decl.h"
+#include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/Decl.h"
 #include "clang/AST/ExprCXX.h"
-#include "clang/AST/Stmt.h"
 #include "clang/AST/StmtCXX.h"
+#include "clang/AST/Stmt.h"
 #include "clang/AST/Type.h"
-#include "clang/ASTMatchers/ASTTypeTraits.h"
 #include "llvm/ADT/VariadicFunction.h"
 #include "llvm/Support/type_traits.h"
 #include <map>
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -29,76 +29,6 @@
 
 typedef MatchFinder::MatchCallback MatchCallback;
 
-/// \brief A \c RecursiveASTVisitor that builds a map from nodes to their
-/// parents as defined by the \c RecursiveASTVisitor.
-///
-/// Note that the relationship described here is purely in terms of AST
-/// traversal - there are other relationships (for example declaration context)
-/// in the AST that are better modeled by special matchers.
-///
-/// FIXME: Currently only builds up the map using \c Stmt and \c Decl nodes.
-class ParentMapASTVisitor : public RecursiveASTVisitor<ParentMapASTVisitor> {
-public:
-  /// \brief Contains parents of a node.
-  typedef SmallVector<ast_type_traits::DynTypedNode, 1> ParentVector;
-
-  /// \brief Maps from a node to its parents.
-  typedef llvm::DenseMap<const void *, ParentVector> 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; }
-  // Disables data recursion. We intercept Traverse* methods in the RAV, which
-  // are not triggered during data recursion.
-  bool shouldUseDataRecursionFor(clang::Stmt *S) const { return false; }
-
-  template <typename T>
-  bool TraverseNode(T *Node, bool (VisitorBase::*traverse)(T*)) {
-    if (Node == NULL)
-      return true;
-    if (ParentStack.size() > 0)
-      // FIXME: Currently we add the same parent multiple times, for example
-      // when we visit all subexpressions of template instantiations; this is
-      // suboptimal, bug benign: the only way to visit those is with
-      // hasAncestor / hasParent, and those do not create new matches.
-      // The plan is to enable DynTypedNode to be storable in a map or hash
-      // map. The main problem there is to implement hash functions /
-      // comparison operators for all types that DynTypedNode supports that
-      // do not have pointer identity.
-      (*Parents)[Node].push_back(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;
-  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
@@ -458,12 +388,6 @@
                                  const DynTypedMatcher &Matcher,
                                  BoundNodesTreeBuilder *Builder,
                                  AncestorMatchMode MatchMode) {
-    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()));
-    }
     return matchesAncestorOfRecursively(Node, Matcher, Builder, MatchMode);
   }
 
@@ -506,22 +430,21 @@
     assert(Node.getMemoizationData() &&
            "Invariant broken: only nodes that support memoization may be "
            "used in the parent map.");
-    ParentMapASTVisitor::ParentMap::const_iterator I =
-        Parents->find(Node.getMemoizationData());
-    if (I == Parents->end()) {
+    ASTContext::ParentVector Parents = ActiveASTContext->getParents(Node);
+    if (Parents.empty()) {
       assert(false && "Found node that is not in the parent map.");
       return false;
     }
-    for (ParentMapASTVisitor::ParentVector::const_iterator AncestorI =
-             I->second.begin(), AncestorE = I->second.end();
+    for (ASTContext::ParentVector::const_iterator AncestorI = Parents.begin(),
+                                                  AncestorE = Parents.end();
          AncestorI != AncestorE; ++AncestorI) {
       if (Matcher.matches(*AncestorI, this, Builder))
         return true;
     }
     if (MatchMode == ASTMatchFinder::AMM_ParentOnly)
       return false;
-    for (ParentMapASTVisitor::ParentVector::const_iterator AncestorI =
-             I->second.begin(), AncestorE = I->second.end();
+    for (ASTContext::ParentVector::const_iterator AncestorI = Parents.begin(),
+                                                  AncestorE = Parents.end();
          AncestorI != AncestorE; ++AncestorI) {
       if (matchesAncestorOfRecursively(*AncestorI, Matcher, Builder, MatchMode))
         return true;
@@ -574,8 +497,6 @@
   // Maps (matcher, node) -> the match result for memoization.
   typedef llvm::DenseMap<UntypedMatchInput, MemoizedMatchResult> MemoizationMap;
   MemoizationMap ResultCache;
-
-  OwningPtr<ParentMapASTVisitor::ParentMap> Parents;
 };
 
 // Returns true if the given class is directly or indirectly derived
Index: unittests/AST/ASTContextParentMapTest.cpp
===================================================================
--- /dev/null
+++ unittests/AST/ASTContextParentMapTest.cpp
@@ -0,0 +1,71 @@
+//===- unittest/AST/ASTContextParentMapTest.cpp - AST parent map test -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for the getParents(...) methods of ASTContext.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+#include "MatchVerifier.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using clang::tooling::newFrontendActionFactory;
+using clang::tooling::runToolOnCodeWithArgs;
+using clang::tooling::FrontendActionFactory;
+
+TEST(GetParents, ReturnsParentForDecl) {
+  MatchVerifier<Decl> Verifier;
+  EXPECT_TRUE(Verifier.match("class C { void f(); };",
+                             methodDecl(hasParent(recordDecl(hasName("C"))))));
+}
+
+TEST(GetParents, ReturnsParentForStmt) {
+  MatchVerifier<Stmt> Verifier;
+  EXPECT_TRUE(Verifier.match("class C { void f() { if (true) {} } };",
+                             ifStmt(hasParent(compoundStmt()))));
+}
+
+TEST(GetParents, ReturnsParentInsideTemplateInstantiations) {
+  MatchVerifier<Decl> DeclVerifier;
+  EXPECT_TRUE(DeclVerifier.match(
+      "template<typename T> struct C { void f() {} };"
+      "void g() { C<int> c; c.f(); }",
+      methodDecl(hasName("f"),
+                 hasParent(recordDecl(isTemplateInstantiation())))));
+  EXPECT_TRUE(DeclVerifier.match(
+      "template<typename T> struct C { void f() {} };"
+      "void g() { C<int> c; c.f(); }",
+      methodDecl(hasName("f"),
+                 hasParent(recordDecl(unless(isTemplateInstantiation()))))));
+  EXPECT_FALSE(DeclVerifier.match(
+      "template<typename T> struct C { void f() {} };"
+      "void g() { C<int> c; c.f(); }",
+      methodDecl(hasName("f"),
+                 allOf(hasParent(recordDecl(unless(isTemplateInstantiation()))),
+                       hasParent(recordDecl(isTemplateInstantiation()))))));
+}
+
+TEST(GetParents, ReturnsMultipleParentsInTemplateInstantiations) {
+  MatchVerifier<Stmt> TemplateVerifier;
+  EXPECT_TRUE(TemplateVerifier.match(
+      "template<typename T> struct C { void f() {} };"
+      "void g() { C<int> c; c.f(); }",
+      compoundStmt(
+          allOf(hasAncestor(recordDecl(isTemplateInstantiation())),
+                hasAncestor(recordDecl(unless(isTemplateInstantiation())))))));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
Index: unittests/AST/CMakeLists.txt
===================================================================
--- unittests/AST/CMakeLists.txt
+++ unittests/AST/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_clang_unittest(ASTTests
+  ASTContextParentMapTest.cpp
   CommentLexer.cpp
   CommentParser.cpp
   DeclPrinterTest.cpp
Index: unittests/AST/MatchVerifier.h
===================================================================
--- unittests/AST/MatchVerifier.h
+++ unittests/AST/MatchVerifier.h
@@ -44,7 +44,7 @@
 protected:
   virtual void run(const MatchFinder::MatchResult &Result);
   virtual void verify(const MatchFinder::MatchResult &Result,
-                      const NodeType &Node) = 0;
+                      const NodeType &Node) {}
 
   void setFailure(const Twine &Result) {
     Verified = false;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to