Author: klimek Date: Wed Aug 2 06:04:44 2017 New Revision: 309809 URL: http://llvm.org/viewvc/llvm-project?rev=309809&view=rev Log: Unify and simplify the behavior of the hasDeclaration matcher.
Originally, we weren't able to match on Type nodes themselves (only QualType), so the hasDeclaration matcher was initially written to give what we thought are reasonable results for QualType matches. When we chagned the matchers to allow matching on Type nodes, it turned out that the hasDeclaration matcher was by chance written templated enough to now allow hasDeclaration to also match on (some) Type nodes. This patch change the hasDeclaration matcher to: a) work the same on Type and QualType nodes, b) be completely explicit about what nodes we can match instead of just allowing anything with a getDecl() to match, c) explicitly control desugaring only one level in very specific instances. d) adds hasSpecializedTemplate and tagType matchers to allow migrating existing use cases that now need more explicit matchers Note: This patch breaks clang-tools-extra. The corresponding patch there is approved and will land in a subsequent patch. Differential Revision: https://reviews.llvm.org/D27104 Modified: cfe/trunk/docs/LibASTMatchersReference.html cfe/trunk/docs/ReleaseNotes.rst cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h cfe/trunk/unittests/AST/ASTImporterTest.cpp cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp Modified: cfe/trunk/docs/LibASTMatchersReference.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LibASTMatchersReference.html?rev=309809&r1=309808&r2=309809&view=diff ============================================================================== --- cfe/trunk/docs/LibASTMatchersReference.html (original) +++ cfe/trunk/docs/LibASTMatchersReference.html Wed Aug 2 06:04:44 2017 @@ -1729,6 +1729,21 @@ substTemplateTypeParmType() matches the </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('tagType0')"><a name="tagType0Anchor">tagType</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagType.html">TagType</a>>...</td></tr> +<tr><td colspan="4" class="doc" id="tagType0"><pre>Matches tag types (record and enum types). + +Given + enum E {}; + class C {}; + + E e; + C c; + +tagType() matches the type of the variable declarations of both e +and c. +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('templateSpecializationType0')"><a name="templateSpecializationType0Anchor">templateSpecializationType</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateSpecializationType.html">TemplateSpecializationType</a>>...</td></tr> <tr><td colspan="4" class="doc" id="templateSpecializationType0"><pre>Matches template specialization types. @@ -4546,6 +4561,18 @@ functionDecl(hasAnyTemplateArgument(refe </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>></td><td class="name" onclick="toggle('hasSpecializedTemplate0')"><a name="hasSpecializedTemplate0Anchor">hasSpecializedTemplate</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateDecl.html">ClassTemplateDecl</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasSpecializedTemplate0"><pre>Matches the specialized template of a specialization declaration. + +Given + tempalate<typename T> class A {}; + typedef A<int> B; +classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl())) + matches 'B' with classTemplateDecl() matching the class template + declaration of 'A'. +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>></td><td class="name" onclick="toggle('hasTemplateArgument0')"><a name="hasTemplateArgument0Anchor">hasTemplateArgument</a></td><td>unsigned N, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="hasTemplateArgument0"><pre>Matches classTemplateSpecializations, templateSpecializationType and functionDecl where the n'th TemplateArgument matches the given InnerMatcher. Modified: cfe/trunk/docs/ReleaseNotes.rst URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ReleaseNotes.rst?rev=309809&r1=309808&r2=309809&view=diff ============================================================================== --- cfe/trunk/docs/ReleaseNotes.rst (original) +++ cfe/trunk/docs/ReleaseNotes.rst Wed Aug 2 06:04:44 2017 @@ -151,8 +151,33 @@ this section should help get you past th AST Matchers ------------ -... +The hasDeclaration matcher now works the same for Type and QualType and only +ever looks through one level of sugaring in a limited number of cases. +There are two main patterns affected by this: + +- qualType(hasDeclaration(recordDecl(...))): previously, we would look through + sugar like TypedefType to get at the underlying recordDecl; now, we need + to explicitly remove the sugaring: + qualType(hasUnqualifiedDesugaredType(hasDeclaration(recordDecl(...)))) + +- hasType(recordDecl(...)): hasType internally uses hasDeclaration; previously, + this matcher used to match for example TypedefTypes of the RecordType, but + after the change they don't; to fix, use: + +:: + hasType(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(recordDecl(...))))) + +- templateSpecializationType(hasDeclaration(classTemplateDecl(...))): + previously, we would directly match the underlying ClassTemplateDecl; + now, we can explicitly match the ClassTemplateSpecializationDecl, but that + requires to explicitly get the ClassTemplateDecl: + +:: + templateSpecializationType(hasDeclaration( + classTemplateSpecializationDecl( + hasSpecializedTemplate(classTemplateDecl(...))))) clang-format ------------ Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h?rev=309809&r1=309808&r2=309809&view=diff ============================================================================== --- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h (original) +++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h Wed Aug 2 06:04:44 2017 @@ -582,6 +582,23 @@ AST_MATCHER_P(FieldDecl, hasInClassIniti InnerMatcher.matches(*Initializer, Finder, Builder)); } +/// \brief Matches the specialized template of a specialization declaration. +/// +/// Given +/// \code +/// tempalate<typename T> class A {}; +/// typedef A<int> B; +/// \endcode +/// classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl())) +/// matches 'B' with classTemplateDecl() matching the class template +/// declaration of 'A'. +AST_MATCHER_P(ClassTemplateSpecializationDecl, hasSpecializedTemplate, + internal::Matcher<ClassTemplateDecl>, InnerMatcher) { + const ClassTemplateDecl* Decl = Node.getSpecializedTemplate(); + return (Decl != nullptr && + InnerMatcher.matches(*Decl, Finder, Builder)); +} + /// \brief Matches a declaration that has been implicitly added /// by the compiler (eg. implicit default/copy constructors). AST_MATCHER(Decl, isImplicit) { @@ -5083,6 +5100,21 @@ AST_TYPE_MATCHER(UnaryTransformType, una /// and \c s. AST_TYPE_MATCHER(RecordType, recordType); +/// \brief Matches tag types (record and enum types). +/// +/// Given +/// \code +/// enum E {}; +/// class C {}; +/// +/// E e; +/// C c; +/// \endcode +/// +/// \c tagType() matches the type of the variable declarations of both \c e +/// and \c c. +AST_TYPE_MATCHER(TagType, tagType); + /// \brief Matches types specified with an elaborated type keyword or with a /// qualified name. /// Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h?rev=309809&r1=309808&r2=309809&view=diff ============================================================================== --- cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h (original) +++ cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h Wed Aug 2 06:04:44 2017 @@ -728,48 +728,85 @@ public: } private: - /// \brief If getDecl exists as a member of U, returns whether the inner - /// matcher matches Node.getDecl(). - template <typename U> - bool matchesSpecialized( - const U &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder, - typename std::enable_if<has_getDecl<U>::value, int>::type = 0) const { - return matchesDecl(Node.getDecl(), Finder, Builder); - } - - /// \brief Extracts the TagDecl of a QualType and returns whether the inner - /// matcher matches on it. + /// \brief Forwards to matching on the underlying type of the QualType. bool matchesSpecialized(const QualType &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const { if (Node.isNull()) return false; - if (auto *TD = Node->getAsTagDecl()) - return matchesDecl(TD, Finder, Builder); - else if (auto *TT = Node->getAs<TypedefType>()) - return matchesDecl(TT->getDecl(), Finder, Builder); - // Do not use getAs<TemplateTypeParmType> 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<TemplateTypeParmType>(Node.getTypePtr())) - return matchesDecl(TTP->getDecl(), Finder, Builder); - else if (auto *OCIT = Node->getAs<ObjCInterfaceType>()) - return matchesDecl(OCIT->getDecl(), Finder, Builder); - else if (auto *UUT = Node->getAs<UnresolvedUsingType>()) - return matchesDecl(UUT->getDecl(), Finder, Builder); - else if (auto *ICNT = Node->getAs<InjectedClassNameType>()) - return matchesDecl(ICNT->getDecl(), Finder, Builder); + return matchesSpecialized(*Node, Finder, Builder); + } + + /// \brief Finds the best declaration for a type and returns whether the inner + /// matcher matches on it. + bool matchesSpecialized(const Type &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + // First, for any types that have a declaration, extract the declaration and + // match on it. + if (const auto *S = dyn_cast<TagType>(&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast<InjectedClassNameType>(&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast<TemplateTypeParmType>(&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast<TypedefType>(&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast<UnresolvedUsingType>(&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast<ObjCObjectType>(&Node)) { + return matchesDecl(S->getInterface(), Finder, Builder); + } + + // A SubstTemplateTypeParmType exists solely to mark a type substitution + // on the instantiated template. As users usually want to match the + // template parameter on the uninitialized template, we can always desugar + // one level without loss of expressivness. + // For example, given: + // template<typename T> struct X { T t; } class A {}; X<A> a; + // The following matcher will match, which otherwise would not: + // fieldDecl(hasType(pointerType())). + if (const auto *S = dyn_cast<SubstTemplateTypeParmType>(&Node)) { + return matchesSpecialized(S->getReplacementType(), Finder, Builder); + } + + // For template specialization types, we want to match the template + // declaration, as long as the type is still dependent, and otherwise the + // declaration of the instantiated tag type. + if (const auto *S = dyn_cast<TemplateSpecializationType>(&Node)) { + if (!S->isTypeAlias() && S->isSugared()) { + // If the template is non-dependent, we want to match the instantiated + // tag type. + // For example, given: + // template<typename T> struct X {}; X<int> a; + // The following matcher will match, which otherwise would not: + // templateSpecializationType(hasDeclaration(cxxRecordDecl())). + return matchesSpecialized(*S->desugar(), Finder, Builder); + } + // If the template is dependent or an alias, match the template + // declaration. + return matchesDecl(S->getTemplateName().getAsTemplateDecl(), Finder, + Builder); + } + + // FIXME: We desugar elaborated types. This makes the assumption that users + // do never want to match on whether a type is elaborated - there are + // arguments for both sides; for now, continue desugaring. + if (const auto *S = dyn_cast<ElaboratedType>(&Node)) { + return matchesSpecialized(S->desugar(), Finder, Builder); + } return false; } - /// \brief Gets the TemplateDecl from a TemplateSpecializationType - /// and returns whether the inner matches on it. - bool matchesSpecialized(const TemplateSpecializationType &Node, - ASTMatchFinder *Finder, + /// \brief Extracts the Decl the DeclRefExpr references and returns whether + /// the inner matcher matches on it. + bool matchesSpecialized(const DeclRefExpr &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const { - return matchesDecl(Node.getTemplateName().getAsTemplateDecl(), - Finder, Builder); + return matchesDecl(Node.getDecl(), Finder, Builder); } /// \brief Extracts the Decl of the callee of a CallExpr and returns whether @@ -811,6 +848,13 @@ private: return matchesDecl(Node.getLabel(), Finder, Builder); } + /// \brief Extracts the declaration of a LabelStmt and returns whether the + /// inner matcher matches on it. + bool matchesSpecialized(const LabelStmt &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return matchesDecl(Node.getDecl(), Finder, Builder); + } + /// \brief Returns whether the inner matcher \c Node. Returns false if \c Node /// is \c NULL. bool matchesDecl(const Decl *Node, ASTMatchFinder *Finder, @@ -1016,9 +1060,10 @@ typedef TypeList<Decl, Stmt, NestedNameS /// \brief All types that are supported by HasDeclarationMatcher above. typedef TypeList<CallExpr, CXXConstructExpr, CXXNewExpr, DeclRefExpr, EnumType, - InjectedClassNameType, LabelStmt, AddrLabelExpr, MemberExpr, - QualType, RecordType, TagType, TemplateSpecializationType, - TemplateTypeParmType, TypedefType, UnresolvedUsingType> + ElaboratedType, InjectedClassNameType, LabelStmt, + AddrLabelExpr, MemberExpr, QualType, RecordType, TagType, + TemplateSpecializationType, TemplateTypeParmType, TypedefType, + UnresolvedUsingType> HasDeclarationSupportedTypes; /// \brief Converts a \c Matcher<T> to a matcher of desired type \c To by Modified: cfe/trunk/unittests/AST/ASTImporterTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/ASTImporterTest.cpp?rev=309809&r1=309808&r2=309809&view=diff ============================================================================== --- cfe/trunk/unittests/AST/ASTImporterTest.cpp (original) +++ cfe/trunk/unittests/AST/ASTImporterTest.cpp Wed Aug 2 06:04:44 2017 @@ -252,35 +252,19 @@ AST_MATCHER_P(TemplateDecl, hasTemplateD TEST(ImportExpr, ImportParenListExpr) { MatchVerifier<Decl> Verifier; - EXPECT_TRUE( - testImport( - "template<typename T> class dummy { void f() { dummy X(*this); } };" - "typedef dummy<int> declToImport;" - "template class dummy<int>;", - Lang_CXX, "", Lang_CXX, Verifier, - typedefDecl( - hasType( - templateSpecializationType( - hasDeclaration( - classTemplateDecl( - hasTemplateDecl( - cxxRecordDecl( - hasMethod( - allOf( - hasName("f"), - hasBody( - compoundStmt( - has( - declStmt( - hasSingleDecl( - varDecl( - hasInitializer( - parenListExpr( - has( - unaryOperator( - hasOperatorName("*"), - hasUnaryOperand(cxxThisExpr()) - ))))))))))))))))))))); + EXPECT_TRUE(testImport( + "template<typename T> class dummy { void f() { dummy X(*this); } };" + "typedef dummy<int> declToImport;" + "template class dummy<int>;", + Lang_CXX, "", Lang_CXX, Verifier, + typedefDecl(hasType(templateSpecializationType( + hasDeclaration(classTemplateSpecializationDecl(hasSpecializedTemplate( + classTemplateDecl(hasTemplateDecl(cxxRecordDecl(hasMethod(allOf( + hasName("f"), + hasBody(compoundStmt(has(declStmt(hasSingleDecl( + varDecl(hasInitializer(parenListExpr(has(unaryOperator( + hasOperatorName("*"), + hasUnaryOperand(cxxThisExpr())))))))))))))))))))))))); } TEST(ImportExpr, ImportStmtExpr) { Modified: cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp?rev=309809&r1=309808&r2=309809&view=diff ============================================================================== --- cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp (original) +++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp Wed Aug 2 06:04:44 2017 @@ -230,6 +230,17 @@ TEST(HasDeclaration, HasGetDeclTraitTest "Expected TemplateSpecializationType to *not* have a getDecl."); } +TEST(HasDeclaration, ElaboratedType) { + EXPECT_TRUE(matches( + "namespace n { template <typename T> struct X {}; }" + "void f(n::X<int>);", + parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl())))))); + EXPECT_TRUE(matches( + "namespace n { template <typename T> struct X {}; }" + "void f(n::X<int>);", + parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl())))))); +} + TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) { EXPECT_TRUE(matches("typedef int X; X a;", varDecl(hasName("a"), @@ -242,6 +253,13 @@ TEST(HasDeclaration, HasDeclarationOfTem EXPECT_TRUE(matches("template <typename T> class A {}; A<int> a;", varDecl(hasType(templateSpecializationType( hasDeclaration(namedDecl(hasName("A")))))))); + EXPECT_TRUE(matches("template <typename T> class A {};" + "template <typename T> class B { A<T> a; };", + fieldDecl(hasType(templateSpecializationType( + hasDeclaration(namedDecl(hasName("A")))))))); + EXPECT_TRUE(matches("template <typename T> class A {}; A<int> a;", + varDecl(hasType(templateSpecializationType( + hasDeclaration(cxxRecordDecl())))))); } TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) { @@ -250,6 +268,12 @@ TEST(HasDeclaration, HasDeclarationOfCXX cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1)))))); } +TEST(HasDeclaration, HasDeclarationOfTypeAlias) { + EXPECT_TRUE(matches("template <typename T> using C = T; C<int> c;", + varDecl(hasType(templateSpecializationType( + hasDeclaration(typeAliasTemplateDecl())))))); +} + TEST(HasUnqualifiedDesugaredType, DesugarsUsing) { EXPECT_TRUE( matches("struct A {}; using B = A; B b;", @@ -2211,8 +2235,7 @@ TEST(Matcher, HasAnyDeclaration) { functionDecl(hasName("bar")))))); } -TEST(SubstTemplateTypeParmType, HasReplacementType) -{ +TEST(SubstTemplateTypeParmType, HasReplacementType) { std::string Fragment = "template<typename T>" "double F(T t);" "int i;" @@ -2228,5 +2251,13 @@ TEST(SubstTemplateTypeParmType, HasRepla substTemplateTypeParmType(hasReplacementType(qualType())))); } +TEST(ClassTemplateSpecializationDecl, HasSpecializedTemplate) { + auto Matcher = classTemplateSpecializationDecl( + hasSpecializedTemplate(classTemplateDecl())); + EXPECT_TRUE( + matches("template<typename T> class A {}; typedef A<int> B;", Matcher)); + EXPECT_TRUE(notMatches("template<typename T> class A {};", Matcher)); +} + } // namespace ast_matchers } // namespace clang _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits