Skip to content

Commit c6e425a

Browse files
committed
[Parse] Disallow space between attribute name and '(' in Swift 6 mode
This allows us to resolve disambiguities of whether a parenthesis belong to an argument to the attribute or if it is eg. the start of a tuple.
1 parent cc5e79b commit c6e425a

File tree

6 files changed

+108
-42
lines changed

6 files changed

+108
-42
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,9 @@ ERROR(decl_attribute_applied_to_type,none,
15141514
ERROR(attr_extra_whitespace_after_at,PointsToFirstBadToken,
15151515
"extraneous whitespace between '@' and attribute name", ())
15161516

1517+
ERROR(attr_extra_whitespace_before_lparen,PointsToFirstBadToken,
1518+
"extraneous whitespace between attribute name and '('", ())
1519+
15171520
ERROR(attr_expected_lparen,none,
15181521
"expected '(' in '%0' %select{attribute|modifier}1", (StringRef, bool))
15191522

include/swift/Parse/Parser.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,18 @@ class Parser {
602602
return true;
603603
}
604604

605+
/// Consume a '('. If it is not directly following the previous token, emit an
606+
/// error (Swift 6) or warning (Swift <6) that attribute name and parentheses
607+
/// must not be separated by a space.
608+
SourceLoc consumeAttributeLParen();
609+
610+
/// If the next token is a '(' that's not on a new line consume it, and error
611+
/// (Swift 6) or warn (Swift <6) that the attribute must not be separted from
612+
/// the '(' by a space.
613+
///
614+
/// If the next token is not '(' or it's on a new line, return false.
615+
bool consumeIfAttributeLParen();
616+
605617
bool consumeIfNotAtStartOfLine(tok K) {
606618
if (Tok.isAtStartOfLine()) return false;
607619
return consumeIf(K);

lib/Parse/ParseDecl.cpp

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,14 @@ bool Parser::parseSpecializeAttribute(
10931093
llvm::function_ref<bool(Parser &)> parseSILSIPModule) {
10941094
assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square);
10951095

1096-
SourceLoc lParenLoc = consumeToken();
1096+
SourceLoc lParenLoc;
1097+
if (Tok.is(tok::l_paren)) {
1098+
lParenLoc = consumeAttributeLParen();
1099+
} else {
1100+
// SIL parsing is positioned at _specialize when entering this and parses
1101+
// the location of the _specialize keyword as the lParenLoc.
1102+
lParenLoc = consumeToken();
1103+
}
10971104
bool DiscardAttribute = false;
10981105
StringRef AttrName = "_specialize";
10991106

@@ -1157,7 +1164,7 @@ Parser::parseStorageRestrictionsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
11571164
}
11581165

11591166
// Consume '('
1160-
SourceLoc lParenLoc = consumeToken();
1167+
SourceLoc lParenLoc = consumeAttributeLParen();
11611168

11621169
SmallVector<Identifier> initializesProperties;
11631170
SmallVector<Identifier> accessesProperties;
@@ -1319,7 +1326,7 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
13191326
return Status;
13201327
}
13211328

1322-
SourceLoc lParenLoc = consumeToken();
1329+
SourceLoc lParenLoc = consumeAttributeLParen();
13231330

