[clang] [Clang][Sema] Diagnose use of template keyword after declarative nested-name-specifiers (PR #78595)

Krystian Stasiowski via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 1 08:56:38 PST 2024


https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/78595

>From 3c211c6fb78f48dc68634e042e94dbe2f33fb999 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 17 Jan 2024 10:13:29 -0500
Subject: [PATCH] [Clang][Sema] Diagnose use of template keyword after
 declarative nested-name-specifiers

---
 clang/docs/ReleaseNotes.rst                   |   1 +
 clang/include/clang/AST/TypeLoc.h             |   5 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Sema/Sema.h               |   3 +-
 clang/lib/AST/TypeLoc.cpp                     |   9 +
 clang/lib/Parse/ParseDecl.cpp                 |   4 +-
 clang/lib/Sema/SemaDecl.cpp                   |  49 ++++-
 clang/lib/Sema/SemaDeclCXX.cpp                |  14 +-
 clang/lib/Sema/SemaTemplate.cpp               |  17 +-
 clang/lib/Sema/TreeTransform.h                |   3 +-
 clang/test/CXX/drs/dr23xx.cpp                 |   4 +-
 clang/test/CXX/drs/dr7xx.cpp                  |   6 +-
 .../temp.class/temp.mem.func/p1.cpp           |   2 +-
 clang/test/CXX/temp/temp.names/p5.cpp         | 206 ++++++++++++++++++
 clang/test/CXX/temp/temp.spec/part.spec.cpp   |   2 +-
 clang/test/SemaCXX/static-assert.cpp          |   2 +-
 .../test/SemaTemplate/class-template-spec.cpp |   8 +-
 17 files changed, 306 insertions(+), 32 deletions(-)
 create mode 100644 clang/test/CXX/temp/temp.names/p5.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 53040aa0f9074..b9344f83d6e1f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -149,6 +149,7 @@ Improvements to Clang's diagnostics
   prints.
 
 - Clang now diagnoses member template declarations with multiple declarators.
+- Clang now diagnoses use of the ``template`` keyword after declarative nested name specifiers.
 
 Improvements to Clang's time-trace
 ----------------------------------
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index b1b38f882d19f..32028e877fc8d 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -189,6 +189,9 @@ class TypeLoc {
   /// pointer types, but not through decltype or typedefs.
   AutoTypeLoc getContainedAutoTypeLoc() const;
 
+  /// Get the SourceLocation of the template keyword (if any).
+  SourceLocation getTemplateKeywordLoc() const;
+
   /// Initializes this to state that every location in this
   /// type is the given location.
   ///
@@ -1691,7 +1694,7 @@ class TemplateSpecializationTypeLoc :
   }
 
   void initializeLocal(ASTContext &Context, SourceLocation Loc) {
-    setTemplateKeywordLoc(Loc);
+    setTemplateKeywordLoc(SourceLocation());
     setTemplateNameLoc(Loc);
     setLAngleLoc(Loc);
     setRAngleLoc(Loc);
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7638a7e84c3c0..bee413e6da614 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8241,6 +8241,9 @@ def err_invalid_declarator_in_block : Error<
   "definition or redeclaration of %0 not allowed inside a block">;
 def err_not_tag_in_scope : Error<
   "no %select{struct|interface|union|class|enum}0 named %1 in %2">;
+def ext_template_after_declarative_nns : ExtWarn<
+    "'template' cannot be used after a declarative nested name specifier">,
+    InGroup<DiagGroup<"template-in-declaration-name">>;
 
 def err_no_typeid_with_fno_rtti : Error<
   "use of typeid requires -frtti">;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 780a2f2d8ce27..a74511a8cf94b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2960,7 +2960,8 @@ class Sema final {
   bool DiagnoseClassNameShadow(DeclContext *DC, DeclarationNameInfo Info);
   bool diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
                                     DeclarationName Name, SourceLocation Loc,
-                                    bool IsTemplateId);
+                                    TemplateIdAnnotation *TemplateId,
+                                    bool IsMemberSpecialization);
   void
   diagnoseIgnoredQualifiers(unsigned DiagID, unsigned Quals,
                             SourceLocation FallbackLoc,
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index 66732bba18e2d..b0acb18230875 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -738,3 +738,12 @@ AutoTypeLoc TypeLoc::getContainedAutoTypeLoc() const {
     return AutoTypeLoc();
   return Res.getAs<AutoTypeLoc>();
 }
+
+SourceLocation TypeLoc::getTemplateKeywordLoc() const {
+  if (const auto TSTL = getAsAdjusted<TemplateSpecializationTypeLoc>())
+    return TSTL.getTemplateKeywordLoc();
+  if (const auto DTSTL =
+          getAsAdjusted<DependentTemplateSpecializationTypeLoc>())
+    return DTSTL.getTemplateKeywordLoc();
+  return SourceLocation();
+}
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index a186253954f68..94696d8e482e5 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -6663,12 +6663,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
       }
 
       bool HadScope = D.getCXXScopeSpec().isValid();
+      SourceLocation TemplateKWLoc;
       if (ParseUnqualifiedId(D.getCXXScopeSpec(),
                              /*ObjectType=*/nullptr,
                              /*ObjectHadErrors=*/false,
                              /*EnteringContext=*/true,
                              /*AllowDestructorName=*/true, AllowConstructorName,
-                             AllowDeductionGuide, nullptr, D.getName()) ||
+                             AllowDeductionGuide, &TemplateKWLoc,
+                             D.getName()) ||
           // Once we're past the identifier, if the scope was bad, mark the
           // whole declarator bad.
           D.getCXXScopeSpec().isInvalid()) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fd1c47008d685..f29e9a9ac29cd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6196,13 +6196,17 @@ bool Sema::DiagnoseClassNameShadow(DeclContext *DC,
 ///
 /// \param Loc The location of the name of the entity being declared.
 ///
-/// \param IsTemplateId Whether the name is a (simple-)template-id, and thus
-/// we're declaring an explicit / partial specialization / instantiation.
+/// \param IsMemberSpecialization Whether we are declaring a member
+/// specialization.
+///
+/// \param TemplateId The template-id, if any.
 ///
 /// \returns true if we cannot safely recover from this error, false otherwise.
 bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
                                         DeclarationName Name,
-                                        SourceLocation Loc, bool IsTemplateId) {
+                                        SourceLocation Loc,
+                                        TemplateIdAnnotation *TemplateId,
+                                        bool IsMemberSpecialization) {
   DeclContext *Cur = CurContext;
   while (isa<LinkageSpecDecl>(Cur) || isa<CapturedDecl>(Cur))
     Cur = Cur->getParent();
@@ -6231,7 +6235,7 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
   // Check whether the qualifying scope encloses the scope of the original
   // declaration. For a template-id, we perform the checks in
   // CheckTemplateSpecializationScope.
-  if (!Cur->Encloses(DC) && !IsTemplateId) {
+  if (!Cur->Encloses(DC) && !(TemplateId || IsMemberSpecialization)) {
     if (Cur->isRecord())
       Diag(Loc, diag::err_member_qualification)
         << Name << SS.getRange();
@@ -6277,12 +6281,32 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
     return false;
   }
 
+  // C++23 [temp.names]p5:
+  //   The keyword template shall not appear immediately after a declarative
+  //   nested-name-specifier.
+  //
+  // First check the template-id (if any), and then check each component of the
+  // nested-name-specifier in reverse order.
+  //
+  // FIXME: nested-name-specifiers in friend declarations are declarative,
+  // but we don't call diagnoseQualifiedDeclaration for them. We should.
+  if (TemplateId && TemplateId->TemplateKWLoc.isValid())
+    Diag(Loc, diag::ext_template_after_declarative_nns)
+        << FixItHint::CreateRemoval(TemplateId->TemplateKWLoc);
+
+  NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
+  while (SpecLoc.getPrefix()) {
+    if (SpecLoc.getNestedNameSpecifier()->getKind() ==
+        NestedNameSpecifier::TypeSpecWithTemplate)
+      Diag(Loc, diag::ext_template_after_declarative_nns)
+          << FixItHint::CreateRemoval(
+                 SpecLoc.getTypeLoc().getTemplateKeywordLoc());
+
+    SpecLoc = SpecLoc.getPrefix();
+  }
   // C++11 [dcl.meaning]p1:
   //   [...] "The nested-name-specifier of the qualified declarator-id shall
   //   not begin with a decltype-specifer"
-  NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
-  while (SpecLoc.getPrefix())
-    SpecLoc = SpecLoc.getPrefix();
   if (isa_and_nonnull<DecltypeType>(
           SpecLoc.getNestedNameSpecifier()->getAsType()))
     Diag(Loc, diag::err_decltype_in_declarator)
@@ -6350,9 +6374,13 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
       return nullptr;
     }
     if (!D.getDeclSpec().isFriendSpecified()) {
-      if (diagnoseQualifiedDeclaration(
-              D.getCXXScopeSpec(), DC, Name, D.getIdentifierLoc(),
-              D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId)) {
+      TemplateIdAnnotation *TemplateId =
+          D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
+              ? D.getName().TemplateId
+              : nullptr;
+      if (diagnoseQualifiedDeclaration(D.getCXXScopeSpec(), DC, Name,
+                                       D.getIdentifierLoc(), TemplateId,
+                                       /*IsMemberSpecialization=*/false)) {
         if (DC->isRecord())
           return nullptr;
 
@@ -17956,6 +17984,7 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
       // nested-name-specifier against the current context.
       if ((TUK == TUK_Definition || TUK == TUK_Declaration) &&
           diagnoseQualifiedDeclaration(SS, DC, OrigName, Loc,
+                                       /*TemplateId=*/nullptr,
                                        isMemberSpecialization))
         Invalid = true;
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5adc262cd6bc9..ab8a967b06a45 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -3621,14 +3621,18 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
       // class X {
       //   int X::member;
       // };
-      if (DeclContext *DC = computeDeclContext(SS, false))
+      if (DeclContext *DC = computeDeclContext(SS, false)) {
+        TemplateIdAnnotation *TemplateId =
+            D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
+                ? D.getName().TemplateId
+                : nullptr;
         diagnoseQualifiedDeclaration(SS, DC, Name, D.getIdentifierLoc(),
-                                     D.getName().getKind() ==
-                                         UnqualifiedIdKind::IK_TemplateId);
-      else
+                                     TemplateId,
+                                     /*IsMemberSpecialization=*/false);
+      } else {
         Diag(D.getIdentifierLoc(), diag::err_member_qualification)
           << Name << SS.getRange();
-
+      }
       SS.clear();
     }
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 5616682e909aa..fc59376965150 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1890,8 +1890,12 @@ DeclResult Sema::CheckClassTemplate(
       ContextRAII SavedContext(*this, SemanticContext);
       if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
         Invalid = true;
-    } else if (TUK != TUK_Friend && TUK != TUK_Reference)
-      diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc, false);
+    }
+
+    if (TUK != TUK_Friend && TUK != TUK_Reference)
+      diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc,
+                                   /*TemplateId-*/ nullptr,
+                                   /*IsMemberSpecialization*/ false);
 
     LookupQualifiedName(Previous, SemanticContext);
   } else {
@@ -8831,6 +8835,15 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
   bool isMemberSpecialization = false;
   bool isPartialSpecialization = false;
 
+  if (SS.isSet()) {
+    if (TUK != TUK_Reference && TUK != TUK_Friend &&
+        diagnoseQualifiedDeclaration(SS, ClassTemplate->getDeclContext(),
+                                     ClassTemplate->getDeclName(),
+                                     TemplateNameLoc, &TemplateId,
+                                     /*IsMemberSpecialization=*/false))
+      return true;
+  }
+
   // Check the validity of the template headers that introduce this
   // template.
   // FIXME: We probably shouldn't complain about these headers for
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4dc36a8a5f4f0..3ed17c3360a83 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4394,7 +4394,8 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
           SS.Adopt(ETL.getQualifierLoc());
           TL = ETL.getNamedTypeLoc();
         }
