Skip to content

Commit 529d16d

Browse files
committed
[Clang][Sema] Diagnose use of template keyword after declarative nested-name-specifiers
1 parent c5edef6 commit 529d16d

File tree

17 files changed

+308
-32
lines changed

17 files changed

+308
-32
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ Improvements to Clang's diagnostics
114114
- Clang now applies syntax highlighting to the code snippets it
115115
prints.
116116

117+
- Clang now diagnoses use of the ``template`` keyword after declarative nested name specifiers.
118+
119+
117120
Improvements to Clang's time-trace
118121
----------------------------------
119122

clang/include/clang/AST/TypeLoc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ class TypeLoc {
189189
/// pointer types, but not through decltype or typedefs.
190190
AutoTypeLoc getContainedAutoTypeLoc() const;
191191

192+
/// Get the SourceLocation of the template keyword (if any).
193+
SourceLocation getTemplateKeywordLoc() const;
194+
192195
/// Initializes this to state that every location in this
193196
/// type is the given location.
194197
///
@@ -1688,7 +1691,7 @@ class TemplateSpecializationTypeLoc :
16881691
}
16891692

16901693
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
1691-
setTemplateKeywordLoc(Loc);
1694+
setTemplateKeywordLoc(SourceLocation());
16921695
setTemplateNameLoc(Loc);
16931696
setLAngleLoc(Loc);
16941697
setRAngleLoc(Loc);

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8241,6 +8241,9 @@ def err_invalid_declarator_in_block : Error<
82418241
"definition or redeclaration of %0 not allowed inside a block">;
82428242
def err_not_tag_in_scope : Error<
82438243
"no %select{struct|interface|union|class|enum}0 named %1 in %2">;
8244+
def ext_template_after_declarative_nns : ExtWarn<
8245+
"'template' cannot be used after a declarative nested name specifier">,
8246+
InGroup<DiagGroup<"template-in-declaration-name">>;
82448247

82458248
def err_no_typeid_with_fno_rtti : Error<
82468249
"use of typeid requires -frtti">;

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2962,7 +2962,8 @@ class Sema final {
29622962
bool DiagnoseClassNameShadow(DeclContext *DC, DeclarationNameInfo Info);
29632963
bool diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
29642964
DeclarationName Name, SourceLocation Loc,
2965-
bool IsTemplateId);
2965+
TemplateIdAnnotation *TemplateId,
2966+
bool IsMemberSpecialization);
29662967
void
29672968
diagnoseIgnoredQualifiers(unsigned DiagID, unsigned Quals,
29682969
SourceLocation FallbackLoc,

clang/lib/AST/TypeLoc.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,3 +738,12 @@ AutoTypeLoc TypeLoc::getContainedAutoTypeLoc() const {
738738
return AutoTypeLoc();
739739
return Res.getAs<AutoTypeLoc>();
740740
}
741+
742+
SourceLocation TypeLoc::getTemplateKeywordLoc() const {
743+
if (const auto TSTL = getAsAdjusted<TemplateSpecializationTypeLoc>())
744+
return TSTL.getTemplateKeywordLoc();
745+
if (const auto DTSTL =
746+
getAsAdjusted<DependentTemplateSpecializationTypeLoc>())
747+
return DTSTL.getTemplateKeywordLoc();
748+
return SourceLocation();
749+
}

clang/lib/Parse/ParseDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6592,12 +6592,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
65926592
}
65936593

