lukasza created this revision.
lukasza added a reviewer: rsmith.
lukasza added a subscriber: cfe-commits.
Herald added a subscriber: klimek.

The unit tests in this patch demonstrate the need to traverse template
parameter lists of DeclaratorDecls (e.g. VarDecls, CXXMethodDecls) and
TagDecls (e.g. EnumDecls, RecordDecls).  Without new traversal code
in RecursiveASTVisitor, the new unit tests would trigger an assert
failure in MatchASTVisitor::matchesAncestorOfRecursively while verifing
that all AST nodes have a parent:
  bool matchesAncestorOfRecursively(const ast_type_traits::DynTypedNode &Node,
                                    ...) {
    const auto &Parents = ActiveASTContext->getParents(Node);
    assert(!Parents.empty() && "Found node that is not in the parent map.");

Fixes PR29042.

https://reviews.llvm.org/D24268

Files:
  include/clang/AST/RecursiveASTVisitor.h
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -1720,6 +1720,68 @@
                         "}\n",
                       declRefExpr(to(decl(hasAncestor(decl()))))));
 }
+
+AST_MATCHER_P(QualType,
+              hasUnderlyingType,
+              internal::Matcher<Type>,
+              InnerMatcher) {
+  const Type* type = Node.getTypePtrOrNull();
+  return type && InnerMatcher.matches(*type, Finder, Builder);
+}
+
+TEST(HasAncestor, TemplateTypeParmDeclUnderCXXMethodDecl) {
+  EXPECT_TRUE(matches(
+      "template<typename T>\n"
+      "class Class {\n"
+      "  void method();\n"
+      "};\n"
+      "template<typename U>\n"
+      "void Class<U>::method() {}\n",
+      qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl(
+          hasAncestor(decl()))))))));
+}
+
+TEST(HasAncestor, TemplateTypeParmDeclUnderVarDecl) {
+  EXPECT_TRUE(matches(
+      "template<typename T>\n"
+      "class Class {\n"
+      "  static T pi;\n"
+      "};\n"
+      "template<typename U>\n"
+      "U Class<U>::pi = U(3.1415926535897932385);\n",
+      qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl(
+          hasAncestor(decl()))))))));
+}
+
+TEST(HasAncestor, TemplateTypeParmDeclUnderEnumDecl) {
+  EXPECT_TRUE(matches(
+      "template<typename T>\n"
+      "struct Struct {\n"
+      "  enum class Enum : T;\n"
+      "};\n"
+      "template<typename U>\n"
+      "enum class Struct<U>::Enum : U {\n"
+      "  e1,\n"
+      "  e2\n"
+      "};\n",
+      qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl(
+          hasAncestor(decl()))))))));
+}
+
+TEST(HasAncestor, TemplateTypeParmDeclUnderStructDecl) {
+  EXPECT_TRUE(matches(
+      "template<typename T>\n"
+      "class Class {\n"
+      "  struct Struct;\n"
+      "};\n"
+      "template<typename U>\n"
+      "struct Class<U>::Struct {\n"
+      "  U field;\n"
+      "};\n",
+      qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl(
+          hasAncestor(decl()))))))));
+}
+
 TEST(HasAncestor, NonParmDependentTemplateParmVarDeclRefExpr) {
   EXPECT_TRUE(matches("struct PartitionAllocator {\n"
                         "  template<typename T>\n"
Index: include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -482,6 +482,13 @@
 private:
   // These are helper methods used by more than one Traverse* method.
   bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
+
+  // Traverses template parameter lists of either a DeclaratorDecl or TagDecl.
+  template <typename T, typename Enabled = typename std::enable_if<
+      std::is_base_of<DeclaratorDecl, T>::value ||
+      std::is_base_of<TagDecl, T>::value>::type>
+  bool TraverseDeclTemplateParameterLists(T *D);
+
 #define DEF_TRAVERSE_TMPL_INST(TMPLDECLKIND)                                   \
   bool TraverseTemplateInstantiations(TMPLDECLKIND##TemplateDecl *D);
   DEF_TRAVERSE_TMPL_INST(Class)
@@ -1532,6 +1539,16 @@
 }
 
 template <typename Derived>
+template <typename T, typename Enabled>
+bool RecursiveASTVisitor<Derived>::TraverseDeclTemplateParameterLists(T *D) {
+  for (unsigned i = 0; i < D->getNumTemplateParameterLists(); i++) {
+    TemplateParameterList *TPL = D->getTemplateParameterList(i);
+    TraverseTemplateParameterListHelper(TPL);
+  }
+  return true;
+}
+
+template <typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseTemplateInstantiations(
     ClassTemplateDecl *D) {
   for (auto *SD : D->specializations()) {
@@ -1692,6 +1709,8 @@
 })
 
 DEF_TRAVERSE_DECL(EnumDecl, {
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
+
   if (D->getTypeForDecl())
     TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
 
@@ -1706,6 +1725,7 @@
   // We shouldn't traverse D->getTypeForDecl(); it's a result of
   // declaring the type, not something that was written in the source.
 
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
   TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
   return true;
 }
@@ -1800,6 +1820,7 @@
 
 template <typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseDeclaratorHelper(DeclaratorDecl *D) {
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
   TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
   if (D->getTypeSourceInfo())
     TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
@@ -1846,6 +1867,7 @@
 
 template <typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
   TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
   TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to