Skip to content

Commit c5f4619

Browse files
authored
[Clang][Parse] Diagnose member template declarations with multiple declarators (#78243)
According to [temp.pre] p5: > In a template-declaration, explicit specialization, or explicit instantiation the init-declarator-list in the declaration shall contain at most one declarator. A member-declaration that is a template-declaration or explicit-specialization contains a declaration, even though it declares a member. This means it _will_ contain an init-declarator-list (not a member-declarator-list), so [temp.pre] p5 applies. This diagnoses declarations such as: ``` struct A { template<typename T> static const int x = 0, f(); // error: a template declaration can only declare a single entity template<typename T> static const int g(), y = 0; // error: a template declaration can only declare a single entity }; ``` The diagnostic messages are the same as those of the equivalent namespace scope declarations. Note: since we currently do not diagnose declarations with multiple abbreviated function template declarators at namespace scope e.g., `void f(auto), g(auto);`, so this patch does not add diagnostics for the equivalent member declarations. This patch also refactors `ParseSingleDeclarationAfterTemplate` (now named `ParseDeclarationAfterTemplate`) to call `ParseDeclGroup` and return the resultant `DeclGroup`.
1 parent 9acd61e commit c5f4619

File tree

8 files changed

+185
-205
lines changed

8 files changed

+185
-205
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ Improvements to Clang's diagnostics
144144
- Clang now applies syntax highlighting to the code snippets it
145145
prints.
146146

147+
- Clang now diagnoses member template declarations with multiple declarators.
148+
147149
Improvements to Clang's time-trace
148150
----------------------------------
149151

clang/include/clang/Parse/Parser.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,6 +2423,7 @@ class Parser : public CodeCompletionHandler {
24232423
bool MightBeDeclarator(DeclaratorContext Context);
24242424
DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context,
24252425
ParsedAttributes &Attrs,
2426+
ParsedTemplateInfo &TemplateInfo,
24262427
SourceLocation *DeclEnd = nullptr,
24272428
ForRangeInit *FRI = nullptr);
24282429
Decl *ParseDeclarationAfterDeclarator(Declarator &D,
@@ -3615,16 +3616,15 @@ class Parser : public CodeCompletionHandler {
36153616
// C++ 14: Templates [temp]
36163617

36173618
// C++ 14.1: Template Parameters [temp.param]
3618-
Decl *ParseDeclarationStartingWithTemplate(DeclaratorContext Context,
3619-
SourceLocation &DeclEnd,
3620-
ParsedAttributes &AccessAttrs,
3621-
AccessSpecifier AS = AS_none);
3622-
Decl *ParseTemplateDeclarationOrSpecialization(DeclaratorContext Context,
3623-
SourceLocation &DeclEnd,
3624-
ParsedAttributes &AccessAttrs,
3625-
AccessSpecifier AS);
3626-
Decl *ParseSingleDeclarationAfterTemplate(
3627-
DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
3619+
DeclGroupPtrTy
3620+
ParseDeclarationStartingWithTemplate(DeclaratorContext Context,
3621+
SourceLocation &DeclEnd,
3622+
ParsedAttributes &AccessAttrs);
3623+
DeclGroupPtrTy ParseTemplateDeclarationOrSpecialization(
3624+
DeclaratorContext Context, SourceLocation &DeclEnd,
3625+
ParsedAttributes &AccessAttrs, AccessSpecifier AS);
3626+
DeclGroupPtrTy ParseDeclarationAfterTemplate(
3627+
DeclaratorContext Context, ParsedTemplateInfo &TemplateInfo,
36283628
ParsingDeclRAIIObject &DiagsFromParams, SourceLocation &DeclEnd,
36293629
ParsedAttributes &AccessAttrs, AccessSpecifier AS = AS_none);
36303630
bool ParseTemplateParameters(MultiParseScope &TemplateScopes, unsigned Depth,
@@ -3673,12 +3673,12 @@ class Parser : public CodeCompletionHandler {
36733673
TemplateTy Template, SourceLocation OpenLoc);
36743674
ParsedTemplateArgument ParseTemplateTemplateArgument();
36753675
ParsedTemplateArgument ParseTemplateArgument();
3676-
Decl *ParseExplicitInstantiation(DeclaratorContext Context,
3677-
SourceLocation ExternLoc,
3678-
SourceLocation TemplateLoc,
3679-
SourceLocation &DeclEnd,
3680-
ParsedAttributes &AccessAttrs,
3681-
AccessSpecifier AS = AS_none);
3676+
DeclGroupPtrTy ParseExplicitInstantiation(DeclaratorContext Context,
3677+
SourceLocation ExternLoc,
3678+
SourceLocation TemplateLoc,
3679+
SourceLocation &DeclEnd,
3680+
ParsedAttributes &AccessAttrs,
3681+
AccessSpecifier AS = AS_none);
36823682
// C++2a: Template, concept definition [temp]
36833683
Decl *
36843684
ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,

clang/lib/Parse/ParseDecl.cpp

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,9 +1916,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context,
19161916
case tok::kw_export:
19171917
ProhibitAttributes(DeclAttrs);
19181918
ProhibitAttributes(DeclSpecAttrs);
1919-
SingleDecl =
1920-
ParseDeclarationStartingWithTemplate(Context, DeclEnd, DeclAttrs);
1921-
break;
1919+
return ParseDeclarationStartingWithTemplate(Context, DeclEnd, DeclAttrs);
19221920
case tok::kw_inline:
19231921
// Could be the start of an inline namespace. Allowed as an ext in C++03.
19241922
if (getLangOpts().CPlusPlus && NextToken().is(tok::kw_namespace)) {
@@ -1994,8 +1992,9 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(
19941992
ParsingDeclSpec DS(*this);
19951993
DS.takeAttributesFrom(DeclSpecAttrs);
19961994

1995+
ParsedTemplateInfo TemplateInfo;
19971996
DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context);
1998-
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext);
1997+
ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext);
19991998

20001999
// If we had a free-standing type definition with a missing semicolon, we
20012000
// may get this far before the problem becomes obvious.
@@ -2027,7 +2026,7 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(
20272026
if (DeclSpecStart)
20282027
DS.SetRangeStart(*DeclSpecStart);
20292028

2030-
return ParseDeclGroup(DS, Context, DeclAttrs, &DeclEnd, FRI);
2029+
return ParseDeclGroup(DS, Context, DeclAttrs, TemplateInfo, &DeclEnd, FRI);
20312030
}
20322031

20332032
/// Returns true if this might be the start of a declarator, or a common typo
@@ -2184,6 +2183,7 @@ void Parser::SkipMalformedDecl() {
21842183
Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
21852184
DeclaratorContext Context,
21862185
ParsedAttributes &Attrs,
2186+
ParsedTemplateInfo &TemplateInfo,
21872187
SourceLocation *DeclEnd,
21882188
ForRangeInit *FRI) {
21892189
// Parse the first declarator.
@@ -2193,8 +2193,19 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
21932193
ParsedAttributes LocalAttrs(AttrFactory);
21942194
LocalAttrs.takeAllFrom(Attrs);
21952195
ParsingDeclarator D(*this, DS, LocalAttrs, Context);
2196+
if (TemplateInfo.TemplateParams)
2197+
D.setTemplateParameterLists(*TemplateInfo.TemplateParams);
2198+
2199+
bool IsTemplateSpecOrInst =
2200+
(TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
2201+
TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
2202+
SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
2203+
21962204
ParseDeclarator(D);
21972205

2206+
if (IsTemplateSpecOrInst)
2207+
SAC.done();
2208+
21982209
// Bail out if the first declarator didn't seem well-formed.
21992210
if (!D.hasName() && !D.mayOmitIdentifier()) {
22002211
SkipMalformedDecl();
@@ -2262,15 +2273,54 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
22622273
// need to handle the file scope definition case.
22632274
if (Context == DeclaratorContext::File) {
22642275
if (isStartOfFunctionDefinition(D)) {
2276+
// C++23 [dcl.typedef] p1:
2277+
// The typedef specifier shall not be [...], and it shall not be
2278+
// used in the decl-specifier-seq of a parameter-declaration nor in
2279+
// the decl-specifier-seq of a function-definition.
22652280
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
2266-
Diag(Tok, diag::err_function_declared_typedef);
2267-
2268-
// Recover by treating the 'typedef' as spurious.
2281+
// If the user intended to write 'typename', we should have already
2282+
// suggested adding it elsewhere. In any case, recover by ignoring
2283+
// 'typedef' and suggest removing it.
2284+
Diag(DS.getStorageClassSpecLoc(),
2285+
diag::err_function_declared_typedef)
2286+
<< FixItHint::CreateRemoval(DS.getStorageClassSpecLoc());
22692287
DS.ClearStorageClassSpecs();
22702288
}
2289+
Decl *TheDecl = nullptr;
2290+
2291+
if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) {
2292+
if (D.getName().getKind() != UnqualifiedIdKind::IK_TemplateId) {
2293+
// If the declarator-id is not a template-id, issue a diagnostic
2294+
// and recover by ignoring the 'template' keyword.
2295+
Diag(Tok, diag::err_template_defn_explicit_instantiation) << 0;
2296+
TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(),
2297+
&LateParsedAttrs);
2298+
} else {
2299+
SourceLocation LAngleLoc =
2300+
PP.getLocForEndOfToken(TemplateInfo.TemplateLoc);
2301+
Diag(D.getIdentifierLoc(),
2302+
diag::err_explicit_instantiation_with_definition)
2303+
<< SourceRange(TemplateInfo.TemplateLoc)
2304+
<< FixItHint::CreateInsertion(LAngleLoc, "<>");
2305+
2306+
// Recover as if it were an explicit specialization.
2307+
TemplateParameterLists FakedParamLists;
2308+
FakedParamLists.push_back(Actions.ActOnTemplateParameterList(
2309+
0, SourceLocation(), TemplateInfo.TemplateLoc, LAngleLoc,
2310+
std::nullopt, LAngleLoc, nullptr));
2311+
2312+
TheDecl = ParseFunctionDefinition(
2313+
D,
2314+
ParsedTemplateInfo(&FakedParamLists,
2315+
/*isSpecialization=*/true,
2316+
/*lastParameterListWasEmpty=*/true),
2317+
&LateParsedAttrs);
2318+
}
2319+
} else {
2320+
TheDecl =
2321+
ParseFunctionDefinition(D, TemplateInfo, &LateParsedAttrs);
2322+
}
22712323

2272-
Decl *TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(),
2273-
&LateParsedAttrs);
22742324
return Actions.ConvertDeclToDeclGroup(TheDecl);
22752325
}
22762326

@@ -2360,8 +2410,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
23602410
}
23612411

23622412
SmallVector<Decl *, 8> DeclsInGroup;
2363-
Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(
2364-
D, ParsedTemplateInfo(), FRI);
2413+
Decl *FirstDecl =
2414+
ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo, FRI);
23652415
if (LateParsedAttrs.size() > 0)
23662416
ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false);
23672417
D.complete(FirstDecl);
@@ -2384,6 +2434,16 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
23842434
break;
23852435
}
23862436

