]> granicus.if.org Git - clang/commitdiff
First step towards adding a parent map to the ASTContext.
authorManuel Klimek <klimek@google.com>
Thu, 28 Feb 2013 13:21:39 +0000 (13:21 +0000)
committerManuel Klimek <klimek@google.com>
Thu, 28 Feb 2013 13:21:39 +0000 (13:21 +0000)
This does not yet implement the LimitNode approach discussed.

The impact of this is an O(n) in the number of nodes in the AST
reduction of complexity for certain kinds of matchers (as otherwise the
parent map gets recreated for every new MatchFinder).

See FIXMEs in the comments for the direction of future work.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@176251 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTContext.h
include/clang/AST/ASTTypeTraits.h [moved from include/clang/ASTMatchers/ASTTypeTraits.h with 95% similarity]
include/clang/ASTMatchers/ASTMatchersInternal.h
lib/ASTMatchers/ASTMatchFinder.cpp
unittests/AST/ASTContextParentMapTest.cpp [new file with mode: 0644]
unittests/AST/CMakeLists.txt
unittests/AST/MatchVerifier.h

index 7c02699ee42a231cb2d8b80043dc1a9ca8cb83d7..d12d0545c507f6518d430332d78141486a98757a 100644 (file)
@@ -15,6 +15,7 @@
 #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"
@@ -22,6 +23,7 @@
 #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"
@@ -382,6 +384,58 @@ public:
   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;
   }
@@ -2136,8 +2190,81 @@ private:
   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);
similarity index 95%
rename from include/clang/ASTMatchers/ASTTypeTraits.h
rename to include/clang/AST/ASTTypeTraits.h
index 1dc5441bbbe12c0c135a7d29ce40844decd81426..4688b12de7016f0d5cd8d10587484b04547911b1 100644 (file)
@@ -1,4 +1,4 @@
-//===--- ASTMatchersTypeTraits.h --------------------------------*- C++ -*-===//
+//===--- ASTTypeTraits.h ----------------------------------------*- C++ -*-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -12,8 +12,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H
-#define LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H
+#ifndef LLVM_CLANG_AST_AST_TYPE_TRAITS_H
+#define LLVM_CLANG_AST_AST_TYPE_TRAITS_H
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
@@ -88,8 +88,9 @@ private:
   /// 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.
@@ -207,4 +208,4 @@ inline const void *DynTypedNode::getMemoizationData() const {
 } // end namespace ast_type_traits
 } // end namespace clang
 
-#endif // LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H
+#endif // LLVM_CLANG_AST_AST_TYPE_TRAITS_H
index 1119a72a425fac300a9bd6f79d113fd6d3d1388a..f309794034076720b752f29699758a459d015f96 100644 (file)
 #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 3a3c1fb6e17d550aaa88b2a8932cdd5361877cd7..1af2f09c3efc64a064dce6f4f99f2cf0cdea3f1a 100644 (file)
@@ -29,76 +29,6 @@ namespace {
 
 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 @@ public:
                                  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 @@ private:
     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 @@ private:
   // 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
diff --git a/unittests/AST/ASTContextParentMapTest.cpp b/unittests/AST/ASTContextParentMapTest.cpp
new file mode 100644 (file)
index 0000000..c1910a8
--- /dev/null
@@ -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 1ea293ee83ae74d54ea3e6121498da2e1a24159b..ad29428220bfa89801e557e177ae8dc7b6d0134b 100644 (file)
@@ -1,4 +1,5 @@
 add_clang_unittest(ASTTests
+  ASTContextParentMapTest.cpp
   CommentLexer.cpp
   CommentParser.cpp
   DeclPrinterTest.cpp
index f0a58537048285e707b031522842e85e4bdb4924..7aa78860aa99750cd46bad7287ef19e41c6647d6 100644 (file)
@@ -44,7 +44,7 @@ public:
 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;