martong created this revision.
martong added reviewers: klimek, aprantl, pcc, sbenza, Prazek, dblaikie, 
balazske, xazax.hun.
Herald added subscribers: cfe-commits, dkrupp, rnkovacs.

Add matchSubtree, so we can traverse on a subtree rooted on a specific node.

Currently, we can match **one** node against a matcher, but we will not traverse
into the children (this is MatchFinder::match).
Or we can traverse through the whole tree rooted at the TUDecl (this
is MatchFinder::matchAST).
Note, findAll may provide an alternative, but that will traverse throught the
whole AST, and that has some weaknesses:
https://bugs.llvm.org/show_bug.cgi?id=38318


Repository:
  rC Clang

https://reviews.llvm.org/D49840

Files:
  include/clang/ASTMatchers/ASTMatchFinder.h
  lib/ASTMatchers/ASTMatchFinder.cpp
  unittests/ASTMatchers/ASTMatchersInternalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersInternalTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersInternalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersInternalTest.cpp
@@ -236,5 +236,53 @@
 
 #endif // _WIN32
 
+
+TEST(Matcher, matchSubtree) {
+
+  std::unique_ptr<ASTUnit> AST =
+      clang::tooling::buildASTFromCode(
+      R"(
+      template <typename T>
+      struct X {
+        void f() {}
+        void g() {}
+      };
+      void foo() {
+          X<char> xc;
+          xc.f();
+          X<int> xi;
+      }
+      )");
+  ASSERT_TRUE(AST.get());
+
+  // To get the "f" FunctionDecl inside the instantiation ...
+  auto FullPattern =
+      functionDecl(hasName("f")
+          // ... we must specify the parent.
+          ,hasParent(classTemplateSpecializationDecl()));
+  auto *FunD = selectFirst<FunctionDecl>(
+      "dontcare", match(FullPattern.bind("dontcare"), AST->getASTContext()));
+
+
+  auto *SpecD = selectFirst<ClassTemplateSpecializationDecl>(
+      "dontcare", match(classTemplateSpecializationDecl().bind("dontcare"),
+                        AST->getASTContext()));
+  MatchFinder Finder;
+  struct TestCallback : MatchFinder::MatchCallback {
+    FunctionDecl *Node = nullptr;
+    void run(const MatchFinder::MatchResult &Result) override {
+      Node =
+          const_cast<FunctionDecl *>(Result.Nodes.getNodeAs<FunctionDecl>(""));
+    }
+  };
+  TestCallback Cb;
+
+  // With matchSubtree we can traverse through the given instantiation.
+  auto SimplePattern = functionDecl(hasName("f"));
+  Finder.addMatcher(SimplePattern.bind(""), &Cb);
+  Finder.matchSubtree(*SpecD, AST->getASTContext());
+  EXPECT_EQ(FunD, Cb.Node);
+}
+
 } // end namespace ast_matchers
 } // end namespace clang
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -1015,6 +1015,32 @@
   Visitor.match(Node);
 }
 
+void MatchFinder::matchSubtree(const clang::ast_type_traits::DynTypedNode &Node,
+                               ASTContext &Context) {
+  internal::MatchASTVisitor Visitor(&Matchers, Options);
+  Visitor.set_active_ast_context(&Context);
+  // FIXME: Improve this with a switch or a visitor pattern.
+  if (auto *N = Node.get<Decl>()) {
+    if (dyn_cast<TranslationUnitDecl>(N))
+      Visitor.onStartOfTranslationUnit();
+    Visitor.TraverseDecl(const_cast<Decl *>(N));
+    if (dyn_cast<TranslationUnitDecl>(N))
+      Visitor.onEndOfTranslationUnit();
+  } else if (auto *N = Node.get<Stmt>()) {
+    Visitor.TraverseStmt(const_cast<Stmt *>(N));
+  } else if (auto *N = Node.get<QualType>()) {
+    Visitor.TraverseType(*N);
+  } else if (auto *N = Node.get<TypeLoc>()) {
+    Visitor.TraverseTypeLoc(*N);
+  } else if (auto *N = Node.get<NestedNameSpecifier>()) {
+    Visitor.TraverseNestedNameSpecifier(const_cast<NestedNameSpecifier *>(N));
+  } else if (auto *N = Node.get<NestedNameSpecifierLoc>()) {
+    Visitor.TraverseNestedNameSpecifierLoc(*N);
+  } else if (auto *N = Node.get<CXXCtorInitializer>()) {
+    Visitor.TraverseConstructorInitializer(const_cast<CXXCtorInitializer *>(N));
+  }
+}
+
 void MatchFinder::matchAST(ASTContext &Context) {
   internal::MatchASTVisitor Visitor(&Matchers, Options);
   Visitor.set_active_ast_context(&Context);
Index: include/clang/ASTMatchers/ASTMatchFinder.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchFinder.h
+++ include/clang/ASTMatchers/ASTMatchFinder.h
@@ -189,6 +189,15 @@
              ASTContext &Context);
   /// @}
 
+  /// \brief Finds all matches in the given subtree rooted at \p Node
+  /// @{
+  template <typename T> void matchSubtree(const T &Node, ASTContext &Context) {
+    matchSubtree(clang::ast_type_traits::DynTypedNode::create(Node), Context);
+  }
+  void matchSubtree(const clang::ast_type_traits::DynTypedNode &Node,
+                    ASTContext &Context);
+  /// @}
+
   /// Finds all matches in the given AST.
   void matchAST(ASTContext &Context);
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to