Hi klimek, doug.gregor, gribozavr,

To support finding bound nodes found anywhere previously in the match
expression from a matcher that was provided a BoundNodesTreeBuilder
building a sub-tree of the full bound-nodes tree, every Builder
requires:
1) A pointer back to the root
2) A way of building a bound-nodes tree of results *so far* where these
results may be later thrown away.

Item 1 is handled by adding a root pointer to each builder. To make
interface harder to mis-use Builders can only be created by a static
'root creator' function or by Grafters.
Item 2 is handled by the introduction of the "Grafter" class. It
represents 'temporary' results which may appear in a tree created from
a Builder. If a Grafter is discarded, the results are thrown away.
Grafter is intrusively ref-counted so that it can serve as an RAII
object but still be created by a Builder and ownership passed to the
caller (auto_ptr would have worked but llvm::OwningPtr will not).

This revision introduces the "equalsBoundNode()" matcher currently
implemented for QualType, Stmt, and Decl which allows it to replace
existing "equalsNode()" uses.

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

Files:
  include/clang/ASTMatchers/ASTMatchers.h
  include/clang/ASTMatchers/ASTMatchersInternal.h
  lib/ASTMatchers/ASTMatchFinder.cpp
  lib/ASTMatchers/ASTMatchersInternal.cpp
  unittests/ASTMatchers/ASTMatchersTest.cpp
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -52,6 +52,11 @@
 #include "llvm/Support/Regex.h"
 #include <iterator>
 
+// DEBUG
+#include "llvm/Support/raw_ostream.h"
+#include "clang/AST/ASTContext.h"
+////////
+
 namespace clang {
 namespace ast_matchers {
 
@@ -85,6 +90,8 @@
   }
   /// @}
 
+  const internal::BoundNodesMap &getNodeMap() const { return MyBoundNodes; }
+
 private:
   /// \brief Create BoundNodes from a pre-filled map of bindings.
   BoundNodes(internal::BoundNodesMap &MyBoundNodes)
@@ -1841,6 +1848,8 @@
 /// varDecl(hasType(qualType(hasCanonicalType(referenceType())))))) does.
 AST_MATCHER_P(QualType, hasCanonicalType, internal::Matcher<QualType>,
               InnerMatcher) {
+  if (Node.isNull())
+    return false;
   return InnerMatcher.matches(Node.getCanonicalType(), Finder, Builder);
 }
 
@@ -3329,6 +3338,12 @@
   return &Node == Other;
 }
 
+inline internal::PolymorphicMatcherWithParam1<internal::EqualsBoundNodeMatcher,
+                                              llvm::StringRef>
+equalsBoundNode(StringRef id) {
+  return internal::PolymorphicMatcherWithParam1<
+      internal::EqualsBoundNodeMatcher, llvm::StringRef>(id);
+}
 /// @}
 
 } // end namespace ast_matchers
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -42,6 +42,7 @@
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/Type.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/VariadicFunction.h"
 #include "llvm/Support/type_traits.h"
 #include <map>
@@ -60,10 +61,14 @@
 
 namespace internal {
 
-class BoundNodesTreeBuilder;
 /// \brief Internal version of BoundNodes. Holds all the bound nodes.
 class BoundNodesMap {
 public:
+  /// \brief A map from IDs to the bound nodes.
+  typedef std::map<std::string, ast_type_traits::DynTypedNode> IDToNodeMap;
+  typedef IDToNodeMap::const_iterator const_iterator;
+
+public:
   /// \brief Adds \c Node to the map with key \c ID.
   ///
   /// The node's base type should be in NodeBaseType or it will be unaccessible.
@@ -88,16 +93,13 @@
     return It->second.get<T>();
   }
 
-  /// \brief Copies all ID/Node pairs to BoundNodesTreeBuilder \c Builder.
-  void copyTo(BoundNodesTreeBuilder *Builder) const;
-
   /// \brief Copies all ID/Node pairs to BoundNodesMap \c Other.
   void copyTo(BoundNodesMap *Other) const;
 
-private:
-  /// \brief A map from IDs to the bound nodes.
-  typedef std::map<std::string, ast_type_traits::DynTypedNode> IDToNodeMap;
+  const_iterator begin() const { return NodeMap.begin(); }
+  const_iterator end() const { return NodeMap.end(); }
 
+private:
   IDToNodeMap NodeMap;
 };
 
