diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index 6c40eb1168ce..7810c306dd9d 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -34,8 +34,7 @@ struct PrintingPolicy; class TypeSourceInfo; class ValueDecl; -/// \brief Represents a template argument within a class template -/// specialization. +/// \brief Represents a template argument. class TemplateArgument { public: /// \brief The kind of template argument we're storing. @@ -52,16 +51,19 @@ public: /// was provided for a non-type template parameter. NullPtr, /// The template argument is an integral value stored in an llvm::APSInt - /// that was provided for an integral non-type template parameter. + /// that was provided for an integral non-type template parameter. Integral, - /// The template argument is a template name that was provided for a + /// The template argument is a template name that was provided for a /// template template parameter. Template, - /// The template argument is a pack expansion of a template name that was + /// The template argument is a pack expansion of a template name that was /// provided for a template template parameter. TemplateExpansion, - /// The template argument is a value- or type-dependent expression or a - /// non-dependent __uuidof expression stored in an Expr*. + /// The template argument is an expression, and we've not resolved it to one + /// of the other forms yet, either because it's dependent or because we're + /// representing a non-canonical template argument (for instance, in a + /// TemplateSpecializationType). Also used to represent a non-dependent + /// __uuidof expression (a Microsoft extension). Expression, /// The template argument is actually a parameter pack. Arguments are stored /// in the Args struct. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 5355a9a85786..e010133ca238 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -311,6 +311,18 @@ AST_MATCHER(Decl, isPrivate) { return Node.getAccess() == AS_private; } +// FIXME: unify ClassTemplateSpecializationDecl and TemplateSpecializationType's +// APIs for accessing the template argument list. +inline llvm::ArrayRef +getTemplateSpecializationArgs(const ClassTemplateSpecializationDecl &D) { + return D.getTemplateArgs().asArray(); +} + +inline llvm::ArrayRef +getTemplateSpecializationArgs(const TemplateSpecializationType &T) { + return llvm::ArrayRef(T.getArgs(), T.getNumArgs()); +} + /// \brief Matches classTemplateSpecializations that have at least one /// TemplateArgument matching the given InnerMatcher. /// @@ -323,9 +335,12 @@ AST_MATCHER(Decl, isPrivate) { /// classTemplateSpecializationDecl(hasAnyTemplateArgument( /// refersToType(asString("int")))) /// matches the specialization \c A -AST_MATCHER_P(ClassTemplateSpecializationDecl, hasAnyTemplateArgument, - internal::Matcher, InnerMatcher) { - llvm::ArrayRef List = Node.getTemplateArgs().asArray(); +AST_POLYMORPHIC_MATCHER_P( + hasAnyTemplateArgument, + AST_POLYMORPHIC_SUPPORTED_TYPES_2(ClassTemplateSpecializationDecl, + TemplateSpecializationType), + internal::Matcher, InnerMatcher) { + llvm::ArrayRef List = getTemplateSpecializationArgs(Node); return matchesFirstInRange(InnerMatcher, List.begin(), List.end(), Finder, Builder); } @@ -419,12 +434,15 @@ AST_MATCHER_P(Expr, ignoringParenImpCasts, /// classTemplateSpecializationDecl(hasTemplateArgument( /// 1, refersToType(asString("int")))) /// matches the specialization \c A -AST_MATCHER_P2(ClassTemplateSpecializationDecl, hasTemplateArgument, - unsigned, N, internal::Matcher, InnerMatcher) { - const TemplateArgumentList &List = Node.getTemplateArgs(); +AST_POLYMORPHIC_MATCHER_P2( + hasTemplateArgument, + AST_POLYMORPHIC_SUPPORTED_TYPES_2(ClassTemplateSpecializationDecl, + TemplateSpecializationType), + unsigned, N, internal::Matcher, InnerMatcher) { + llvm::ArrayRef List = getTemplateSpecializationArgs(Node); if (List.size() <= N) return false; - return InnerMatcher.matches(List.get(N), Finder, Builder); + return InnerMatcher.matches(List[N], Finder, Builder); } /// \brief Matches a TemplateArgument that refers to a certain type. @@ -445,7 +463,8 @@ AST_MATCHER_P(TemplateArgument, refersToType, return InnerMatcher.matches(Node.getAsType(), Finder, Builder); } -/// \brief Matches a TemplateArgument that refers to a certain declaration. +/// \brief Matches a canonical TemplateArgument that refers to a certain +/// declaration. /// /// Given /// \code @@ -464,6 +483,24 @@ AST_MATCHER_P(TemplateArgument, refersToDeclaration, return false; } +/// \brief Matches a sugar TemplateArgument that refers to a certain expression. +/// +/// Given +/// \code +/// template struct A {}; +/// struct B { B* next; }; +/// A<&B::next> a; +/// \endcode +/// templateSpecializationType(hasAnyTemplateArgument( +/// isExpr(hasDescendant(declRefExpr(to(fieldDecl(hasName("next")))))))) +/// matches the specialization \c A<&B::next> with \c fieldDecl(...) matching +/// \c B::next +AST_MATCHER_P(TemplateArgument, isExpr, internal::Matcher, InnerMatcher) { + if (Node.getKind() == TemplateArgument::Expression) + return InnerMatcher.matches(*Node.getAsExpr(), Finder, Builder); + return false; +} + /// \brief Matches C++ constructor declarations. /// /// Example matches Foo::Foo() and Foo::Foo(int) diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 11230ad049df..d4b68bf0d058 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -228,6 +228,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(isConstQualified); REGISTER_MATCHER(isDefinition); REGISTER_MATCHER(isExplicitTemplateSpecialization); + REGISTER_MATCHER(isExpr); REGISTER_MATCHER(isExternC); REGISTER_MATCHER(isImplicit); REGISTER_MATCHER(isInteger); diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp index d84e6a6ef9d1..cf77f2f4dd57 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -1530,6 +1530,19 @@ TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) { "A a;", classTemplateSpecializationDecl(hasAnyTemplateArgument( refersToDeclaration(decl()))))); + + EXPECT_TRUE(matches( + "struct B { int next; };" + "template struct A {};" + "A<&B::next> a;", + templateSpecializationType(hasAnyTemplateArgument(isExpr( + hasDescendant(declRefExpr(to(fieldDecl(hasName("next")))))))))); + + EXPECT_TRUE(notMatches( + "template struct A {};" + "A a;", + templateSpecializationType(hasAnyTemplateArgument( + refersToDeclaration(decl()))))); } TEST(Matcher, MatchesSpecificArgument) { @@ -1543,6 +1556,17 @@ TEST(Matcher, MatchesSpecificArgument) { "A a;", classTemplateSpecializationDecl(hasTemplateArgument( 1, refersToType(asString("int")))))); + + EXPECT_TRUE(matches( + "template class A {};" + "A a;", + templateSpecializationType(hasTemplateArgument( + 1, refersToType(asString("int")))))); + EXPECT_TRUE(notMatches( + "template class A {};" + "A a;", + templateSpecializationType(hasTemplateArgument( + 1, refersToType(asString("int")))))); } TEST(Matcher, MatchesAccessSpecDecls) {