Index: cfe/trunk/docs/LibASTMatchersReference.html =================================================================== --- cfe/trunk/docs/LibASTMatchersReference.html +++ cfe/trunk/docs/LibASTMatchersReference.html @@ -1729,6 +1729,21 @@ +
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. +
Matches template specialization types. @@ -4546,6 +4561,18 @@
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'. +
Matches classTemplateSpecializations, templateSpecializationType and functionDecl where the n'th TemplateArgument matches the given InnerMatcher. Index: cfe/trunk/docs/ReleaseNotes.rst =================================================================== --- cfe/trunk/docs/ReleaseNotes.rst +++ cfe/trunk/docs/ReleaseNotes.rst @@ -151,8 +151,33 @@ 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 ------------ Index: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h +++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h @@ -582,6 +582,23 @@ InnerMatcher.matches(*Initializer, Finder, Builder)); } +/// \brief Matches the specialized template of a specialization declaration. +/// +/// Given +/// \code +/// tempalateclass A {}; +/// typedef A B; +/// \endcode +/// classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl())) +/// matches 'B' with classTemplateDecl() matching the class template +/// declaration of 'A'. +AST_MATCHER_P(ClassTemplateSpecializationDecl, hasSpecializedTemplate, + internal::Matcher , 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 @@ /// 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. /// Index: cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h +++ cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -728,48 +728,85 @@ } private: - /// \brief If getDecl exists as a member of U, returns whether the inner - /// matcher matches Node.getDecl(). - template - bool matchesSpecialized( - const U &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder, - typename std::enable_if ::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 ()) - 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())) - return matchesDecl(TTP->getDecl(), 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 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 (&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast (&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast (&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast (&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast (&Node)) { + return matchesDecl(S->getDecl(), Finder, Builder); + } + if (const auto *S = dyn_cast (&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 struct X { T t; } class A {}; X a; + // The following matcher will match, which otherwise would not: + // fieldDecl(hasType(pointerType())). + if (const auto *S = dyn_cast (&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 (&Node)) { + if (!S->isTypeAlias() && S->isSugared()) { + // If the template is non-dependent, we want to match the instantiated + // tag type. + // For example, given: + // template struct X {}; X 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 (&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 @@ 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 @@ /// \brief All types that are supported by HasDeclarationMatcher above. typedef TypeList + ElaboratedType, InjectedClassNameType, LabelStmt, + AddrLabelExpr, MemberExpr, QualType, RecordType, TagType, + TemplateSpecializationType, TemplateTypeParmType, TypedefType, + UnresolvedUsingType> HasDeclarationSupportedTypes; /// \brief Converts a \c Matcher to a matcher of desired type \c To by Index: cfe/trunk/unittests/AST/ASTImporterTest.cpp =================================================================== --- cfe/trunk/unittests/AST/ASTImporterTest.cpp +++ cfe/trunk/unittests/AST/ASTImporterTest.cpp @@ -252,35 +252,19 @@ TEST(ImportExpr, ImportParenListExpr) { MatchVerifier Verifier; - EXPECT_TRUE( - testImport( - "template class dummy { void f() { dummy X(*this); } };" - "typedef dummy declToImport;" - "template class dummy ;", - 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 class dummy { void f() { dummy X(*this); } };" + "typedef dummy declToImport;" + "template class dummy ;", + 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) { Index: cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -230,6 +230,17 @@ "Expected TemplateSpecializationType to *not* have a getDecl."); } +TEST(HasDeclaration, ElaboratedType) { + EXPECT_TRUE(matches( + "namespace n { template struct X {}; }" + "void f(n::X );", + parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl())))))); + EXPECT_TRUE(matches( + "namespace n { template struct X {}; }" + "void f(n::X );", + parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl())))))); +} + TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) { EXPECT_TRUE(matches("typedef int X; X a;", varDecl(hasName("a"), @@ -242,6 +253,13 @@ EXPECT_TRUE(matches("template class A {}; A a;", varDecl(hasType(templateSpecializationType( hasDeclaration(namedDecl(hasName("A")))))))); + EXPECT_TRUE(matches("template class A {};" + "template class B { A a; };", + fieldDecl(hasType(templateSpecializationType( + hasDeclaration(namedDecl(hasName("A")))))))); + EXPECT_TRUE(matches("template class A {}; A a;", + varDecl(hasType(templateSpecializationType( + hasDeclaration(cxxRecordDecl())))))); } TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) { @@ -250,6 +268,12 @@ cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1)))))); } +TEST(HasDeclaration, HasDeclarationOfTypeAlias) { + EXPECT_TRUE(matches("template using C = T; C c;", + varDecl(hasType(templateSpecializationType( + hasDeclaration(typeAliasTemplateDecl())))))); +} + TEST(HasUnqualifiedDesugaredType, DesugarsUsing) { EXPECT_TRUE( matches("struct A {}; using B = A; B b;", @@ -2211,8 +2235,7 @@ functionDecl(hasName("bar")))))); } -TEST(SubstTemplateTypeParmType, HasReplacementType) -{ +TEST(SubstTemplateTypeParmType, HasReplacementType) { std::string Fragment = "template " "double F(T t);" "int i;" @@ -2228,5 +2251,13 @@ substTemplateTypeParmType(hasReplacementType(qualType())))); } +TEST(ClassTemplateSpecializationDecl, HasSpecializedTemplate) { + auto Matcher = classTemplateSpecializationDecl( + hasSpecializedTemplate(classTemplateDecl())); + EXPECT_TRUE( + matches("template class A {}; typedef A B;", Matcher)); + EXPECT_TRUE(notMatches("template class A {};", Matcher)); +} + } // namespace ast_matchers } // namespace clang