2437+
// C++23 [temp.pre]p5:
2438+
// In a template-declaration, explicit specialization, or explicit
2439+
// instantiation the init-declarator-list in the declaration shall
2440+
// contain at most one declarator.
2441+
if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
2442+
D.isFirstDeclarator()) {
2443+
Diag(CommaLoc, diag::err_multiple_template_declarators)
2444+
<< TemplateInfo.Kind;
2445+
}
2446+
23872447
// Parse the next declarator.
23882448
D.clear();
23892449
D.setCommaLoc(CommaLoc);
@@ -2413,7 +2473,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
24132473
// declarator requires-clause
24142474
if (Tok.is(tok::kw_requires))
24152475
ParseTrailingRequiresClause(D);
2416-
Decl *ThisDecl = ParseDeclarationAfterDeclarator(D);
2476+
Decl *ThisDecl = ParseDeclarationAfterDeclarator(D, TemplateInfo);
24172477
D.complete(ThisDecl);
24182478
if (ThisDecl)
24192479
DeclsInGroup.push_back(ThisDecl);
@@ -6526,6 +6586,17 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
65266586
/*ObjectHasErrors=*/false, EnteringContext);
65276587
}
65286588

6589+
// C++23 [basic.scope.namespace]p1:
6590+
// For each non-friend redeclaration or specialization whose target scope
6591+
// is or is contained by the scope, the portion after the declarator-id,
6592+
// class-head-name, or enum-head-name is also included in the scope.
6593+
// C++23 [basic.scope.class]p1:
6594+
// For each non-friend redeclaration or specialization whose target scope
6595+
// is or is contained by the scope, the portion after the declarator-id,
6596+
// class-head-name, or enum-head-name is also included in the scope.
6597+
//
6598+
// FIXME: We should not be doing this for friend declarations; they have
6599+
// their own special lookup semantics specified by [basic.lookup.unqual]p6.
65296600
if (D.getCXXScopeSpec().isValid()) {
65306601
if (Actions.ShouldEnterDeclaratorScope(getCurScope(),
65316602
D.getCXXScopeSpec()))

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,7 +2855,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
28552855
}
28562856

