Re: [PATCH] D24361: hasDeclaration(qualType(...)) matcher should unwrap ElaboratedType and TemplateSpecializationType
lukasza added a comment. Richard, could you please take a look? Comment at: include/clang/ASTMatchers/ASTMatchersInternal.h:750 @@ +749,3 @@ +else if (auto *ET = Node->getAs()) + return matchesSpecialized(ET->getNamedType(), Finder, Builder); +else if (auto *TST = Node->getAs()) There is also a question whether I should not only start supporting not only qualType(hasDeclaration(...)) when qualType wraps an ElaboratedType node, but also start supporting elaboratedType(hasDeclaration(...)). Snippets of a patch that could achieve this (but then again - I am not sure if this is desirable and/or necessary): +++ include/clang/ASTMatchers/ASTMatchersInternal.h (working copy) ... + /// \brief Gets the QualType from ElaboratedType + /// and returns whether the inner matches on it. + bool matchesSpecialized(const ElaboratedType &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { +return matchesSpecialized(Node.getNamedType(), Finder, Builder); + } + ... /// \brief All types that are supported by HasDeclarationMatcher above. -typedef TypeList HasDeclarationSupportedTypes; Comment at: include/clang/ASTMatchers/ASTMatchersInternal.h:753 @@ -749,2 +752,3 @@ + return matchesSpecialized(*TST, Finder, Builder); else if (auto *TT = Node->getAs()) return matchesDecl(TT->getDecl(), Finder, Builder); I do notice that there are quite a few similar Node->getAs if statements here. They could be replaced with something "clever" (and probably unreadable): +++ include/clang/ASTMatchers/ASTMatchersInternal.h (working copy) ... + /// \brief Terminating case for recursion over template parameters + /// performed by matchesQualTypeAsAnyOf template. + template + bool matchesQualTypeAsAnyOf(const QualType &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { +return false; + } + + /// \brief Returns true if Node->getAs is not null and matches inner + /// matcher for any type U listed in template parameters. + template ::value, int>::type = 0> + bool matchesQualTypeAsAnyOf(const QualType &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { +if (const U *u = Node->getAs()) + return matchesSpecialized(*u, Finder, Builder); + +return matchesQualTypeAsAnyOf(Node, Finder, Builder); + } + ... -else if (auto *OCIT = Node->getAs()) - return matchesDecl(OCIT->getDecl(), Finder, Builder); -else if (auto *UUT = Node->getAs()) - return matchesDecl(UUT->getDecl(), Finder, Builder); -else if (auto *ICNT = Node->getAs()) - return matchesDecl(ICNT->getDecl(), Finder, Builder); -return false; +else if (auto *TD = Node->getAsTagDecl()) + return matchesDecl(TD, Finder, Builder); + +return matchesQualTypeAsAnyOf< +ArrayType, InjectedClassNameType, ObjCInterfaceType, TemplateSpecializationType, +TypedefType, UnresolvedUsingType, void>( +Node, Finder, Builder); Comment at: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:2124 @@ +2123,3 @@ + // hasDeclaration should see through: + // 1. from ElaboratedType (Namespace::MyTemplate) to + //TemplateSpecializationType (MyTemplate) FWIW, I couldn't repro a similar issue (i.e. VarDecl or ParmVarDecl of a type with user-provided declaration, but where hasDeclaration doesn't match *anything*) for other types (i.e. AutoType or ArrayType) - only for ElaboratedType and TemplateSpecializationType. https://reviews.llvm.org/D24361 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D24361: hasDeclaration(qualType(...)) matcher should unwrap ElaboratedType and TemplateSpecializationType
lukasza updated the summary for this revision. lukasza updated this revision to Diff 72350. lukasza marked an inline comment as done. lukasza added a comment. - Added test where both TemplateSpecializationType and TypedefType are present and both should match regardless of code order inside HasDeclarationMatcher::matchesSpecialized(const QualType &Node...). Removed significance of order inside this matchesSpecialized method. - Tweaked test proposed in the previous patchset, so that it matches specific decls (rather than any decls) - Added support for matching hasDeclaration(elaboratedType(...)) + test (in the test from the previous patchset). https://reviews.llvm.org/D24361 Files: include/clang/ASTMatchers/ASTMatchers.h include/clang/ASTMatchers/ASTMatchersInternal.h unittests/ASTMatchers/ASTMatchersTraversalTest.cpp Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp === --- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -2106,5 +2106,56 @@ functionDecl(hasName("bar")); } +TEST(HasDeclaration, ElaboratedTypeAndTemplateSpecializationType) { + std::string input = + "namespace Namespace {\n" + "template\n" + "class Template {\n" + " public:\n" + " void Method() {}\n" + "};\n" + "} // namespace Namespace\n" + "template \n" + "void Function(Namespace::Template param) {\n" + " param.Method();\n" + "};\n"; + + // Matcher for ::Namespace::Template template decl. + auto param_type_decl_matcher = classTemplateDecl( + hasName("Template"), + hasParent(namespaceDecl(hasName("Namespace"))), + has(templateTypeParmDecl(hasName("T"; + + // hasDeclaration / qualType-flavour. + EXPECT_TRUE(matches(input, parmVarDecl( + hasName("param"), + hasType(qualType(hasDeclaration(decl(param_type_decl_matcher))); + + // hasDeclaration / elaboratedType-flavour. + EXPECT_TRUE(matches(input, parmVarDecl( + hasName("param"), + hasType(elaboratedType(hasDeclaration(decl(param_type_decl_matcher))); +} + +TEST(HasDeclaration, TypedefTypeAndTemplateSpecializationTypeAreBothHere) { + std::string input = + "template\n" + "class Foo {};\n" + "using Bar = Foo;\n" + "Bar variable;\n"; + + // hasDeclaration should match typedefNameDecl. + EXPECT_TRUE(matches(input, varDecl( + hasName("variable"), + hasType(qualType(hasDeclaration(decl( + typeAliasDecl(hasName("Bar"); + + // hasDeclaration should match templateSpecializationType + EXPECT_TRUE(matches(input, varDecl( + hasName("variable"), + hasType(qualType(hasDeclaration(decl( + classTemplateSpecializationDecl(; +} + } // namespace ast_matchers } // namespace clang Index: include/clang/ASTMatchers/ASTMatchersInternal.h === --- include/clang/ASTMatchers/ASTMatchersInternal.h +++ include/clang/ASTMatchers/ASTMatchersInternal.h @@ -744,16 +744,29 @@ if (Node.isNull()) return false; -if (auto *TD = Node->getAsTagDecl()) - return matchesDecl(TD, Finder, Builder); -else if (auto *TT = Node->getAs()) - return matchesDecl(TT->getDecl(), Finder, Builder); // Do not use getAs instead of the direct dyn_cast. // Calling getAs will return the canonical type, but that type does not // store a TemplateTypeParmDecl. We *need* the uncanonical type, if it is // available, and using dyn_cast ensures that. -else if (auto *TTP = dyn_cast(Node.getTypePtr())) +if (auto *TTP = dyn_cast(Node.getTypePtr())) return matchesDecl(TTP->getDecl(), Finder, Builder); + +// Try if any of desugaring results match +// (these are not mutually exclusive). +if (auto *TST = Node->getAs()) { + if (matchesSpecialized(*TST, Finder, Builder)) +return true; +} +if (auto *TT = Node->getAs()) { + if (matchesDecl(TT->getDecl(), Finder, Builder)) +return true; +} + +// Try non-desugarable types. +if (auto *TD = Node->getAsTagDecl()) + return matchesDecl(TD, Finder, Builder); +else if (auto *ET = Node->getAs()) + return matchesSpecialized(*ET, Finder, Builder); else if (auto *OCIT = Node->getAs()) return matchesDecl(OCIT->getDecl(), Finder, Builder); else if (auto *UUT = Node->getAs()) @@ -760,9 +773,18 @@ return matchesDecl(UUT->getDecl(), Finder, Builder); else if (auto *ICNT = Node->getAs()) return matchesDecl(ICNT->getDecl(), Finder, Builder); + return false; } + /// \brief Gets the QualType from ElaboratedType + /// and returns whether the inner matches on it. + bool matchesSpecialized(const ElaboratedType &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *
Re: [PATCH] D24361: hasDeclaration(qualType(...)) matcher should unwrap ElaboratedType and TemplateSpecializationType
lukasza added inline comments. Comment at: include/clang/ASTMatchers/ASTMatchersInternal.h:750 @@ +749,3 @@ +else if (auto *ET = Node->getAs()) + return matchesSpecialized(ET->getNamedType(), Finder, Builder); +else if (auto *TST = Node->getAs()) lukasza wrote: > There is also a question whether I should not only start supporting not only > qualType(hasDeclaration(...)) when qualType wraps an ElaboratedType node, but > also start supporting elaboratedType(hasDeclaration(...)). > > Snippets of a patch that could achieve this (but then again - I am not sure > if this is desirable and/or necessary): > > +++ include/clang/ASTMatchers/ASTMatchersInternal.h (working copy) > ... > + /// \brief Gets the QualType from ElaboratedType > + /// and returns whether the inner matches on it. > + bool matchesSpecialized(const ElaboratedType &Node, > + ASTMatchFinder *Finder, > + BoundNodesTreeBuilder *Builder) const { > +return matchesSpecialized(Node.getNamedType(), Finder, Builder); > + } > + > ... > /// \brief All types that are supported by HasDeclarationMatcher above. > -typedef TypeList - InjectedClassNameType, LabelStmt, AddrLabelExpr, > MemberExpr, > - QualType, RecordType, TagType, > TemplateSpecializationType, > - TemplateTypeParmType, TypedefType, > +typedef TypeList ElaboratedType, > + EnumType, InjectedClassNameType, LabelStmt, > AddrLabelExpr, > + MemberExpr, QualType, RecordType, TagType, > + TemplateSpecializationType, TemplateTypeParmType, > TypedefType, > UnresolvedUsingType> HasDeclarationSupportedTypes; FWIW, I've added this support in the latest patchset. Comment at: include/clang/ASTMatchers/ASTMatchersInternal.h:752 @@ -749,1 +751,3 @@ +else if (auto *TST = Node->getAs()) + return matchesSpecialized(*TST, Finder, Builder); else if (auto *TT = Node->getAs()) rsmith wrote: > lukasza wrote: > > I am a little bit confused and concerned whether the order of the if > > statements here matters. In particular - I am not sure if the ordering of > > dyn_cast below and getAs calls here matters (and whether things would be > > safer / less fragile if the dyn_cast was moved all the way to the top?). > I don't think the position of the `TemplateTypeParmType` case matters; a > non-canonical TTPT always desugars to a canonical TTPT, so none of the other > cases can match. > > The relative order of the `getAs` cases can matter, though, and neither > ordering of `TypedefType` and `TemplateSpecializationType` will actually do > the right thing in general (you can have a typedef that desugars to a TST and > vice versa -- the TST would need to represent an alias template > specialization in the latter case). If you want to get this "right" you'll > need to do single-step desugaring until you hit a type you like the look of. > (See `getAsSugar` in lib/AST/Type.cpp for an example.) Thank you for the explanation and the hint to look at getAsSugar. I've added a test where both TypeSpecializationType and TypedefType could be used. One of the asserts (the one trying to match typeAliasDecl) was initially failing as expected / as explained by your comment above (i.e. HasDeclarationMatcher::matchesSpecialized(QualType...) was first looking at TypeSpecializationType and when that succeeded it would not consider TypedefType later on). The test covers alias-desugars-to-template-specialization-type case. Unfortunately, I was not able to come up with desugaring going in the other direction. I started with something like what I have below, but there is no typeAliasDecl here (I think) and I don't know how to insert one: template class Foo {}; template using Bar = Foo; Bar variable; I am not sure if I really need to do single-step desugaring (and consult hasDeclaration's innerMatcher for each step). Wouldn't it be sufficient that desugaring to TemplateSpecializationType does not latch onto TST code path, but that we will also consider TypedefType desugaring later? Code is probably better at explaining at what I mean here - please look at the latest patchset. The new test passes, so I think my changes really made ordering insignificant as desired (at least insignificant when it comes to ordering between the only types (TemplateSpecializationType and TypedefType) that are 1) used in HasDeclarationMatcher / QualType overload + 2) covered via getAsSugar helper). Comment at: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:2119 @@ +2118,3 @@ + "template \n" + "void Function(Namespace::Template param) {\n" + " param.Method();\n" rsmith wrote: > @klimek, would y
Re: [PATCH] D24361: hasDeclaration(qualType(...)) matcher should unwrap ElaboratedType and TemplateSpecializationType
lukasza added inline comments. Comment at: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:2119 @@ +2118,3 @@ + "template \n" + "void Function(Namespace::Template param) {\n" + " param.Method();\n" klimek wrote: > Given your use case: why do we need hasDeclaration here at all? > I'd have expected this working with just matching on the nested name > specifier of the type instead of saying hasDeclaration on the template type. > Btw, if you add a type alias for a class not in the namespace into the > namespace (typedef / using), do you wan that to rename or not? :) > > I'd personally probably have expected (2), but I'm never sure in these cases > without playing around with more test cases... > Given your use case: why do we need hasDeclaration here at all? > I'd have expected this working with just matching on the nested name > specifier of the type instead of saying hasDeclaration on the template type. Because I want "namespace-of-user-provided-declaration" matching to work both for ElaboratedType nodes (with explicit nested name specifier) and for other kinds of nodes (where there might be no nested name specifier). I was hoping that I could do this with a single hasDeclaration matcher, rather than listing all possible type nodes myself (when building my own matcher) like I sort of do in a workaround. In particular, after this CL a single, simple hasDeclaration-based matcher can be used in //auto blink_qual_type_base_matcher = //qualType(hasDeclaration(in_blink_namespace)); inside https://codereview.chromium.org/2256913002/patch/180001/190001. > Btw, if you add a type alias for a class not in the namespace into the > namespace (typedef / using), do you wan that to rename or not? :) Good question. I want a rename to happen if I have ::SomeOtherNamespace::Typedef resolving to ::NamespaceWithRenamedMethods::Class, but I do not want rename to happen if I have ::NamespaceWithRenamtedMethods::Typedef resolving to ::SomeOtherNamespace::Class. I guess my current hasDeclaration-based matcher will match both cases :-( One way to fix this would be to exclude typedefs in |decl_under_blink_namespace| at https://chromium.googlesource.com/chromium/src/+/14d095b4df6754fa4e6959220b2b332db0b4f504/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp#646 But... this question+answer should have no impact on the CL under review, right? > I'd personally probably have expected (2), but I'm never sure in these cases > without playing around with more test cases... Ok. This (#2) is what the current patch results in. https://reviews.llvm.org/D24361 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D24361: hasDeclaration(qualType(...)) matcher should unwrap ElaboratedType and TemplateSpecializationType
lukasza added a comment. Richard - what are the next steps for this patch? https://reviews.llvm.org/D24361 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D24268: Traversing template paramter lists of DeclaratorDecls and/or TagDecls.
lukasza updated the summary for this revision. lukasza updated this revision to Diff 70483. lukasza added a comment. Addressing CR feedbackfrom rsmith@. 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 @@ -641,6 +641,89 @@ functionDecl(hasAnyTemplateArgument(templateArgument(); } +TEST(TemplateTypeParmDecl, CXXMethodDecl) { + const char input[] = + "template\n" + "class Class {\n" + " void method();\n" + "};\n" + "template\n" + "void Class::method() {}\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"; +} + +TEST(TemplateTypeParmDecl, VarDecl) { + const char input[] = + "template\n" + "class Class {\n" + " static T pi;\n" + "};\n" + "template\n" + "U Class::pi = U(3.1415926535897932385);\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"; +} + +TEST(TemplateTypeParmDecl, VarTemplatePartialSpecializationDecl) { + const char input[] = + "template\n" + "struct Struct {\n" + " template static int field;\n" + "};\n" + "template\n" + "template\n" + "int Struct::field = 123;\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T2"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U2"; +} + +TEST(TemplateTypeParmDecl, ClassTemplatePartialSpecializationDecl) { + const char input[] = + "template\n" + "class Class {\n" + " template struct Struct;\n" + "};\n" + "template\n" + "template\n" + "struct Class::Struct {};\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T2"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U2"; +} + +TEST(TemplateTypeParmDecl, EnumDecl) { + const char input[] = + "template\n" + "struct Struct {\n" + " enum class Enum : T;\n" + "};\n" + "template\n" + "enum class Struct::Enum : U {\n" + " e1,\n" + " e2\n" + "};\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"; +} + +TEST(TemplateTypeParmDecl, RecordDecl) { + const char input[] = + "template\n" + "class Class {\n" + " struct Struct;\n" + "};\n" + "template\n" + "struct Class::Struct {\n" + " U field;\n" + "};\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"; +} + TEST(RefersToIntegralType, Matches) { EXPECT_TRUE(matches("template struct C {}; C<42> c;", classTemplateSpecializationDecl( Index: include/clang/AST/RecursiveASTVisitor.h === --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -482,6 +482,11 @@ 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 + bool TraverseDeclTemplateParameterLists(T *D); + #define DEF_TRAVERSE_TMPL_INST(TMPLDECLKIND) \ bool TraverseTemplateInstantiations(TMPLDECLKIND##TemplateDecl *D); DEF_TRAVERSE_TMPL_INST(Class) @@ -1532,6 +1537,16 @@ } template +template +bool RecursiveASTVisitor::TraverseDeclTemplateParameterLists(T *D) { + for (unsigned i = 0; i < D->getNumTemplateParameterLists(); i++) { +TemplateParameterList *TPL = D->getTemplateParameterList(i); +TraverseTemplateParameterListHelper(TPL); + } + return true; +} + +template bool RecursiveASTVisitor::TraverseTemplateInstantiations( ClassTemplateDecl *D) { for (auto *SD : D->specializations()) { @@ -1692,6 +1707,8 @@ }) DEF_TRAVERSE_DECL(EnumDecl, { + TRY_TO(TraverseDeclTemplateParameterLists(D)); + if (D->getTypeForDecl()) TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0))); @@ -1706,6 +1723,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(TraverseNestedNameS
[PATCH] D24268: Traversing template paramter lists of DeclaratorDecls and/or TagDecls.
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, + InnerMatcher) { + const Type* type = Node.getTypePtrOrNull(); + return type && InnerMatcher.matches(*type, Finder, Builder); +} + +TEST(HasAncestor, TemplateTypeParmDeclUnderCXXMethodDecl) { + EXPECT_TRUE(matches( + "template\n" + "class Class {\n" + " void method();\n" + "};\n" + "template\n" + "void Class::method() {}\n", + qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl( + hasAncestor(decl(); +} + +TEST(HasAncestor, TemplateTypeParmDeclUnderVarDecl) { + EXPECT_TRUE(matches( + "template\n" + "class Class {\n" + " static T pi;\n" + "};\n" + "template\n" + "U Class::pi = U(3.1415926535897932385);\n", + qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl( + hasAncestor(decl(); +} + +TEST(HasAncestor, TemplateTypeParmDeclUnderEnumDecl) { + EXPECT_TRUE(matches( + "template\n" + "struct Struct {\n" + " enum class Enum : T;\n" + "};\n" + "template\n" + "enum class Struct::Enum : U {\n" + " e1,\n" + " e2\n" + "};\n", + qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl( + hasAncestor(decl(); +} + +TEST(HasAncestor, TemplateTypeParmDeclUnderStructDecl) { + EXPECT_TRUE(matches( + "template\n" + "class Class {\n" + " struct Struct;\n" + "};\n" + "template\n" + "struct Class::Struct {\n" + " U field;\n" + "};\n", + qualType(hasUnderlyingType(templateTypeParmType(hasDeclaration(decl( + hasAncestor(decl(); +} + TEST(HasAncestor, NonParmDependentTemplateParmVarDeclRefExpr) { EXPECT_TRUE(matches("struct PartitionAllocator {\n" " template\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 ::value || + std::is_base_of::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 +template +bool RecursiveASTVisitor::TraverseDeclTemplateParameterLists(T *D) { + for (unsigned i = 0; i < D->getNumTemplateParameterLists(); i++) { +TemplateParameterList *TPL = D->getTemplateParameterList(i); +TraverseTemplateParameterListHelper(TPL); + } + return true; +} + +template bool RecursiveASTVisitor::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 bool RecursiveASTVisitor::TraverseDeclarato
Re: [PATCH] D24268: Traversing template paramter lists of DeclaratorDecls and/or TagDecls.
lukasza added inline comments. Comment at: include/clang/AST/RecursiveASTVisitor.h:487-489 @@ +486,5 @@ + // Traverses template parameter lists of either a DeclaratorDecl or TagDecl. + template ::value || + std::is_base_of::value>::type> + bool TraverseDeclTemplateParameterLists(T *D); rsmith wrote: > What's the purpose of the `enable_if` here? enable_if is here to document via code that this method works for arguments that are either a TagDecl or a DeclaratorDecl. enable_if is not really necessary here - I could take it out and just make the TraverseDeclTemplateParameterLists method compile as long as T contains the right getNumTemplateParameterLists and getTemplateParameterList methods. FWIW, I got rid of std::enable_if above in the latest patch revision. Does the code look better and less surprising now? Comment at: include/clang/AST/RecursiveASTVisitor.h:1728 @@ -1708,2 +1727,3 @@ + TRY_TO(TraverseDeclTemplateParameterLists(D)); TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); rsmith wrote: > lukasza wrote: > > I think that tweaks of EnumDecl traversal together with tweaks of > > TraverseRecordHelper tweaks, take care of all the relevant node types > > derived from TagDecl: > > > > - EnumDecl - traversal now calls TraverseDeclTemplateParameterLists > > (+EnumDecl is covered by the new unit tests) > > - RecordDecl - TraverseRecordHelper now calls > > TraverseDeclTemplateParameterLists (+RecordDecl is covered by the new unit > > tests) > > - CXXRecordDecl - traversal includes a call to TraverseRecordHelper > > > > I believe that nodes derived further from CXXRecordDecl cannot have a > > template parameter list (?): > > - ClassTemplateSpecializationDecl > > - ClassTemplatePartialSpecializationDecl > > If this is not correct, then can you please help me see the shape of unit > > tests that would cover these nodes? > I don't think `ClassTemplateSpecializationDecl` can have a template parameter > list for its qualifier. A `ClassTemplatePartialSpecializationDecl` can, > though: > > template struct A { > template struct B; > }; > template template struct A::B {}; Thanks for the example. I've added a unit test covering this case (and verified that it fails before the fix and passes afterwards). A bit surprisingly, the extra test passed without further tweaks to the product code (surprisingly, because I am not sure how TraverseDeclTemplateParameterLists ends up getting called for ClassTemplatePartialSpecializationDecl). I don't think I want to keep digging to understand why things work without further tweaks, but the surprise factor makes me think the extra test is worth keeping (even though it apparently doesn't really increase test coverage). Comment at: include/clang/AST/RecursiveASTVisitor.h:1823 @@ -1802,2 +1822,3 @@ bool RecursiveASTVisitor::TraverseDeclaratorHelper(DeclaratorDecl *D) { + TRY_TO(TraverseDeclTemplateParameterLists(D)); TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); rsmith wrote: > lukasza wrote: > > I think that TraverseDeclaratorHelper together with TraverseFunctionHelper > > tweaks, this takes care of all node types derived from DeclaratorDecl: > > - FieldDecl - traversal calls TraverseDeclaratorHelper > > - FunctionDecl - traversal calls TraverseFunctionHelper > > - CXXMethodDecl - traversal calls TraverseFunctionHelper (+CXXMethodDecl is > > covered by the new unit tests) > > - CXXConstructorDecl - traversal calls TraverseFunctionHelper > > - CXXConversionDecl - traversal calls TraverseFunctionHelper > > - CXXDestructorDecl - traversal calls TraverseFunctionHelper > > - VarDecl - TraverseVarHelper calls TraverseDeclaratorHelper (+VarDecl is > > covered by the new unit tests) > > > > I believe that nodes derived further from VarDecl cannot have a template > > parameter list (?), but most of them should still be safe (because their > > traversal goes through TraverseVarHelper): > > - DecompositionDecl - traversal calls TraverseVarHelper > > - ImplicitParamDecl - traversal calls TraverseVarHelper > > - OMPCapturedExprDecl - traversal calls TraverseVarHelper > > - ParmVarDecl - traversal calls TraverseVarHelper > > - VarTemplateSpecializationDecl > > - VarTemplatePartialSpecializationDecl > > > > Please let me know if I am wrong above and it is indeed possible to have > > template parameter lists next to VarTemplateSpecializationDecl and/or > > VarTemplatePartialSpecializationDecl (and I would also appreciate if you > > could help me see the shape of unit tests that would cover these nodes). > `VarTemplatePartialSpecializationDecl`s can have template parameter lists: > > template struct A { template static int n; }; > template template int A::n; > > I don't think other `VarTemplateSpecializationDecl`s can. Thanks for the example. I've added a unit test covering this case (and verifi
Re: [PATCH] D24268: Traversing template paramter lists of DeclaratorDecls and/or TagDecls.
lukasza added inline comments. Comment at: include/clang/AST/RecursiveASTVisitor.h:1728 @@ -1708,2 +1727,3 @@ + TRY_TO(TraverseDeclTemplateParameterLists(D)); TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); I think that tweaks of EnumDecl traversal together with tweaks of TraverseRecordHelper tweaks, take care of all the relevant node types derived from TagDecl: - EnumDecl - traversal now calls TraverseDeclTemplateParameterLists (+EnumDecl is covered by the new unit tests) - RecordDecl - TraverseRecordHelper now calls TraverseDeclTemplateParameterLists (+RecordDecl is covered by the new unit tests) - CXXRecordDecl - traversal includes a call to TraverseRecordHelper I believe that nodes derived further from CXXRecordDecl cannot have a template parameter list (?): - ClassTemplateSpecializationDecl - ClassTemplatePartialSpecializationDecl If this is not correct, then can you please help me see the shape of unit tests that would cover these nodes? Comment at: include/clang/AST/RecursiveASTVisitor.h:1823 @@ -1802,2 +1822,3 @@ bool RecursiveASTVisitor::TraverseDeclaratorHelper(DeclaratorDecl *D) { + TRY_TO(TraverseDeclTemplateParameterLists(D)); TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); I think that TraverseDeclaratorHelper together with TraverseFunctionHelper tweaks, this takes care of all node types derived from DeclaratorDecl: - FieldDecl - traversal calls TraverseDeclaratorHelper - FunctionDecl - traversal calls TraverseFunctionHelper - CXXMethodDecl - traversal calls TraverseFunctionHelper (+CXXMethodDecl is covered by the new unit tests) - CXXConstructorDecl - traversal calls TraverseFunctionHelper - CXXConversionDecl - traversal calls TraverseFunctionHelper - CXXDestructorDecl - traversal calls TraverseFunctionHelper - VarDecl - TraverseVarHelper calls TraverseDeclaratorHelper (+VarDecl is covered by the new unit tests) I believe that nodes derived further from VarDecl cannot have a template parameter list (?), but most of them should still be safe (because their traversal goes through TraverseVarHelper): - DecompositionDecl - traversal calls TraverseVarHelper - ImplicitParamDecl - traversal calls TraverseVarHelper - OMPCapturedExprDecl - traversal calls TraverseVarHelper - ParmVarDecl - traversal calls TraverseVarHelper - VarTemplateSpecializationDecl - VarTemplatePartialSpecializationDecl Please let me know if I am wrong above and it is indeed possible to have template parameter lists next to VarTemplateSpecializationDecl and/or VarTemplatePartialSpecializationDecl (and I would also appreciate if you could help me see the shape of unit tests that would cover these nodes). Comment at: include/clang/AST/RecursiveASTVisitor.h:1870 @@ -1848,2 +1869,3 @@ bool RecursiveASTVisitor::TraverseFunctionHelper(FunctionDecl *D) { + TRY_TO(TraverseDeclTemplateParameterLists(D)); TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); Richard, in https://llvm.org/bugs/show_bug.cgi?id=29042#c6 you've suggested reusing TraverseDeclaratorHelper here. I agree that there are refactoring and code reusing opportunities here (e.g. TraverseNestedNameSpecifier[Loc] are getting called from both TraverseDeclaratorHelper and from TraverseFunctionHelper), but I didn't pursue them in this patch, because I was worried about little details: 1. traversal order (i.e. TraverseDeclaratorHelper traverses NestedNameSpecifier immediately before traversing TypeSourceInfo; this is different than TraverseFunctionHelper which traverses DeclarationNameInfo in-between traversing NestedNameSpecifier and TypeSourceInfo) 2. traversal in absence of TypeSourceInfo (i.e. when TypeSourceInfo is missing, then TraverseDeclaratorHelper traverses Type; this is different than TraverseFunctionHelper which in this case 1) checks shouldVisitImplicitCode condition and only then 2) traverses function parameters). Because of the above, I think that for now it is better to call TraverseDeclTemplateParameterLists directly from TraverseFunctionHelper, rather than trying to reuse the whole (or bigger part of) TraverseDeclaratorHelper from TraverseFunctionHelper. Comment at: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:1730 @@ +1729,3 @@ + return type && InnerMatcher.matches(*type, Finder, Builder); +} + I've needed this custom AST matcher in https://codereview.chromium.org/2256913002 - let me try to sneak in a question related to this other CL: Can I somehow avoid introducing a new, custom AST matcher? (i.e. is it possible to express hasUnderlyingType in terms of built-in matchers?) https://reviews.llvm.org/D24268 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D24268: Traversing template paramter lists of DeclaratorDecls and/or TagDecls.
lukasza added a comment. In https://reviews.llvm.org/D24268#535463, @rsmith wrote: > This patch looks great, thank you! Do you have an SVN account or do you need > someone to commit this for you? I don't have an SVN account. Could you please commit the patch for me? https://reviews.llvm.org/D24268 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D24361: hasDeclaration(qualType(...)) matcher should unwrap ElaboratedType and TemplateSpecializationType
lukasza updated the summary for this revision. lukasza updated this revision to Diff 75661. lukasza added a comment. Reverted changes in the patch that are not related to the issue of hasDeclaration not matching *anything* in some cases. https://reviews.llvm.org/D24361 Files: include/clang/ASTMatchers/ASTMatchers.h include/clang/ASTMatchers/ASTMatchersInternal.h unittests/ASTMatchers/ASTMatchersTraversalTest.cpp Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp === --- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -2106,5 +2106,36 @@ functionDecl(hasName("bar")); } +TEST(HasDeclaration, ElaboratedTypeAndTemplateSpecializationType) { + std::string input = + "namespace Namespace {\n" + "template\n" + "class Template {\n" + " public:\n" + " void Method() {}\n" + "};\n" + "} // namespace Namespace\n" + "template \n" + "void Function(Namespace::Template param) {\n" + " param.Method();\n" + "};\n"; + + // Matcher for ::Namespace::Template template decl. + auto param_type_decl_matcher = classTemplateDecl( + hasName("Template"), + hasParent(namespaceDecl(hasName("Namespace"))), + has(templateTypeParmDecl(hasName("T"; + + // hasDeclaration / qualType-flavour. + EXPECT_TRUE(matches(input, parmVarDecl( + hasName("param"), + hasType(qualType(hasDeclaration(decl(param_type_decl_matcher))); + + // hasDeclaration / elaboratedType-flavour. + EXPECT_TRUE(matches(input, parmVarDecl( + hasName("param"), + hasType(elaboratedType(hasDeclaration(decl(param_type_decl_matcher))); +} + } // namespace ast_matchers } // namespace clang Index: include/clang/ASTMatchers/ASTMatchersInternal.h === --- include/clang/ASTMatchers/ASTMatchersInternal.h +++ include/clang/ASTMatchers/ASTMatchersInternal.h @@ -748,6 +748,10 @@ return matchesDecl(TD, Finder, Builder); else if (auto *TT = Node->getAs()) return matchesDecl(TT->getDecl(), Finder, Builder); +else if (auto *ET = Node->getAs()) + return matchesSpecialized(ET->getNamedType(), Finder, Builder); +else if (auto *TST = Node->getAs()) + return matchesSpecialized(*TST, Finder, Builder); // Do not use getAs instead of the direct dyn_cast. // Calling getAs will return the canonical type, but that type does not // store a TemplateTypeParmDecl. We *need* the uncanonical type, if it is @@ -763,6 +767,14 @@ return false; } + /// \brief Gets the QualType from ElaboratedType + /// and returns whether the inner matches on it. + bool matchesSpecialized(const ElaboratedType &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { +return matchesSpecialized(Node.getNamedType(), Finder, Builder); + } + /// \brief Gets the TemplateDecl from a TemplateSpecializationType /// and returns whether the inner matches on it. bool matchesSpecialized(const TemplateSpecializationType &Node, @@ -1007,10 +1019,10 @@ TypeLoc, QualType> AdaptativeDefaultToTypes; /// \brief All types that are supported by HasDeclarationMatcher above. -typedef TypeList HasDeclarationSupportedTypes; /// \brief Converts a \c Matcher to a matcher of desired type \c To by Index: include/clang/ASTMatchers/ASTMatchers.h === --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -2454,7 +2454,8 @@ /// function. e.g. various subtypes of clang::Type and various expressions. /// /// Usable as: Matcher, Matcher, -/// Matcher, Matcher, Matcher, +/// Matcher, Matcher, Matcher, +/// Matcher, /// Matcher, Matcher, Matcher, /// Matcher, Matcher, Matcher, /// Matcher, Matcher, Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp === --- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -2106,5 +2106,36 @@ functionDecl(hasName("bar")); } +TEST(HasDeclaration, ElaboratedTypeAndTemplateSpecializationType) { + std::string input = + "namespace Namespace {\n" + "template\n" + "class Template {\n" + " public:\n" + " void Method() {}\n" + "};\n" + "} // namespace Namespace\n" + "template \n" + "void Function(Namespace::Template param) {\n" + " param.Method();\n" + "};\n"; + + // Matcher for ::Namespace::Template template decl. + auto param_type_decl_matcher = classTemplateDecl( + hasName("Template"), + hasParent(namespaceDecl(hasName("Namespace"))), + has(template
[PATCH] D26032: [ASTMatcher] Add CXXNewExpr support to hasDeclaration
lukasza added a comment. FWIW, a non-owner LGTM: - CXXNewExpr seems very similar to CallExpr, so it makes sense that hasDeclaration would behave similarily for both of these expressions (i.e. matching the "callee") - The issues we've been trying to work through in https://reviews.llvm.org/D24361 mainly revolve around Type and QualType, so I think those issues should not apply to CXXNewExpr matching. https://reviews.llvm.org/D26032 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits