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