@@ -130,14 +132,26 @@
   BoundNodesTree(const BoundNodesMap& Bindings,
                  const std::vector<BoundNodesTree> RecursiveBindings);
 
-  /// \brief Adds all bound nodes to \c Builder.
-  void copyTo(BoundNodesTreeBuilder* Builder) const;
-
   /// \brief Visits all matches that this BoundNodesTree represents.
   ///
   /// The ownership of 'ResultVisitor' remains at the caller.
   void visitMatches(Visitor* ResultVisitor);
 
+  /// \brief Accessor for the bound nodes for this node in the bound-nodes
+  /// tree.
+  const BoundNodesMap &getBoundNodes() const { return Bindings; }
+  
+  /// \brief Iterator to the beginning of the vector of children of this
+  /// node in the bound-nodes tree.
+  std::vector<BoundNodesTree>::const_iterator branches_begin() const {
+    return RecursiveBindings.begin();
+  }
+  /// \brief Iterator to the end of the vector of children of this node in the
+  /// bound-nodes tree.
+  std::vector<BoundNodesTree>::const_iterator branches_end() const {
+    return RecursiveBindings.end();
+  }
+
 private:
   void visitMatchesRecursively(
       Visitor* ResultVistior,
@@ -157,7 +171,55 @@
 /// nodes from the Id matcher.
 class BoundNodesTreeBuilder {
 public:
-  BoundNodesTreeBuilder();
+  class Grafter : public llvm::RefCountedBase<Grafter> {
+  public:
+    enum Kind {
+      GK_Branch,
+      GK_Merge
+    };
+    ~Grafter();
+
+    BoundNodesTreeBuilder *getBuilder();
+    const BoundNodesTreeBuilder *getBuilder() const;
+
+    Kind getKind() const { return GraftKind; }
+
+    void apply();
+
+    friend class BoundNodesTreeBuilder;
+  private:
+    Grafter(BoundNodesTreeBuilder *Parent,
+            Kind GraftKind);
+
+    BoundNodesTreeBuilder *Parent;
+    llvm::OwningPtr<BoundNodesTreeBuilder> SubTreeBuilder;
+    Kind GraftKind;
+  };
+
+  typedef llvm::IntrusiveRefCntPtr<Grafter> GraftPtr;
+
+public:
+  /// \brief Create a new root BoundNodesTreeBuilder.
+  static BoundNodesTreeBuilder *createRootBuilder() {
+    return new BoundNodesTreeBuilder(0);
+  }
+
+  /// \brief Creates a Grafter object to represent a BoundNodesTreeBuilder
+  /// that, upon graft application, will be added as a branch of the tree this
+  /// builder is building.
+  GraftPtr makeBranch();
+
+  /// \brief Creates a Grafter object that represents a BoundNodesTreeBuilder
+  /// whose tree will be merged into the tree this builder is building upon
+  /// graft application.
+  GraftPtr makeCheckpoint();
+
+  /// \brief Returns a reference to the builder for the root of the tree being
+  /// built. 'this' may only be a subtree of the root depending on which
+  /// matchers have been used in the match expression.
+  const BoundNodesTreeBuilder *getRoot() const {
+    return (Root == 0 ? this : Root);
+  }
 
   /// \brief Add a binding from an id to a node.
   template <typename T>
@@ -168,19 +230,37 @@
     Bindings.addNode(Id, Node);
   }
 
-  /// \brief Adds a branch in the tree.
-  void addMatch(const BoundNodesTree& Bindings);
+  /// \brief Add the bound nodes and branches of a bound-nodes tree to the
+  /// bound nodes and branches of the tree this builder is building.
+  void mergeFrom(const BoundNodesTree& Other);
 
   /// \brief Returns a BoundNodes object containing all current bindings.
   BoundNodesTree build() const;
 
 private:
   BoundNodesTreeBuilder(const BoundNodesTreeBuilder &) LLVM_DELETED_FUNCTION;
   void operator=(const BoundNodesTreeBuilder &) LLVM_DELETED_FUNCTION;
 
-  BoundNodesMap Bindings;
+  BoundNodesTreeBuilder(const BoundNodesTreeBuilder *Root);
+
+  /// \brief Indicates to the builder the graft can be discarded. Called by
+  /// Grafter instances when they destruct. Another graft may be created
+  /// immediately upon return.
+  void discardGraft();
 
+  /// \brief Indicates to the builder that the current state of the graft can
+  /// be applied to the tree being built. Called by Grafter instances. Calling
+  /// this function disassociates the current grafter from the Builder allowing
+  /// for another graft to be created immediately upon return.
+  void applyGraft();
+
+  /// \brief Whether or not this builder has an active graft.
+  bool hasGraft() const { return Graft != 0; }
+
+  BoundNodesMap Bindings;
   std::vector<BoundNodesTree> RecursiveBindings;
+  const BoundNodesTreeBuilder *Root;
+  const Grafter *Graft;
 };
 
 class ASTMatchFinder;