65946594
bool HadScope = D.getCXXScopeSpec().isValid();
6595+
SourceLocation TemplateKWLoc;
65956596
if (ParseUnqualifiedId(D.getCXXScopeSpec(),
65966597
/*ObjectType=*/nullptr,
65976598
/*ObjectHadErrors=*/false,
65986599
/*EnteringContext=*/true,
65996600
/*AllowDestructorName=*/true, AllowConstructorName,
6600-
AllowDeductionGuide, nullptr, D.getName()) ||
6601+
AllowDeductionGuide, &TemplateKWLoc,
6602+
D.getName()) ||
66016603
// Once we're past the identifier, if the scope was bad, mark the
66026604
// whole declarator bad.
66036605
D.getCXXScopeSpec().isInvalid()) {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6246,13 +6246,17 @@ bool Sema::DiagnoseClassNameShadow(DeclContext *DC,
62466246
///
62476247
/// \param Loc The location of the name of the entity being declared.
62486248
///
6249-
/// \param IsTemplateId Whether the name is a (simple-)template-id, and thus
6250-
/// we're declaring an explicit / partial specialization / instantiation.
6249+
/// \param IsMemberSpecialization Whether we are declaring a member
6250+
/// specialization.
6251+
///
6252+
/// \param TemplateId The template-id, if any.
62516253
///
62526254
/// \returns true if we cannot safely recover from this error, false otherwise.
62536255
bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
62546256
DeclarationName Name,
6255-
SourceLocation Loc, bool IsTemplateId) {
6257+
SourceLocation Loc,
6258+
TemplateIdAnnotation *TemplateId,
6259+
bool IsMemberSpecialization) {
62566260
DeclContext *Cur = CurContext;
62576261
while (isa<LinkageSpecDecl>(Cur) || isa<CapturedDecl>(Cur))
62586262
Cur = Cur->getParent();
@@ -6281,7 +6285,7 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
62816285
// Check whether the qualifying scope encloses the scope of the original
62826286
// declaration. For a template-id, we perform the checks in
62836287
// CheckTemplateSpecializationScope.
6284-
if (!Cur->Encloses(DC) && !IsTemplateId) {
6288+
if (!Cur->Encloses(DC) && !(TemplateId || IsMemberSpecialization)) {
62856289
if (Cur->isRecord())
62866290
Diag(Loc, diag::err_member_qualification)
62876291
<< Name << SS.getRange();
@@ -6327,12 +6331,32 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
63276331
return false;
63286332
}
63296333

6334+
// C++23 [temp.names]p5:
6335+
// The keyword template shall not appear immediately after a declarative
6336+
// nested-name-specifier.
6337+
//
6338+
// First check the template-id (if any), and then check each component of the
6339+
// nested-name-specifier in reverse order.
6340+
//
6341+
// FIXME: nested-name-specifiers in friend declarations are declarative,
6342+
// but we don't call diagnoseQualifiedDeclaration for them. We should.
6343+
if (TemplateId && TemplateId->TemplateKWLoc.isValid())
6344+
Diag(Loc, diag::ext_template_after_declarative_nns)
6345+
<< FixItHint::CreateRemoval(TemplateId->TemplateKWLoc);
6346+
6347+
NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
6348+
while (SpecLoc.getPrefix()) {
6349+
if (SpecLoc.getNestedNameSpecifier()->getKind() ==
6350+
NestedNameSpecifier::TypeSpecWithTemplate)
6351+
Diag(Loc, diag::ext_template_after_declarative_nns)
6352+
<< FixItHint::CreateRemoval(
6353+
SpecLoc.getTypeLoc().getTemplateKeywordLoc());
6354+
6355+
SpecLoc = SpecLoc.getPrefix();
6356+
}
63306357
// C++11 [dcl.meaning]p1:
63316358
// [...] "The nested-name-specifier of the qualified declarator-id shall
63326359
// not begin with a decltype-specifer"
6333-
NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
6334-
while (SpecLoc.getPrefix())
6335-
SpecLoc = SpecLoc.getPrefix();
63366360
if (isa_and_nonnull<DecltypeType>(
63376361
SpecLoc.getNestedNameSpecifier()->getAsType()))
63386362
Diag(Loc, diag::err_decltype_in_declarator)
@@ -6400,9 +6424,13 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
64006424
return nullptr;
64016425
}
64026426
if (!D.getDeclSpec().isFriendSpecified()) {
6403-
if (diagnoseQualifiedDeclaration(
6404-
D.getCXXScopeSpec(), DC, Name, D.getIdentifierLoc(),
6405-
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId)) {
6427+
TemplateIdAnnotation *TemplateId =
6428+
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
6429+
? D.getName().TemplateId
6430+
: nullptr;
6431+
if (diagnoseQualifiedDeclaration(D.getCXXScopeSpec(), DC, Name,
6432+
D.getIdentifierLoc(), TemplateId,
6433+
/*IsMemberSpecialization=*/false)) {
64066434
if (DC->isRecord())
64076435
return nullptr;
64086436

@@ -18006,6 +18034,7 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1800618034
// nested-name-specifier against the current context.
1800718035
if ((TUK == TUK_Definition || TUK == TUK_Declaration) &&
1800818036
diagnoseQualifiedDeclaration(SS, DC, OrigName, Loc,
18037+
/*TemplateId=*/nullptr,
1800918038
isMemberSpecialization))
1801018039
Invalid = true;
1801118040

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3621,14 +3621,18 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
36213621
// class X {
36223622
// int X::member;
36233623
// };
3624-
if (DeclContext *DC = computeDeclContext(SS, false))
3624+
if (DeclContext *DC = computeDeclContext(SS, false)) {
3625+
TemplateIdAnnotation *TemplateId =
3626+
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
3627+
? D.getName().TemplateId
3628+
: nullptr;
36253629
diagnoseQualifiedDeclaration(SS, DC, Name, D.getIdentifierLoc(),
3626-
D.getName().getKind() ==
3627-
UnqualifiedIdKind::IK_TemplateId);
3628-
else
3630+
TemplateId,
3631+
/*IsMemberSpecialization=*/false);
3632+
} else {
36293633
Diag(D.getIdentifierLoc(), diag::err_member_qualification)
36303634
<< Name << SS.getRange();
3631-
3635+
}
36323636
SS.clear();
36333637
}
36343638

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,8 +1890,12 @@ DeclResult Sema::CheckClassTemplate(
18901890
ContextRAII SavedContext(*this, SemanticContext);
18911891
if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
18921892
Invalid = true;
1893-
} else if (TUK != TUK_Friend && TUK != TUK_Reference)
1894-
diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc, false);
1893+
}
1894+
1895+
if (TUK != TUK_Friend && TUK != TUK_Reference)
1896+
diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc,
1897+
/*TemplateId-*/ nullptr,
1898+
/*IsMemberSpecialization*/ false);
18951899