13241331
DeclNameLoc MemberNameLoc;
13251332
DeclNameRef MemberName;
@@ -1379,13 +1386,13 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
13791386
ParserResult<DifferentiableAttr>
13801387
Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) {
13811388
StringRef AttrName = "differentiable";
1382-
SourceLoc lParenLoc = loc, rParenLoc = loc;
1389+
SourceLoc rParenLoc = loc;
13831390
DifferentiabilityKind diffKind = DifferentiabilityKind::Normal;
13841391
SmallVector<ParsedAutoDiffParameter, 8> parameters;
13851392
TrailingWhereClause *whereClause = nullptr;
13861393

13871394
// Parse '('.
1388-
if (consumeIf(tok::l_paren, lParenLoc)) {
1395+
if (consumeIfAttributeLParen()) {
13891396
// Parse @differentiable attribute arguments.
13901397
if (parseDifferentiableAttributeArguments(
13911398
diffKind, parameters, whereClause))
@@ -1415,7 +1422,7 @@ bool Parser::parseExternAttribute(DeclAttributes &Attributes,
14151422
SourceLoc lParenLoc = Tok.getLoc(), rParenLoc;
14161423

14171424
// Parse @_extern(<language>, ...)
1418-
if (!consumeIf(tok::l_paren)) {
1425+
if (!consumeIfAttributeLParen()) {
14191426
diagnose(Loc, diag::attr_expected_lparen, AttrName,
14201427
DeclAttribute::isDeclModifier(DAK_Extern));
14211428
return false;
@@ -1857,7 +1864,7 @@ static bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError,
18571864
ParserResult<DerivativeAttr> Parser::parseDerivativeAttribute(SourceLoc atLoc,
18581865
SourceLoc loc) {
18591866
StringRef AttrName = "derivative";
1860-
SourceLoc lParenLoc = loc, rParenLoc = loc;
1867+
SourceLoc rParenLoc = loc;
18611868
TypeRepr *baseType = nullptr;
18621869
DeclNameRefWithLoc original;
18631870
SmallVector<ParsedAutoDiffParameter, 8> parameters;
@@ -1885,7 +1892,7 @@ ParserResult<DerivativeAttr> Parser::parseDerivativeAttribute(SourceLoc atLoc,
18851892
return errorAndSkipUntilConsumeRightParen(*this, AttrName);
18861893
};
18871894
// Parse '('.
1888-
if (!consumeIf(tok::l_paren, lParenLoc)) {
1895+
if (!consumeIfAttributeLParen()) {
18891896
diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName,
18901897
/*DeclModifier*/ false);
18911898
return makeParserError();
@@ -1937,7 +1944,7 @@ ParserResult<DerivativeAttr> Parser::parseDerivativeAttribute(SourceLoc atLoc,
19371944
ParserResult<TransposeAttr> Parser::parseTransposeAttribute(SourceLoc atLoc,
19381945
SourceLoc loc) {
19391946
StringRef AttrName = "transpose";
1940-
SourceLoc lParenLoc = loc, rParenLoc = loc;
1947+
SourceLoc rParenLoc = loc;
19411948
TypeRepr *baseType = nullptr;
19421949
DeclNameRefWithLoc original;
19431950
SmallVector<ParsedAutoDiffParameter, 8> parameters;
@@ -1966,7 +1973,7 @@ ParserResult<TransposeAttr> Parser::parseTransposeAttribute(SourceLoc atLoc,
19661973
};
19671974

19681975
// Parse '('.
1969-
if (!consumeIf(tok::l_paren, lParenLoc)) {
1976+
if (!consumeIfAttributeLParen()) {
19701977
diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName,
19711978
/*DeclModifier*/ false);
19721979
return makeParserError();
@@ -2270,7 +2277,7 @@ bool Parser::parseBackDeployedAttribute(DeclAttributes &Attributes,
22702277
SourceLoc Loc) {
22712278
std::string AtAttrName = (llvm::Twine("@") + AttrName).str();
22722279
auto LeftLoc = Tok.getLoc();
2273-
if (!consumeIf(tok::l_paren)) {
2280+
if (!consumeIfAttributeLParen()) {
22742281
diagnose(Loc, diag::attr_expected_lparen, AtAttrName,
22752282
DeclAttribute::isDeclModifier(DAK_BackDeployed));
22762283
return false;
@@ -2418,7 +2425,7 @@ Parser::parseDocumentationAttribute(SourceLoc AtLoc, SourceLoc Loc) {
24182425
llvm::Optional<AccessLevel> Visibility = llvm::None;
24192426
llvm::Optional<StringRef> Metadata = llvm::None;
24202427

2421-
if (!consumeIf(tok::l_paren)) {
2428+
if (!consumeIfAttributeLParen()) {
24222429
diagnose(Loc, diag::attr_expected_lparen, AttrName,
24232430
declModifier);
24242431
return makeParserError();
@@ -2503,7 +2510,7 @@ Parser::parseMacroRoleAttribute(
25032510
}
25042511

25052512
// Parse the argments.
2506-
SourceLoc lParenLoc = consumeToken();
2513+
SourceLoc lParenLoc = consumeAttributeLParen();
25072514
SourceLoc rParenLoc;
25082515
llvm::Optional<MacroRole> role;
25092516
bool sawRole = false;
@@ -2750,7 +2757,7 @@ static llvm::Optional<Identifier> parseSingleAttrOptionImpl(
27502757
return llvm::None;
27512758
}
27522759

2753-
P.consumeToken(tok::l_paren);
2760+
P.consumeAttributeLParen();
27542761

27552762
StringRef parsedName = P.Tok.getText();
27562763
if (!P.consumeIf(tok::identifier)) {
@@ -2913,7 +2920,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
29132920
break;
29142921

29152922
case DAK_Effects: {
2916-
if (!consumeIf(tok::l_paren)) {
2923+
if (!consumeIfAttributeLParen()) {
29172924
diagnose(Loc, diag::attr_expected_lparen, AttrName,
29182925
DeclAttribute::isDeclModifier(DK));
29192926
return makeParserSuccess();
@@ -3093,7 +3100,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
30933100
break;
30943101
}
30953102

3096-
consumeToken(tok::l_paren);
3103+
consumeAttributeLParen();
30973104

30983105
// Parse the subject.
30993106
if (Tok.isContextualKeyword("set")) {
@@ -3145,7 +3152,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
31453152
}
31463153

31473154
case DAK_SPIAccessControl: {
3148-
if (!consumeIf(tok::l_paren)) {
3155+
if (!consumeIfAttributeLParen()) {
31493156
diagnose(Loc, diag::attr_expected_lparen, AttrName,
31503157
DeclAttribute::isDeclModifier(DK));
31513158
return makeParserSuccess();
@@ -3186,7 +3193,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
31863193
case DAK_CDecl:
31873194
case DAK_Expose:
31883195
case DAK_SILGenName: {
3189-
if (!consumeIf(tok::l_paren)) {
3196+
if (!consumeIfAttributeLParen()) {
31903197
diagnose(Loc, diag::attr_expected_lparen, AttrName,
31913198
DeclAttribute::isDeclModifier(DK));
31923199
return makeParserSuccess();
@@ -3293,7 +3300,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
32933300
}
32943301

32953302
case DAK_Section: {
3296-
if (!consumeIf(tok::l_paren)) {
3303+
if (!consumeIfAttributeLParen()) {
32973304
diagnose(Loc, diag::attr_expected_lparen, AttrName,
32983305
DeclAttribute::isDeclModifier(DK));
32993306
return makeParserSuccess();
@@ -3333,12 +3340,12 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
33333340
}
33343341

33353342
case DAK_Alignment: {
3336-
if (!consumeIf(tok::l_paren)) {
3343+
if (!consumeIfAttributeLParen()) {
33373344
diagnose(Loc, diag::attr_expected_lparen, AttrName,
33383345
DeclAttribute::isDeclModifier(DK));
33393346
return makeParserSuccess();
33403347
}
3341-
3348+
33423349
if (Tok.isNot(tok::integer_literal)) {
33433350
diagnose(Loc, diag::alignment_must_be_positive_integer);
33443351
return makeParserSuccess();
@@ -3380,7 +3387,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
33803387
}
33813388

33823389
case DAK_Semantics: {
3383-
if (!consumeIf(tok::l_paren)) {
3390+
if (!consumeIfAttributeLParen()) {
33843391
diagnose(Loc, diag::attr_expected_lparen, AttrName,
33853392
DeclAttribute::isDeclModifier(DK));
33863393
return makeParserSuccess();
@@ -3415,7 +3422,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
34153422
}
34163423
case DAK_OriginallyDefinedIn: {
34173424
auto LeftLoc = Tok.getLoc();
3418-
if (!consumeIf(tok::l_paren)) {
3425+
if (!consumeIfAttributeLParen()) {
34193426
diagnose(Loc, diag::attr_expected_lparen, AttrName,
34203427
DeclAttribute::isDeclModifier(DK));
34213428
return makeParserSuccess();
@@ -3505,7 +3512,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
35053512
break;
35063513
}
35073514
case DAK_Available: {
3508-
if (!consumeIf(tok::l_paren)) {
3515+
if (!consumeIfAttributeLParen()) {
35093516
diagnose(Loc, diag::attr_expected_lparen, AttrName,
35103517
DeclAttribute::isDeclModifier(DK));
35113518
return makeParserSuccess();
@@ -3523,7 +3530,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
35233530
DeclAttribute::isDeclModifier(DK));
35243531
return makeParserSuccess();
35253532
}
3526-
SourceLoc LParenLoc = consumeToken(tok::l_paren);
3533+
SourceLoc LParenLoc = consumeAttributeLParen();
35273534
llvm::Optional<StringRef> filename;
35283535
{
35293536
// Parse 'sourceFile'.
@@ -3574,7 +3581,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
35743581
}
35753582

35763583
// Parse the leading '('.
3577-
SourceLoc LParenLoc = consumeToken(tok::l_paren);
3584+
SourceLoc LParenLoc = consumeAttributeLParen();
35783585

35793586
// Parse the names, with trailing colons (if there are present) and populate
35803587
// the inout parameters
@@ -3640,7 +3647,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
36403647
return makeParserSuccess();
36413648
}
36423649

3643-
SourceLoc LParenLoc = consumeToken(tok::l_paren);
3650+
SourceLoc LParenLoc = consumeAttributeLParen();
36443651
DeclNameRef replacedFunction;
36453652
{
36463653
// Parse 'for'.
@@ -3691,7 +3698,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
36913698
return makeParserSuccess();
36923699
}
36933700

3694-
SourceLoc LParenLoc = consumeToken(tok::l_paren);
3701+
SourceLoc LParenLoc = consumeAttributeLParen();
36953702
ParserResult<TypeRepr> ErasedType;
36963703
bool invalid = false;
36973704
{
@@ -3792,7 +3799,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
37923799

37933800
case DAK_UnavailableFromAsync: {
37943801
StringRef message;
3795-
if (consumeIf(tok::l_paren)) {
3802+
if (consumeIfAttributeLParen()) {
37963803
if (!Tok.is(tok::identifier)) {
37973804
llvm_unreachable("Flag must start with an identifier");
37983805
}
@@ -3882,7 +3889,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
38823889
return makeParserSuccess();
38833890
}
38843891

3885-
consumeToken(tok::l_paren);
3892+
consumeAttributeLParen();
38863893

38873894
if (!Tok.canBeArgumentLabel()) {
38883895
diagnose(Loc, diag::attr_rawlayout_expected_label, "'size', 'like', or 'likeArrayOf'");
@@ -4160,6 +4167,10 @@ ParserResult<CustomAttr> Parser::parseCustomAttribute(
41604167

41614168
initParser.emplace(*this, initContext);
41624169
}
4170+
if (getEndOfPreviousLoc() != Tok.getLoc()) {
4171+
diagnose(getEndOfPreviousLoc(), diag::attr_extra_whitespace_before_lparen)
4172+
.warnUntilSwiftVersion(6);
4173+
}
41634174
auto result = parseArgumentList(tok::l_paren, tok::r_paren,
41644175
/*isExprBasic*/ true,
41654176
/*allowTrailingClosure*/ false);
@@ -4427,10 +4438,11 @@ static bool parseDifferentiableTypeAttributeArgument(
44274438
SourceLoc &diffKindLocResult, bool emitDiagnostics) {
44284439
Parser::CancellableBacktrackingScope backtrack(P);
44294440

4430-
SourceLoc beginLoc, kindLoc, endLoc;
4441+
SourceLoc beginLoc = P.Tok.getLoc();
4442+
SourceLoc kindLoc, endLoc;
44314443

44324444
// Match '( <identifier> )', and store the identifier token to `argument`.
4433-
if (!P.consumeIf(tok::l_paren, beginLoc))
4445+
if (!P.consumeIfAttributeLParen())
44344446
return false;
44354447
auto argument = P.Tok;
44364448
if (!P.consumeIf(tok::identifier, kindLoc))
@@ -4498,7 +4510,7 @@ bool Parser::parseConventionAttributeInternal(SourceLoc atLoc, SourceLoc attrLoc
44984510
ConventionTypeAttr *&result,
44994511
bool justChecking) {
45004512
SourceLoc LPLoc = Tok.getLoc();
4501-
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
4513+
if (!consumeIfAttributeLParen()) {
45024514
if (!justChecking)
45034515
diagnose(Tok, diag::convention_attribute_expected_lparen);
45044516
return true;
@@ -4764,7 +4776,7 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result,
47644776
case TAK_opened: {
47654777
// Parse the opened existential ID string in parens
47664778
SourceLoc beginLoc = Tok.getLoc(), idLoc, endLoc;
4767-
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
4779+
if (!consumeAttributeLParen()) {
47684780
if (!justChecking)
47694781
diagnose(Tok, diag::opened_attribute_expected_lparen);
47704782
return makeParserError();
@@ -4817,7 +4829,7 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result,
48174829

48184830
// Parse the opened ID string in parens
48194831
SourceLoc beginLoc = Tok.getLoc(), idLoc, endLoc;
4820-
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
4832+
if (!consumeIfAttributeLParen()) {
48214833
if (!justChecking)
48224834
diagnose(Tok, diag::pack_element_attribute_expected_lparen);
48234835
return makeParserError();
@@ -4892,12 +4904,12 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result,
48924904
case TAK__opaqueReturnTypeOf: {
48934905
// Parse the mangled decl name and index.
48944906
auto beginLoc = Tok.getLoc();
4895-
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
4907+
if (!consumeIfAttributeLParen()) {
48964908
if (!justChecking)
48974909
diagnose(Tok, diag::attr_expected_lparen, "_opaqueReturnTypeOf", false);
48984910
return makeParserError();
48994911
}
4900-
4912+
49014913
if (!Tok.is(tok::string_literal)) {
49024914
if (!justChecking)
49034915
diagnose(Tok, diag::opened_attribute_id_value);

lib/Parse/Parser.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,23 @@ SourceLoc Parser::getEndOfPreviousLoc() const {
576576
return Lexer::getLocForEndOfToken(SourceMgr, PreviousLoc);
577577
}
578578

579+
SourceLoc Parser::consumeAttributeLParen() {
580+
SourceLoc LastTokenEndLoc = getEndOfPreviousLoc();
581+
if (LastTokenEndLoc != Tok.getLoc() && !isInSILMode()) {
582+
diagnose(LastTokenEndLoc, diag::attr_extra_whitespace_before_lparen)
583+
.warnUntilSwiftVersion(6);
584+
}
585+
return consumeToken(tok::l_paren);
586+
}
587+
588+
bool Parser::consumeIfAttributeLParen() {
589+
if (!Tok.isFollowingLParen()) {
590+
return false;
591+
}
592+
consumeAttributeLParen();
593+
return true;
594+
}
595+
579596
SourceLoc Parser::consumeStartingCharacterOfCurrentToken(tok Kind, size_t Len) {
580597
// Consumes prefix of token and returns its location.
581598
// (like '?', '<', '>' or '!' immediately followed by '<')

0 commit comments

Comments
 (0)