28572857
// static_assert-declaration. A templated static_assert declaration is
2858-
// diagnosed in Parser::ParseSingleDeclarationAfterTemplate.
2858+
// diagnosed in Parser::ParseDeclarationAfterTemplate.
28592859
if (!TemplateInfo.Kind &&
28602860
Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
28612861
SourceLocation DeclEnd;
@@ -2868,9 +2868,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
28682868
"Nested template improperly parsed?");
28692869
ObjCDeclContextSwitch ObjCDC(*this);
28702870
SourceLocation DeclEnd;
2871-
return DeclGroupPtrTy::make(
2872-
DeclGroupRef(ParseTemplateDeclarationOrSpecialization(
2873-
DeclaratorContext::Member, DeclEnd, AccessAttrs, AS)));
2871+
return ParseTemplateDeclarationOrSpecialization(DeclaratorContext::Member,
2872+
DeclEnd, AccessAttrs, AS);
28742873
}
28752874

28762875
// Handle: member-declaration ::= '__extension__' member-declaration
@@ -3279,6 +3278,16 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
32793278
break;
32803279
}
32813280

3281+
// C++23 [temp.pre]p5:
3282+
// In a template-declaration, explicit specialization, or explicit
3283+
// instantiation the init-declarator-list in the declaration shall
3284+
// contain at most one declarator.
3285+
if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
3286+
DeclaratorInfo.isFirstDeclarator()) {
3287+
Diag(CommaLoc, diag::err_multiple_template_declarators)
3288+
<< TemplateInfo.Kind;
3289+
}
3290+
32823291
// Parse the next declarator.
32833292
DeclaratorInfo.clear();
32843293
VS.clear();
@@ -4228,6 +4237,24 @@ void Parser::ParseTrailingRequiresClause(Declarator &D) {
42284237

42294238
SourceLocation RequiresKWLoc = ConsumeToken();
42304239

4240+
// C++23 [basic.scope.namespace]p1:
4241+
// For each non-friend redeclaration or specialization whose target scope
4242+
// is or is contained by the scope, the portion after the declarator-id,
4243+
// class-head-name, or enum-head-name is also included in the scope.
4244+
// C++23 [basic.scope.class]p1:
4245+
// For each non-friend redeclaration or specialization whose target scope
4246+
// is or is contained by the scope, the portion after the declarator-id,
4247+
// class-head-name, or enum-head-name is also included in the scope.
4248+
//
4249+
// FIXME: We should really be calling ParseTrailingRequiresClause in
4250+
// ParseDirectDeclarator, when we are already in the declarator scope.
4251+
// This would also correctly suppress access checks for specializations
4252+
// and explicit instantiations, which we currently do not do.
4253+
CXXScopeSpec &SS = D.getCXXScopeSpec();
4254+
DeclaratorScopeObj DeclScopeObj(*this, SS);
4255+
if (SS.isValid() && Actions.ShouldEnterDeclaratorScope(getCurScope(), SS))
4256+
DeclScopeObj.EnterDeclaratorScope();
4257+
42314258
ExprResult TrailingRequiresClause;
42324259
ParseScope ParamScope(this, Scope::DeclScope |
42334260
Scope::FunctionDeclarationScope |

0 commit comments

Comments
 (0)