Index: include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- include/clang/ASTMatchers/ASTMatchersInternal.h +++ include/clang/ASTMatchers/ASTMatchersInternal.h @@ -746,6 +746,10 @@ if (auto *TD = Node->getAsTagDecl()) return matchesDecl(TD, 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); else if (auto *TT = Node->getAs()) return matchesDecl(TT->getDecl(), Finder, Builder); // Do not use getAs instead of the direct dyn_cast. Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -2106,5 +2106,29 @@ functionDecl(hasName("bar")))))); } +TEST(HasDeclaration, QualTypeSpecializedTypeAndTemplateSpecializationType) { + 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"; + + // hasDeclaration should see through: + // 1. from ElaboratedType (Namespace::MyTemplate) to + // TemplateSpecializationType (MyTemplate) + // 2. from TemplateSpecializationType (MyTemplate) + // to TemplateDecl (MyTemplate). + EXPECT_TRUE(matches(input, parmVarDecl( + hasName("param"), + hasType(qualType(hasDeclaration(decl(anything()))))))); +} + } // namespace ast_matchers } // namespace clang