Skip to content

Commit cc5e79b

Browse files
committed
[Parse] Disallow space between @ and attribute name in Swift 6 mode
1 parent 4d53245 commit cc5e79b

File tree

5 files changed

+44
-15
lines changed

5 files changed

+44
-15
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,10 @@ ERROR(type_attribute_applied_to_decl,none,
15101510
ERROR(decl_attribute_applied_to_type,none,
15111511
"attribute can only be applied to declarations, not types", ())
15121512

1513+
1514+
ERROR(attr_extra_whitespace_after_at,PointsToFirstBadToken,
1515+
"extraneous whitespace between '@' and attribute name", ())
1516+
15131517
ERROR(attr_expected_lparen,none,
15141518
"expected '(' in '%0' %select{attribute|modifier}1", (StringRef, bool))
15151519

include/swift/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,7 @@ class Parser {
11471147

11481148
/// Parse a specific attribute.
11491149
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
1150+
SourceLoc AtEndLoc,
11501151
PatternBindingInitializer *&initContext,
11511152
bool isFromClangAttribute = false);
11521153

@@ -1256,6 +1257,7 @@ class Parser {
12561257
bool justChecking);
12571258

12581259
ParserStatus parseTypeAttribute(TypeOrCustomAttr &result, SourceLoc AtLoc,
1260+
SourceLoc AtEndLoc,
12591261
PatternBindingInitializer *&initContext,
12601262
bool justChecking = false);
12611263

lib/ClangImporter/ImportDecl.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8055,11 +8055,14 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
80558055
parser.consumeTokenWithoutFeedingReceiver();
80568056

80578057
bool hadError = false;
8058-
SourceLoc atLoc;
8059-
if (parser.consumeIf(tok::at_sign, atLoc)) {
8060-
hadError = parser.parseDeclAttribute(
8061-
MappedDecl->getAttrs(), atLoc, initContext,
8062-
/*isFromClangAttribute=*/true).isError();
8058+
if (parser.Tok.is(tok::at_sign)) {
8059+
SourceLoc atEndLoc = parser.Tok.getRange().getEnd();
8060+
SourceLoc atLoc = parser.consumeToken(tok::at_sign);
8061+
hadError = parser
8062+
.parseDeclAttribute(MappedDecl->getAttrs(), atLoc,
8063+
atEndLoc, initContext,
8064+
/*isFromClangAttribute=*/true)
8065+
.isError();
80638066
} else {
80648067
SourceLoc staticLoc;
80658068
StaticSpellingKind staticSpelling;

lib/Parse/ParseDecl.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4202,10 +4202,15 @@ ParserResult<CustomAttr> Parser::parseCustomAttribute(
42024202
/// Note that various attributes (like mutating, weak, and unowned) are parsed
42034203
/// but rejected since they have context-sensitive keywords.
42044204
///
4205-
ParserStatus Parser::parseDeclAttribute(
4206-
DeclAttributes &Attributes, SourceLoc AtLoc,
4207-
PatternBindingInitializer *&initContext,
4208-
bool isFromClangAttribute) {
4205+
ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,
4206+
SourceLoc AtLoc, SourceLoc AtEndLoc,
4207+
PatternBindingInitializer *&initContext,
4208+
bool isFromClangAttribute) {
4209+
if (AtEndLoc != Tok.getLoc()) {
4210+
diagnose(AtEndLoc, diag::attr_extra_whitespace_after_at)
4211+
.warnUntilSwiftVersion(6);
4212+
}
4213+
42094214
// If this not an identifier, the attribute is malformed.
42104215
if (Tok.isNot(tok::identifier) &&
42114216
Tok.isNot(tok::kw_in) &&
@@ -4404,8 +4409,10 @@ ParserStatus Parser::parseDeclAttribute(
44044409
bool Parser::canParseTypeAttribute() {
44054410
TypeOrCustomAttr result; // ignored
44064411
PatternBindingInitializer *initContext = nullptr;
4407-
return !parseTypeAttribute(result, /*atLoc=*/SourceLoc(), initContext,
4408-
/*justChecking*/ true).isError();
4412+
return !parseTypeAttribute(result, /*atLoc=*/SourceLoc(),
4413+
/*atEndLoc=*/SourceLoc(), initContext,
4414+
/*justChecking*/ true)
4415+
.isError();
44094416
}
44104417

44114418
/// Parses the '@differentiable' type attribute argument (no argument list,
@@ -4602,9 +4609,14 @@ bool Parser::parseUUIDString(UUID &uuid, Diag<> diagnostic, bool justChecking) {
46024609
/// canParseTypeAttribute; don't emit any diagnostics, and there's
46034610
/// no need to actually record the attribute
46044611
ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result,
4605-
SourceLoc AtLoc,
4612+
SourceLoc AtLoc, SourceLoc AtEndLoc,
46064613
PatternBindingInitializer *&initContext,
46074614
bool justChecking) {
4615+
if (AtEndLoc != Tok.getLoc()) {
4616+
diagnose(AtEndLoc, diag::attr_extra_whitespace_after_at)
4617+
.warnUntilSwiftVersion(6);
4618+
}
4619+
46084620
// If this not an identifier, the attribute is malformed.
46094621
if (Tok.isNot(tok::identifier) &&
46104622
// These are keywords that we accept as attribute names.
@@ -5038,8 +5050,9 @@ ParserStatus Parser::parseDeclAttributeList(
50385050
ParserStatus Status;
50395051
while (Tok.isAny(tok::at_sign, tok::pound_if)) {
50405052
if (Tok.is(tok::at_sign)) {
5041-
SourceLoc AtLoc = consumeToken();
5042-
Status |= parseDeclAttribute(Attributes, AtLoc, initContext);
5053+
SourceLoc AtEndLoc = Tok.getRange().getEnd();
5054+
SourceLoc AtLoc = consumeToken();
5055+
Status |= parseDeclAttribute(Attributes, AtLoc, AtEndLoc, initContext);
50435056
} else {
50445057
if (!ifConfigsAreDeclAttrs && !ifConfigContainsOnlyAttributes())
50455058
break;
@@ -5330,8 +5343,9 @@ ParserStatus Parser::ParsedTypeAttributeList::slowParse(Parser &P) {
53305343
return status;
53315344

53325345
TypeOrCustomAttr result;
5346+
SourceLoc AtEndLoc = Tok.getRange().getEnd();
53335347
SourceLoc AtLoc = P.consumeToken();
5334-
status |= P.parseTypeAttribute(result, AtLoc, initContext);
5348+
status |= P.parseTypeAttribute(result, AtLoc, AtEndLoc, initContext);
53355349
if (status.isError())
53365350
return status;
53375351
if (result)

test/Parse/attribute_spacing.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 6
2+
3+
@ MainActor // expected-error {{extraneous whitespace between '@' and attribute name}}
4+
class Foo {
5+
func foo(_ x: @ escaping () -> Int) {} // expected-error {{extraneous whitespace between '@' and attribute name}}
6+
}

0 commit comments

Comments
 (0)