diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-using.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-using.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-using.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-using.cpp @@ -249,6 +249,17 @@ // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' // CHECK-FIXES: using Nested_t = TwoArgTemplate>, S<(0 < 0), Q>>; +template +class TemplateKeyword { + typedef typename a::template b<> d; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef' + // CHECK-FIXES: using d = typename a::template b<>; + + typedef typename a::template b<>::c d2; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef' + // CHECK-FIXES: using d2 = typename a::template b<>::c; +}; + template class Variadic {}; diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -308,6 +308,14 @@ // nested-name-specifier. SpecType->getTemplateName().print(OS, InnerPolicy, true); + // Print the template argument list. + printTemplateArgumentList(OS, SpecType->template_arguments(), + InnerPolicy); + } else if (const auto *SpecType = + dyn_cast(T)) { + // Print the template name without its corresponding + // nested-name-specifier. + OS << SpecType->getIdentifier()->getName(); // Print the template argument list. printTemplateArgumentList(OS, SpecType->template_arguments(), InnerPolicy); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1388,6 +1388,7 @@ if (T->getQualifier()) T->getQualifier()->print(OS, Policy); + OS << "template "; OS << T->getIdentifier()->getName(); printTemplateArgumentList(OS, T->template_arguments(), Policy); spaceBeforePlaceHolder(OS); diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp @@ -109,9 +109,9 @@ struct G { template static T temp; }; template requires requires { typename T::template temp; } -// expected-note@-1{{because 'typename T::temp' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} -// expected-note@-2{{because 'typename T::temp' would be invalid: no member named 'temp' in 'D'}} -// expected-note@-3{{because 'typename T::temp' would be invalid: template name refers to non-type template 'G::template temp'}} +// expected-note@-1{{because 'typename T::template temp' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +// expected-note@-2{{because 'typename T::template temp' would be invalid: no member named 'temp' in 'D'}} +// expected-note@-3{{because 'typename T::template temp' would be invalid: template name refers to non-type template 'G::template temp'}} struct r7 {}; using r7i1 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = int]}}