Skip to content

Commit 3fc4474

Browse files
committed
Only parse subscript and deinit as declaration references within attributes
The macro role attributes require us to parse `subscript` and `deinit` is normal member references. Thread an `ExprFlavor` through to make this happen, and do so for both `@attached` and `@freestanding`.
1 parent 9ce8dbc commit 3fc4474

File tree

6 files changed

+94
-17
lines changed

6 files changed

+94
-17
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ extension Parser {
6060
case derivative
6161
case differentiable
6262
case exclusivity
63+
case freestanding
6364
case inline
6465
case objc
6566
case Sendable
@@ -96,6 +97,7 @@ extension Parser {
9697
case TokenSpec(.derivative): self = .derivative
9798
case TokenSpec(.differentiable): self = .differentiable
9899
case TokenSpec(.exclusivity): self = .exclusivity
100+
case TokenSpec(.freestanding): self = .freestanding
99101
case TokenSpec(.inline): self = .inline
100102
case TokenSpec(.objc): self = .objc
101103
case TokenSpec(.Sendable): self = .Sendable
@@ -136,6 +138,7 @@ extension Parser {
136138
case .derivative: return .keyword(.derivative)
137139
case .differentiable: return .keyword(.differentiable)
138140
case .exclusivity: return .keyword(.exclusivity)
141+
case .freestanding: return .keyword(.freestanding)
139142
case .inline: return .keyword(.inline)
140143
case .objc: return .keyword(.objc)
141144
case .Sendable: return .keyword(.Sendable)
@@ -322,9 +325,9 @@ extension Parser {
322325
return parseAttribute(argumentMode: .optional) { parser in
323326
return .unavailableFromAsyncArguments(parser.parseUnavailableFromAsyncAttributeArguments())
324327
}
325-
case .attached:
328+
case .attached, .freestanding:
326329
return parseAttribute(argumentMode: .customAttribute) { parser in
327-
let arguments = parser.parseAttachedArguments()
330+
let arguments = parser.parseMacroRoleArguments()
328331
return .argumentList(RawLabeledExprListSyntax(elements: arguments, arena: parser.arena))
329332
}
330333
case .rethrows:
@@ -356,7 +359,7 @@ extension Parser {
356359
}
357360

358361
extension Parser {
359-
mutating func parseAttachedArguments() -> [RawLabeledExprSyntax] {
362+
mutating func parseMacroRoleArguments() -> [RawLabeledExprSyntax] {
360363
let (unexpectedBeforeRole, role) = self.expect(.identifier, TokenSpec(.extension, remapping: .identifier), default: .identifier)
361364
let roleTrailingComma = self.consume(if: .comma)
362365
let roleElement = RawLabeledExprSyntax(
@@ -373,7 +376,7 @@ extension Parser {
373376
trailingComma: roleTrailingComma,
374377
arena: self.arena
375378
)
376-
let additionalArgs = self.parseArgumentListElements(pattern: .none)
379+
let additionalArgs = self.parseArgumentListElements(pattern: .none, flavor: .attributeArgument)
377380
return [roleElement] + additionalArgs
378381
}
379382
}

Sources/SwiftParser/Expressions.swift

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ extension Parser {
7878
///
7979
/// We don't allow allow newlines here.
8080
case poundIfDirective
81+
82+
/// Parsing an attribute argument, which can contain declaration references
83+
/// like `subscript` or `deinit`.
84+
case attributeArgument
8185
}
8286

8387
enum PatternContext {
@@ -725,7 +729,7 @@ extension Parser {
725729

726730
// If there is an expr-call-suffix, parse it and form a call.
727731
if let lparen = self.consume(if: TokenSpec(.leftParen, allowAtStartOfLine: false)) {
728-
let args = self.parseArgumentListElements(pattern: pattern)
732+
let args = self.parseArgumentListElements(pattern: pattern, flavor: flavor.callArgumentFlavor)
729733
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)
730734

731735
// If we can parse trailing closures, do so.
@@ -1135,9 +1139,9 @@ extension Parser {
11351139
return RawExprSyntax(RawPatternExprSyntax(pattern: pattern, arena: self.arena))
11361140
}
11371141

1138-
return RawExprSyntax(self.parseIdentifierExpression())
1142+
return RawExprSyntax(self.parseIdentifierExpression(flavor: flavor))
11391143
case (.Self, _)?: // Self
1140-
return RawExprSyntax(self.parseIdentifierExpression())
1144+
return RawExprSyntax(self.parseIdentifierExpression(flavor: flavor))
11411145
case (.Any, _)?: // Any
11421146
let anyType = RawTypeSyntax(self.parseAnyType())
11431147
return RawExprSyntax(RawTypeExprSyntax(type: anyType, arena: self.arena))
@@ -1237,8 +1241,14 @@ extension Parser {
12371241

12381242
extension Parser {
12391243
/// Parse an identifier as an expression.
1240-
mutating func parseIdentifierExpression() -> RawExprSyntax {
1241-
let declName = self.parseDeclReferenceExpr(.compoundNames)
1244+
mutating func parseIdentifierExpression(flavor: ExprFlavor) -> RawExprSyntax {
1245+
var options: DeclNameOptions = .compoundNames
1246+
switch flavor {
1247+
case .basic, .poundIfDirective, .stmtCondition: break
1248+
case .attributeArgument: options.insert(.keywords)
1249+
}
1250+
1251+
let declName = self.parseDeclReferenceExpr(options)
12421252
guard self.withLookahead({ $0.canParseAsGenericArgumentList() }) else {
12431253
return RawExprSyntax(declName)
12441254
}
@@ -1689,7 +1699,7 @@ extension Parser {
16891699
name = nil
16901700
unexpectedBeforeEqual = nil
16911701
equal = nil
1692-
expression = RawExprSyntax(self.parseIdentifierExpression())
1702+
expression = RawExprSyntax(self.parseIdentifierExpression(flavor: .basic))
16931703
}
16941704

16951705
keepGoing = self.consume(if: .comma)
@@ -1833,7 +1843,7 @@ extension Parser {
18331843
///
18341844
/// This is currently the same as parsing a tuple expression. In the future,
18351845
/// this will be a dedicated argument list type.
1836-
mutating func parseArgumentListElements(pattern: PatternContext) -> [RawLabeledExprSyntax] {
1846+
mutating func parseArgumentListElements(pattern: PatternContext, flavor: ExprFlavor = .basic) -> [RawLabeledExprSyntax] {
18371847
if let remainingTokens = remainingTokensIfMaximumNestingLevelReached() {
18381848
return [
18391849
RawLabeledExprSyntax(
@@ -1878,7 +1888,7 @@ extension Parser {
18781888
if self.at(.binaryOperator) && self.peek(isAt: .comma, .rightParen, .rightSquare) {
18791889
expr = RawExprSyntax(self.parseDeclReferenceExpr(.operators))
18801890
} else {
1881-
expr = self.parseExpression(flavor: .basic, pattern: pattern)
1891+
expr = self.parseExpression(flavor: flavor, pattern: pattern)
18821892
}
18831893
keepGoing = self.consume(if: .comma)
18841894
result.append(
@@ -2548,3 +2558,14 @@ extension SyntaxKind {
25482558
}
25492559
}
25502560
}
2561+
2562+
private extension Parser.ExprFlavor {
2563+
/// The expression flavor used for the argument of a call that occurs
2564+
/// within a particularly-flavored expression.
2565+
var callArgumentFlavor: Parser.ExprFlavor {
2566+
switch self {
2567+
case .basic, .poundIfDirective, .stmtCondition: return .basic
2568+
case .attributeArgument: return .attributeArgument
2569+
}
2570+
}
2571+
}

Sources/SwiftParser/Names.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,13 @@ extension Parser {
6666
mutating func parseDeclReferenceExpr(_ flags: DeclNameOptions = []) -> RawDeclReferenceExprSyntax {
6767
// Consume the base name.
6868
let base: RawTokenSyntax
69-
if let identOrSelf = self.consume(if: .identifier, .keyword(.self), .keyword(.Self)) ?? self.consume(if: .keyword(.`init`)) ?? self.consume(
70-
if: .keyword(.`deinit`)
71-
) ?? self.consume(if: .keyword(.`subscript`)) {
69+
if let identOrSelf = self.consume(if: .identifier, .keyword(.self), .keyword(.Self)) ?? self.consume(if: .keyword(.`init`)) {
7270
base = identOrSelf
7371
} else if flags.contains(.operators), let (_, _) = self.at(anyIn: Operator.self) {
7472
base = self.consumeAnyToken(remapping: .binaryOperator)
73+
} else if flags.contains(.keywordsUsingSpecialNames),
74+
let special = self.consume(if: .keyword(.`deinit`), .keyword(.`subscript`)) {
75+
base = special
7576
} else if flags.contains(.keywords) && self.currentToken.isLexerClassifiedKeyword {
7677
base = self.consumeAnyToken(remapping: .identifier)
7778
} else {

Sources/SwiftParser/TokenPrecedence.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ enum TokenPrecedence: Comparable {
282282
.backDeployed,
283283
.derivative,
284284
.exclusivity,
285+
.freestanding,
285286
.inline,
286287
.objc,
287288
.transpose:

Tests/SwiftParserTest/AttributeTests.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ final class AttributeTests: ParserTestCase {
689689
)
690690
}
691691

692-
func testAttachedNames() {
692+
func testMacroRoleNames() {
693693
assertParse(
694694
"""
695695
@attached(member, names: named(deinit))
@@ -720,7 +720,42 @@ final class AttributeTests: ParserTestCase {
720720

721721
assertParse(
722722
"""
723-
@attached(member, names: named(subscript(a:b:)))
723+
@attached(declaration, names: named(subscript(a:b:)))
724+
macro m()
725+
"""
726+
)
727+
728+
assertParse(
729+
"""
730+
@freestanding(declaration, names: named(deinit))
731+
macro m()
732+
"""
733+
)
734+
735+
assertParse(
736+
"""
737+
@freestanding(declaration, names: named(init))
738+
macro m()
739+
"""
740+
)
741+
742+
assertParse(
743+
"""
744+
@freestanding(declaration, names: named(init(a:b:)))
745+
macro m()
746+
"""
747+
)
748+
749+
assertParse(
750+
"""
751+
@freestanding(member, names: named(subscript))
752+
macro m()
753+
"""
754+
)
755+
756+
assertParse(
757+
"""
758+
@freestanding(member, names: named(subscript(a:b:)))
724759
macro m()
725760
"""
726761
)

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,4 +2990,20 @@ final class StatementExpressionTests: ParserTestCase {
29902990
fixedSource: "foo(<#identifier#>: 1)"
29912991
)
29922992
}
2993+
2994+
func testSubscriptDeinitMembers() {
2995+
assertParse(
2996+
"""
2997+
.deinit
2998+
""",
2999+
substructure: DeclReferenceExprSyntax(baseName: .identifier("deinit"))
3000+
)
3001+
3002+
assertParse(
3003+
"""
3004+
.subscript
3005+
""",
3006+
substructure: DeclReferenceExprSyntax(baseName: .identifier("subscript"))
3007+
)
3008+
}
29933009
}

0 commit comments

Comments
 (0)