@@ -914,15 +994,16 @@
 
   virtual bool matches(const T &Node, ASTMatchFinder *Finder,
                        BoundNodesTreeBuilder *Builder) const {
-    BoundNodesTreeBuilder Builder1;
-    bool Matched1 = InnerMatcher1.matches(Node, Finder, &Builder1);
+    BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeBranch();
+    bool Matched1 = InnerMatcher1.matches(Node, Finder, Graft->getBuilder());
     if (Matched1)
-      Builder->addMatch(Builder1.build());
+      Graft->apply();
 
-    BoundNodesTreeBuilder Builder2;
-    bool Matched2 = InnerMatcher2.matches(Node, Finder, &Builder2);
+    Graft.reset();
+    Graft = Builder->makeBranch();
+    bool Matched2 = InnerMatcher2.matches(Node, Finder, Graft->getBuilder());
     if (Matched2)
-      Builder->addMatch(Builder2.build());
+      Graft->apply();
 
     return Matched1 || Matched2;
   }
@@ -1238,6 +1319,80 @@
   return T(makeAllOfComposite<InnerT>(InnerMatchers));
 }
 
+/// \brief A BoundNodesTree Visitor for finding a bound node with a given id.
+class BoundNodeFinder : public BoundNodesTree::Visitor {
+public:
+  BoundNodeFinder(StringRef Id) : Id(Id), Found(false) {}
+
+  virtual void visitMatch(const BoundNodes& Nodes) LLVM_OVERRIDE;
+
+  template <typename T>
+  const T *getResult() const {
+    return Result.get<T>();
+  }
+
+private:
+  StringRef Id;
+  bool Found;
+  ast_type_traits::DynTypedNode Result;
+};
+
+/// \brief Matches nodes of type \c T that \em equal another node bound with the
+/// id provided to the constructor. Bound nodes that are not of type \c T will
+/// never match.
+///
+/// Currently implemented for T in {QualType, Decl, Stmt}. For Decl and Stmt,
+/// equality is defined as pointer equality. For QualType, equality is defined
+/// by QualType::operator==().
+template <typename T, typename ArgT>
+class EqualsBoundNodeMatcher : public MatcherInterface<T> {
+  TOOLING_COMPILE_ASSERT((llvm::is_same<ArgT,
+                          llvm::StringRef>::value &&
+                          (llvm::is_same<T, Decl>::value ||
+                           llvm::is_same<T, Stmt>::value ||
+                           llvm::is_same<T, QualType>::value)),
+                         sameAs_matcher_requires_StringRef_argument);
+
+public:
+  explicit EqualsBoundNodeMatcher(llvm::StringRef Id) : Id(Id) {}
+
+  virtual bool matches(const T &Node,
+                       ASTMatchFinder *Finder,
+                       BoundNodesTreeBuilder *Builder) const LLVM_OVERRIDE {
+
+    // Build tree of matches from the root builder so we can see bound nodes
+    // found above our level of the tree.
+    BoundNodesTree BoundNodes = Builder->getRoot()->build();
+    BoundNodeFinder Visitor(Id);
+    BoundNodes.visitMatches(&Visitor);
+
+    const T *BoundNode = Visitor.getResult<T>();
+    if (!BoundNode) {
+      return false;
+    }
+    return matchesSpecialized(Node, *BoundNode, Finder, Builder);
+  }
+
+  // For QualType use QualType::operator==().
+  bool matchesSpecialized(const QualType &Node,
+                          const QualType &BoundNode,
+                          ASTMatchFinder *Finder,
+                          BoundNodesTreeBuilder *Builder) const {
+    return Node == BoundNode;
+  }
+
+  // For everything else, use pointer identity as the test.
+  template <typename U>
+  bool matchesSpecialized(const U &Node, const U &BoundNode,
+                          ASTMatchFinder *Finder,
+                          BoundNodesTreeBuilder *Builder) const {
+    return &Node == &BoundNode;
+  }
+
+private:
+  std::string Id;
+};
+
 } // end namespace internal
 } // end namespace ast_matchers
 } // end namespace clang
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -226,12 +226,12 @@
         return false;  // Abort as soon as a match is found.
       }
     } else {
-      BoundNodesTreeBuilder RecursiveBuilder;
+      BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeBranch();
       if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node),