-        SS.Extend(SemaRef.Context, /*FIXME:*/ SourceLocation(), TL,
+
+        SS.Extend(SemaRef.Context, TL.getTemplateKeywordLoc(), TL,
                   Q.getLocalEndLoc());
         break;
       }
diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp
index 265b51aff8ae6..3f8c476427eba 100644
--- a/clang/test/CXX/drs/dr23xx.cpp
+++ b/clang/test/CXX/drs/dr23xx.cpp
@@ -182,8 +182,8 @@ struct Bad2 { int a, b; };
 } // namespace dr2386
 namespace std {
 template <typename T> struct tuple_size;
-template <> struct std::tuple_size<dr2386::Bad1> {};
-template <> struct std::tuple_size<dr2386::Bad2> {
+template <> struct tuple_size<dr2386::Bad1> {};
+template <> struct tuple_size<dr2386::Bad2> {
   static const int value = 42;
 };
 } // namespace std
diff --git a/clang/test/CXX/drs/dr7xx.cpp b/clang/test/CXX/drs/dr7xx.cpp
index 926bff1cc479c..2cbdc218ab7b5 100644
--- a/clang/test/CXX/drs/dr7xx.cpp
+++ b/clang/test/CXX/drs/dr7xx.cpp
@@ -105,7 +105,8 @@ namespace dr727 { // dr727: partial
       //   expected-note@#dr727-N {{explicitly specialized declaration is here}}
 
       template<> struct A::C<double>;
-      // expected-error at -1 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
+      // expected-error at -1 {{non-friend class member 'C' cannot have a qualified name}}
+      // expected-error at -2 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
       //   expected-note@#dr727-C {{explicitly specialized declaration is here}}
       template<> void A::f<double>();
       // expected-error at -1 {{o function template matches function template specialization 'f'}}
@@ -116,7 +117,8 @@ namespace dr727 { // dr727: partial
       //   expected-note@#dr727-N {{explicitly specialized declaration is here}}
 
       template<typename T> struct A::C<T***>;
-      // expected-error at -1 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
+      // expected-error at -1 {{non-friend class member 'C' cannot have a qualified name}}
+      // expected-error at -2 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
       //   expected-note@#dr727-C {{explicitly specialized declaration is here}}
       template<typename T> static int A::N<T***>;
       // expected-error at -1 {{non-friend class member 'N' cannot have a qualified name}}
diff --git a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
index 17645639fb82f..8eeb610f79469 100644
--- a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
@@ -75,7 +75,7 @@ struct X1 {
 
 template<typename T>
 template<typename U>
-void X1<T>::template B<U>::f() { }
+void X1<T>::template B<U>::f() { } // expected-warning{{'template' cannot be used after a declarative}}
 
 // PR5527
 template <template <class> class T>
diff --git a/clang/test/CXX/temp/temp.names/p5.cpp b/clang/test/CXX/temp/temp.names/p5.cpp
new file mode 100644
index 0000000000000..6f0925852a402
--- /dev/null
+++ b/clang/test/CXX/temp/temp.names/p5.cpp
@@ -0,0 +1,206 @@
+// RUN: %clang_cc1 -fsyntax-only -pedantic-errors -verify %s
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    // FIXME: The standard does not seem to consider non-friend elaborated-type-specifiers that
+    // declare partial specializations/explicit specializations/explicit instantiations to be
+    // declarative, see https://lists.isocpp.org/core/2024/01/15325.php
+    struct C;
+    template<typename V> struct D;
+
+    void f();
+    template<typename V> void g();
+
+    static int x;
+    template<typename V> static int y;
+
+    enum class E;
+  };
+};
+
+template<typename T>
+template<typename U>
+struct A<T>::template B<U>::C { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+struct A<int>::template B<bool>::C; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+struct A<int>::template B<bool>::C { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+struct A<T>::template B<U>::D<V*>; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+struct A<T>::B<U>::template D<V**>; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+struct A<T>::template B<U>::D { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+struct A<T>::template B<U>::D<V*> { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+struct A<T>::B<U>::template D<V**> { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+struct A<int>::template B<bool>::D; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+struct A<int>::template B<bool>::D<short>; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+struct A<int>::B<bool>::template D<long>; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+struct A<int>::template B<bool>::D<V*>; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+struct A<int>::B<bool>::template D<V**>; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+struct A<int>::template B<bool>::D { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+struct A<int>::template B<bool>::D<short> { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+struct A<int>::B<bool>::template D<long> { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+struct A<int>::template B<bool>::D<V*> { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+struct A<int>::B<bool>::template D<V**> { }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+void A<T>::template B<U>::f() { } // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+void A<int>::template B<bool>::f() { } // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+void A<T>::template B<U>::g() { } // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+void A<int>::B<bool>::template g<short>() { } // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+void A<int>::template B<bool>::g<long>() { } // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+void A<int>::template B<bool>::g() { } // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+int A<T>::template B<U>::x = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+int A<T>::template B<U>::y = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+int A<T>::template B<U>::y<V*> = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<typename T>
+template<typename U>
+template<typename V>
+int A<T>::B<U>::template y<V**> = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+int A<int>::template B<bool>::y = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+int A<int>::template B<bool>::y<short> = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<>
+int A<int>::B<bool>::template y<long> = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+int A<int>::template B<bool>::y<V*> = 0; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+template<typename V>
+int A<int>::B<bool>::template y<V**> = 0; // expected-error{{'template' cannot be used after a declarative}}
+template<typename T>
+template<typename U>
+enum class A<T>::template B<U>::E { a }; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+enum class A<int>::template B<bool>::E; // expected-error{{'template' cannot be used after a declarative}}
+
+template<>
+template<>
+enum class A<int>::template B<bool>::E { a }; // expected-error{{'template' cannot be used after a declarative}}
+
+// FIXME: We don't call Sema::diagnoseQualifiedDeclaration for friend declarations right now
+template<typename T>
+struct F {
+  // FIXME: f should be assumed to name a template per [temp.names] p3.4
+  friend void T::f<int>();
+  // expected-error at -1{{use 'template' keyword to treat 'f' as a dependent template name}}
+  // expected-error at -2{{no candidate function template was found for}}
+
+  // FIXME: We should diagnose the presence of 'template' here
+  friend void T::template f<int>(); // expected-error{{no candidate function template was found for}}
+  friend void T::template U<int>::f();
+
+  // These should be allowed
+  friend class T::template U<int>;
+  friend class T::template U<int>::V;
+};
diff --git a/clang/test/CXX/temp/temp.spec/part.spec.cpp b/clang/test/CXX/temp/temp.spec/part.spec.cpp
index f62050af69cff..4b0fdb902633a 100644
--- a/clang/test/CXX/temp/temp.spec/part.spec.cpp
+++ b/clang/test/CXX/temp/temp.spec/part.spec.cpp
@@ -478,4 +478,4 @@ template <typename T> class PCTT6<TestClass::PrivateClass, T> {
 };
 template <typename T1> template <typename, typename> class PCTT6<TestClass::PrivateClass, T1>::NCT4 final {};
 // expected-error at +1 2{{is a private member of}}
-template <typename T1> template <typename T2> struct PCTT6<TestClass::PrivateClass, T1>::template NCT3<T2, TestClass::TemplatePrivateClass<TestClass::TemplateProtectedClass<TestClass::PublicClass>>> : PCTT6<TestClass::PrivateClass, T1>::NCT4<T2, TestClass::TemplatePrivateClass<int>> {};
+template <typename T1> template <typename T2> struct PCTT6<TestClass::PrivateClass, T1>::NCT3<T2, TestClass::TemplatePrivateClass<TestClass::TemplateProtectedClass<TestClass::PublicClass>>> : PCTT6<TestClass::PrivateClass, T1>::NCT4<T2, TestClass::TemplatePrivateClass<int>> {};
diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp
index 6e1701602ae30..0d384b6b499f7 100644
--- a/clang/test/SemaCXX/static-assert.cpp
+++ b/clang/test/SemaCXX/static-assert.cpp
@@ -197,7 +197,7 @@ struct NestedTemplates1 {
 template <typename T, typename U, int a>
 void foo2() {
   static_assert(::ns::NestedTemplates1<T, a>::NestedTemplates2::template NestedTemplates3<U>::value, "message");
-  // expected-error at -1{{static assertion failed due to requirement '::ns::NestedTemplates1<int, 3>::NestedTemplates2::NestedTemplates3<float>::value': message}}
+  // expected-error at -1{{static assertion failed due to requirement '::ns::NestedTemplates1<int, 3>::NestedTemplates2::template NestedTemplates3<float>::value': message}}
 }
 template void foo2<int, float, 3>();
 // expected-note at -1{{in instantiation of function template specialization 'foo2<int, float, 3>' requested here}}
diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp
index e96ef44b7a251..56b8207bd9a43 100644
--- a/clang/test/SemaTemplate/class-template-spec.cpp
+++ b/clang/test/SemaTemplate/class-template-spec.cpp
@@ -74,14 +74,14 @@ namespace N {
 // Diagnose specialization errors
 struct A<double> { }; // expected-error{{template specialization requires 'template<>'}}
 
-template<> struct ::A<double>;
+template<> struct ::A<double>; // expected-warning {{extra qualification on member}}
 
 namespace N {
   template<typename T> struct B; // expected-note {{explicitly specialized}}
 
-  template<> struct ::N::B<char>; // okay
-  template<> struct ::N::B<short>; // okay
-  template<> struct ::N::B<int>; // okay
+  template<> struct ::N::B<char>; // expected-warning {{extra qualification on member}}
+  template<> struct ::N::B<short>; // expected-warning {{extra qualification on member}}
+  template<> struct ::N::B<int>; // expected-warning {{extra qualification on member}}
 
   int f(int);
 }



More information about the cfe-commits mailing list