18961900
LookupQualifiedName(Previous, SemanticContext);
18971901
} else {
@@ -8831,6 +8835,15 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
88318835
bool isMemberSpecialization = false;
88328836
bool isPartialSpecialization = false;
88338837

8838+
if (SS.isSet()) {
8839+
if (TUK != TUK_Reference && TUK != TUK_Friend &&
8840+
diagnoseQualifiedDeclaration(SS, ClassTemplate->getDeclContext(),
8841+
ClassTemplate->getDeclName(),
8842+
TemplateNameLoc, &TemplateId,
8843+
/*IsMemberSpecialization=*/false))
8844+
return true;
8845+
}
8846+
88348847
// Check the validity of the template headers that introduce this
88358848
// template.
88368849
// FIXME: We probably shouldn't complain about these headers for

clang/lib/Sema/TreeTransform.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4394,7 +4394,8 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
43944394
SS.Adopt(ETL.getQualifierLoc());
43954395
TL = ETL.getNamedTypeLoc();
43964396
}
4397-
SS.Extend(SemaRef.Context, /*FIXME:*/ SourceLocation(), TL,
4397+
4398+
SS.Extend(SemaRef.Context, TL.getTemplateKeywordLoc(), TL,
43984399
Q.getLocalEndLoc());
43994400
break;
44004401
}

clang/test/CXX/drs/dr23xx.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ struct Bad2 { int a, b; };
182182
} // namespace dr2386
183183
namespace std {
184184
template <typename T> struct tuple_size;
185-
template <> struct std::tuple_size<dr2386::Bad1> {};
186-
template <> struct std::tuple_size<dr2386::Bad2> {
185+
template <> struct tuple_size<dr2386::Bad1> {};
186+
template <> struct tuple_size<dr2386::Bad2> {
187187
static const int value = 42;
188188
};
189189
} // namespace std

clang/test/CXX/drs/dr7xx.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ namespace dr727 { // dr727: partial
105105
// expected-note@#dr727-N {{explicitly specialized declaration is here}}
106106

107107
template<> struct A::C<double>;
108-
// expected-error@-1 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
108+
// expected-error@-1 {{non-friend class member 'C' cannot have a qualified name}}
109+
// expected-error@-2 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
109110
// expected-note@#dr727-C {{explicitly specialized declaration is here}}
110111
template<> void A::f<double>();
111112
// expected-error@-1 {{o function template matches function template specialization 'f'}}
@@ -116,7 +117,8 @@ namespace dr727 { // dr727: partial
116117
// expected-note@#dr727-N {{explicitly specialized declaration is here}}
117118

118119
template<typename T> struct A::C<T***>;
119-
// expected-error@-1 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
120+
// expected-error@-1 {{non-friend class member 'C' cannot have a qualified name}}
121+
// expected-error@-2 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
120122
// expected-note@#dr727-C {{explicitly specialized declaration is here}}
121123
template<typename T> static int A::N<T***>;
122124
// expected-error@-1 {{non-friend class member 'N' cannot have a qualified name}}

clang/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ struct X1 {
7575

7676
template<typename T>
7777
template<typename U>
78-
void X1<T>::template B<U>::f() { }
78+
void X1<T>::template B<U>::f() { } // expected-warning{{'template' cannot be used after a declarative}}
7979

8080
// PR5527
8181
template <template <class> class T>

0 commit comments

Comments
 (0)