-                           Finder, &RecursiveBuilder)) {
+                           Finder, Graft->getBuilder())) {
         // After the first match the matcher succeeds.
         Matches = true;
-        Builder->addMatch(RecursiveBuilder.build());
+        Graft->apply();
       }
     }
     return true;
@@ -342,14 +342,15 @@
     std::pair<MemoizationMap::iterator, bool> InsertResult
       = ResultCache.insert(std::make_pair(input, MemoizedMatchResult()));
     if (InsertResult.second) {
-      BoundNodesTreeBuilder DescendantBoundNodesBuilder;
+      BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeCheckpoint();
       InsertResult.first->second.ResultOfMatch =
-        matchesRecursively(Node, Matcher, &DescendantBoundNodesBuilder,
+        matchesRecursively(Node, Matcher, Graft->getBuilder(),
                            MaxDepth, Traversal, Bind);
-      InsertResult.first->second.Nodes =
-        DescendantBoundNodesBuilder.build();
+      InsertResult.first->second.Nodes = Graft->getBuilder()->build();
+      Graft->apply();
+    } else {
+      Builder->mergeFrom(InsertResult.first->second.Nodes);
     }
-    InsertResult.first->second.Nodes.copyTo(Builder);
     return InsertResult.first->second.ResultOfMatch;
   }
 
@@ -400,9 +401,10 @@
                                MatchCallback*> >::const_iterator
              I = MatcherCallbackPairs->begin(), E = MatcherCallbackPairs->end();
          I != E; ++I) {
-      BoundNodesTreeBuilder Builder;
-      if (I->first->matches(Node, this, &Builder)) {
-        BoundNodesTree BoundNodes = Builder.build();
+      llvm::OwningPtr<BoundNodesTreeBuilder> Builder(BoundNodesTreeBuilder::createRootBuilder());
+
+      if (I->first->matches(Node, this, Builder.get())) {
+        BoundNodesTree BoundNodes = Builder->build();
         MatchVisitor Visitor(ActiveASTContext, I->second);
         BoundNodes.visitMatches(&Visitor);
       }
@@ -453,25 +455,25 @@
     const UntypedMatchInput input(Matcher.getID(), Node.getMemoizationData());
     MemoizationMap::iterator I = ResultCache.find(input);
     if (I == ResultCache.end()) {
-      BoundNodesTreeBuilder AncestorBoundNodesBuilder;
+      BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeCheckpoint();
       bool Matches = false;
       if (Parents.size() == 1) {
         // Only one parent - do recursive memoization.
         const ast_type_traits::DynTypedNode Parent = Parents[0];
-        if (Matcher.matches(Parent, this, &AncestorBoundNodesBuilder)) {
+        if (Matcher.matches(Parent, this, Graft->getBuilder())) {
           Matches = true;
         } else if (MatchMode != ASTMatchFinder::AMM_ParentOnly) {
           Matches = memoizedMatchesAncestorOfRecursively(
-              Parent, Matcher, &AncestorBoundNodesBuilder, MatchMode);
+              Parent, Matcher, Graft->getBuilder(), MatchMode);
         }
       } else {
         // Multiple parents - BFS over the rest of the nodes.
         llvm::DenseSet<const void *> Visited;
         std::deque<ast_type_traits::DynTypedNode> Queue(Parents.begin(),
                                                         Parents.end());
         while (!Queue.empty()) {
           if (Matcher.matches(Queue.front(), this,
-                              &AncestorBoundNodesBuilder)) {
+                              Graft->getBuilder())) {
             Matches = true;
             break;
           }
@@ -494,10 +496,12 @@
 
       I = ResultCache.insert(std::make_pair(input, MemoizedMatchResult()))
           .first;
-      I->second.Nodes = AncestorBoundNodesBuilder.build();
+      I->second.Nodes = Graft->getBuilder()->build();
+      Graft->apply();
       I->second.ResultOfMatch = Matches;
+    } else {
+      Builder->mergeFrom(I->second.Nodes);
     }
-    I->second.Nodes.copyTo(Builder);
     return I->second.ResultOfMatch;
   }
 
Index: lib/ASTMatchers/ASTMatchersInternal.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchersInternal.cpp
+++ lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -14,18 +14,25 @@
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/ASTMatchers/ASTMatchersInternal.h"
 
+namespace {
+using namespace clang::ast_matchers::internal;
+
+/// \brief Helper function to copy the bound nodes and list of bounds-node tree
+/// branches from a BoundNodesTree and add them to provided containers.
+void mergeTree(BoundNodesMap &DestBindings,
+               std::vector<BoundNodesTree> &DestBranches,
+               const BoundNodesTree &Src) {
+  Src.getBoundNodes().copyTo(&DestBindings);
+  std::copy(Src.branches_begin(), Src.branches_end(),
+            std::back_inserter(DestBranches));
+}
+
+}
+
 namespace clang {
 namespace ast_matchers {
 namespace internal {
 
-void BoundNodesMap::copyTo(BoundNodesTreeBuilder *Builder) const {
-  for (IDToNodeMap::const_iterator It = NodeMap.begin();
-       It != NodeMap.end();
-       ++It) {
-    Builder->setBinding(It->first, It->second);
-  }
-}
-
 void BoundNodesMap::copyTo(BoundNodesMap *Other) const {
   for (IDToNodeMap::const_iterator I = NodeMap.begin(),
                                    E = NodeMap.end();
@@ -42,16 +49,6 @@
   : Bindings(Bindings),
     RecursiveBindings(RecursiveBindings) {}
 
-void BoundNodesTree::copyTo(BoundNodesTreeBuilder* Builder) const {
-  Bindings.copyTo(Builder);
-  for (std::vector<BoundNodesTree>::const_iterator
-         I = RecursiveBindings.begin(),
-         E = RecursiveBindings.end();
-       I != E; ++I) {
-    Builder->addMatch(*I);
-  }
-}
-
 void BoundNodesTree::visitMatches(Visitor* ResultVisitor) {
   BoundNodesMap AggregatedBindings;
   visitMatchesRecursively(ResultVisitor, AggregatedBindings);
@@ -72,14 +69,116 @@
   }
 }
 
-BoundNodesTreeBuilder::BoundNodesTreeBuilder() {}
+BoundNodesTreeBuilder::Grafter::Grafter(BoundNodesTreeBuilder *Parent,
+                                        Kind GraftKind)
+    : Parent(Parent),
+      SubTreeBuilder(new BoundNodesTreeBuilder(Parent->getRoot())),
+      GraftKind(GraftKind) {}
+
+BoundNodesTreeBuilder::Grafter::~Grafter() {
+  Parent->discardGraft();
+  SubTreeBuilder.reset();
+}
+
+BoundNodesTreeBuilder *BoundNodesTreeBuilder::Grafter::getBuilder() {
+  assert(SubTreeBuilder && "Graft Builder is NULL");
+  return SubTreeBuilder.get();
+}
+
+const BoundNodesTreeBuilder *
+BoundNodesTreeBuilder::Grafter::getBuilder() const {
+  return const_cast<Grafter*>(this)->getBuilder();
+}
+
+void BoundNodesTreeBuilder::Grafter::apply() {
+  Parent->applyGraft();
+  SubTreeBuilder.reset();
+}
+
+BoundNodesTreeBuilder::BoundNodesTreeBuilder(const BoundNodesTreeBuilder *Root)
+    : Root(Root), Graft(0) {}
+
+BoundNodesTreeBuilder::GraftPtr BoundNodesTreeBuilder::makeBranch() {
+  assert(Graft == 0 && "Graft already exists");
+  Grafter *RawGraft = new Grafter(this, Grafter::GK_Branch);
+  Graft = RawGraft;
+  return RawGraft;
+}
+
+BoundNodesTreeBuilder::GraftPtr BoundNodesTreeBuilder::makeCheckpoint() {
+  assert(Graft == 0 && "Graft already exists");
+  Grafter *RawGraft = new Grafter(this, Grafter::GK_Merge);
+  Graft = RawGraft;
+  return RawGraft;
+}
+
+void BoundNodesTreeBuilder::applyGraft() {
+  if (!Graft) {
+    return;
+  }
+
+  // applyGraft() is a permanent operation to the tree used as the tree is
+  // being built. Recursive application of grafts would imply permanently
+  // applying possibly temporary results. See build() for a recursive
+  // applicaiton of grafts.
+  assert(!Graft->getBuilder()->hasGraft() &&
+         "Can't apply a graft that has also has a graft");
+
+  const BoundNodesTree &SubTree = Graft->getBuilder()->build();
+
+  if (Graft->getKind() == Grafter::GK_Branch) {
+    RecursiveBindings.push_back(SubTree);
+  } else if (Graft->getKind() == Grafter::GK_Merge) {
+    mergeTree(Bindings, RecursiveBindings, SubTree);
+  }
+
+  discardGraft();
+}
+
+void BoundNodesTreeBuilder::discardGraft()
+{
+  Graft = 0;
+}
 
-void BoundNodesTreeBuilder::addMatch(const BoundNodesTree& Bindings) {
-  RecursiveBindings.push_back(Bindings);
+void BoundNodesTreeBuilder::mergeFrom(const BoundNodesTree& Other)
+{
+  mergeTree(Bindings, RecursiveBindings, Other);
 }
 
 BoundNodesTree BoundNodesTreeBuilder::build() const {
-  return BoundNodesTree(Bindings, RecursiveBindings);
+  if (!hasGraft()) {
+    return BoundNodesTree(Bindings, RecursiveBindings);
+  }
+
+  // Recursively apply grafts to copies of this builder's internal data.
+  BoundNodesTree TempSubTree = Graft->getBuilder()->build();
+  switch (Graft->getKind()) {
+  case Grafter::GK_Branch: {
+    std::vector<BoundNodesTree> TempBranches(RecursiveBindings);
+    TempBranches.push_back(TempSubTree);
+    return BoundNodesTree(Bindings, TempBranches);
+  }
+  case Grafter::GK_Merge: {
+    BoundNodesMap TempBindings(Bindings);
+    std::vector<BoundNodesTree> TempBranches(RecursiveBindings);
+    mergeTree(TempBindings, TempBranches, TempSubTree);
+    return BoundNodesTree(TempBindings, TempBranches);
+  }
+  }
+}
+
+void BoundNodeFinder::visitMatch(const BoundNodes& Nodes) {
+  if (Found) return;
+
+  for (BoundNodesMap::const_iterator I = Nodes.getNodeMap().begin(),
+                                     E = Nodes.getNodeMap().end();
+       I != E; ++I) {
+    if (Id.equals(I->first)) {
+      Result = I->second;
+      Found = true;
+      break;
+    }
+  }
 }
 
 } // end namespace internal
Index: unittests/ASTMatchers/ASTMatchersTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -3900,5 +3900,147 @@
   EXPECT_TRUE(VerifyCallback.Called);
 }
 
+TEST(EqualsBoundNodeMatcher, QualType) {
+  EXPECT_TRUE(matches("int i = 1;",
+                      varDecl(hasType(qualType().bind("type")),
+                              hasInitializer(ignoringParenImpCasts(hasType(qualType(equalsBoundNode("type"))))))));
+  EXPECT_TRUE(notMatches("int i = 1.f;",
+                      varDecl(hasType(qualType().bind("type")),
+                              hasInitializer(ignoringParenImpCasts(hasType(qualType(equalsBoundNode("type"))))))));
+}
+
+TEST(EqualsBoundNodeMatcher, NonMatchingTypes) {
+  EXPECT_TRUE(notMatches("int i = 1;",
+                         varDecl(namedDecl(hasName("i")).bind("name"),
+                                 hasInitializer(ignoringParenImpCasts(hasType(qualType(equalsBoundNode("type"))))))));
+}
+
+TEST(EqualsBoundNodeMatcher, Stmt) {
+  // A re-implementation of the test IsEqualTo.MatchesNodesByIdentity which uses
+  // equalsNode().
+  EXPECT_TRUE(matches("void f() { if(true) {} }",
+                      stmt(allOf(ifStmt().bind("if"),
+                                 hasParent(stmt(has(stmt(equalsBoundNode("if"))))
+                      )))));
+
+  EXPECT_TRUE(notMatches("void f() { if(true) { if (true) {} } }",
+                         stmt(allOf(ifStmt().bind("if"),
+                                    has(stmt(equalsBoundNode("if"))
+                         )))));
+}
+
+TEST(EqualsBoundNodeMatcher, Decl) {
+  // A re-implementation of the test IsEqualTo.MatchesNodesByIdentity which uses
+  // equalsNode().
+  EXPECT_TRUE(matches("class X { class Y {}; };",
+                      decl(allOf(recordDecl(hasName("::X::Y")).bind("record"),
+                                 hasParent(decl(has(decl(equalsBoundNode("record")))))
+                      ))));
+
+  EXPECT_TRUE(notMatches("class X { class Y {}; };",
+                         decl(allOf(recordDecl(hasName("::X")).bind("record"),
+                                    has(decl(equalsBoundNode("record")))
+                         ))));
+}
+
+TEST(SameAsMatcher, UsingForEachDescendant) {
+  const std::string Fragment = "\
+      int f() {       \
+        if (1) {      \
+          int i = 9;  \
+        }             \
+        int j = 10;   \
+        return j;     \
+      }               \
+      float g() {     \
+        int k = 9.0;  \
+        return k;     \
+      }";
+
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      Fragment,
+      // Look for variable declarations within functions whose type is the same
+      // as the function return type.
+      functionDecl(returns(qualType().bind("type")),
+                            forEachDescendant(
+                              varDecl(
+                                hasType(
+                                  qualType(
+                                    equalsBoundNode("type")))).bind("decl"))),
+      // Only i and j should match, not k.
+      new VerifyIdIsBoundTo<VarDecl>("decl", 2)));
+
+}
+
+TEST(EqualsBoundNodeMatcher, MatchesFirstBoundNode) {
+  // This extremely contrived matcher goes looking for VarDecls within IfStmts
+  // within FunctionDecls and binds the type of every found VarDecl with the id
+  // 'declType'. Then it looks for a call expression that's an immediate child
+  // of the FunctionDecl (technically within the function's CompoundStmt) and
+  // matches only if the return type of that function is the same as the
+  // *first* VarDecl found previously.
+  DeclarationMatcher matcher =
+    functionDecl(
+      forEachDescendant(
+        ifStmt(
+          forEachDescendant(
+            varDecl(hasType(qualType().bind("declType")))
+          )
+        )
+      ),
+      has(compoundStmt(has(
+        callExpr(
+          callee(
+            functionDecl(
+              returns(qualType(equalsBoundNode("declType")))
+            )
+          )
+        )
+      )))
+    );
+
+  // This fragment will match because the first varDecl found is 'int a' and
+  // the type matches the return type of iCall().
+  const std::string MatchingFragment = "\
+      int iCall();    \
+      int f() {       \
+        iCall();      \
+        if (1) {      \
+          {           \
+            int a;    \
+          }           \
+        }             \
+        while (1) {   \
+          if (1) {    \
+            float c;  \
+          }           \
+        }             \
+        return 0;     \
+      }               \
+  ";
+  // This fragment will NOT match because although a var decl is found with
+  // type float, the *first* one found is that of 'int a' and there's no call
+  // to a function whose return type is 'int'.
+  const std::string NegativeFragment = "\
+      float fCall();  \
+      int f() {       \
+        fCall();      \
+        if (1) {      \
+          {           \
+            int a;    \
+          }           \
+        }             \
+        while (1) {   \
+          if (1) {    \
+            float c;  \
+          }           \
+        }             \
+        return 0;     \
+      }               \
+  ";
+  EXPECT_TRUE(matches(MatchingFragment, matcher));
+  EXPECT_TRUE(notMatches(NegativeFragment, matcher));
+}
+
 } // 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