Skip to content

Commit 7bd84c0

Browse files
committed
Skip Declaration Attribute Parsing for Enum Case Parameters
Introduce a subject parameter so that we can correctly skip parsing declaration attributes for enum case parameter lists.
1 parent 0380bd5 commit 7bd84c0

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-12
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ extension Parser {
130130
case (.enumKeyword, _)?:
131131
return RawDeclSyntax(self.parseEnumDeclaration(attrs))
132132
case (.caseKeyword, _)?:
133-
return RawDeclSyntax(self.parseDeclEnumCase(attrs))
133+
return RawDeclSyntax(self.parseEnumCaseDeclaration(attrs))
134134
case (.structKeyword, _)?:
135135
return RawDeclSyntax(self.parseStructDeclaration(attrs))
136136
case (.protocolKeyword, _)?:
@@ -715,7 +715,7 @@ extension Parser {
715715
/// raw-value-assignment → = raw-value-literal
716716
/// raw-value-literal → numeric-literal | static-string-literal | boolean-literal
717717
@_spi(RawSyntax)
718-
public mutating func parseDeclEnumCase(_ attrs: DeclAttributes) -> RawEnumCaseDeclSyntax {
718+
public mutating func parseEnumCaseDeclaration(_ attrs: DeclAttributes) -> RawEnumCaseDeclSyntax {
719719
let (unexpectedBeforeCaseKeyword, caseKeyword) = self.expect(.caseKeyword)
720720
var elements = [RawEnumCaseElementSyntax]()
721721
do {
@@ -726,7 +726,7 @@ extension Parser {
726726

727727
let associatedValue: RawParameterClauseSyntax?
728728
if self.at(.leftParen, where: { !$0.isAtStartOfLine }) {
729-
associatedValue = self.parseParameterClause()
729+
associatedValue = self.parseParameterClause(for: .enumCase)
730730
} else {
731731
associatedValue = nil
732732
}
@@ -1142,27 +1142,52 @@ extension Parser {
11421142

11431143
extension Parser {
11441144
@_spi(RawSyntax)
1145-
public mutating func parseParameterClause(isClosure: Bool = false) -> RawParameterClauseSyntax {
1145+
public enum ParameterSubject {
1146+
case closure
1147+
case enumCase
1148+
case functionParameters
1149+
case indices
1150+
1151+
var isClosure: Bool {
1152+
switch self {
1153+
case .closure: return true
1154+
case .enumCase: return false
1155+
case .functionParameters: return false
1156+
case .indices: return false
1157+
}
1158+
}
1159+
}
1160+
1161+
@_spi(RawSyntax)
1162+
public mutating func parseParameterClause(for subject: ParameterSubject) -> RawParameterClauseSyntax {
11461163
let (unexpectedBeforeLParen, lparen) = self.expect(.leftParen)
11471164
var elements = [RawFunctionParameterSyntax]()
1148-
// If we are missing the left parenthesis and the next token doesn't appear to be an argument label, don't parse any parameters.
1165+
// If we are missing the left parenthesis and the next token doesn't appear
1166+
// to be an argument label, don't parse any parameters.
11491167
let shouldSkipParameterParsing = lparen.isMissing && (!currentToken.canBeArgumentLabel || currentToken.isKeyword)
11501168
if !shouldSkipParameterParsing {
11511169
var keepGoing = true
11521170
var loopProgress = LoopProgressCondition()
11531171
while !self.at(any: [.eof, .rightParen])
11541172
&& keepGoing
11551173
&& loopProgress.evaluate(currentToken) {
1156-
// Attributes.
1157-
let attrs = self.parseAttributeList()
1174+
// Parse any declaration attributes. The exception here is enum cases
1175+
// which only allow types, so we do not consume attributes to allow the
1176+
// type attribute grammar a chance to examine them.
1177+
let attrs: RawAttributeListSyntax?
1178+
if case .enumCase = subject {
1179+
attrs = nil
1180+
} else {
1181+
attrs = self.parseAttributeList()
1182+
}
11581183

11591184
let firstName: RawTokenSyntax?
11601185
let secondName: RawTokenSyntax?
11611186
let unexpectedBeforeColon: RawUnexpectedNodesSyntax?
11621187
let colon: RawTokenSyntax?
11631188
let shouldParseType: Bool
11641189

1165-
if self.lookahead().startsParameterName(isClosure) {
1190+
if self.lookahead().startsParameterName(subject.isClosure) {
11661191
if self.currentToken.canBeArgumentLabel {
11671192
firstName = self.parseArgumentLabel()
11681193
} else {
@@ -1174,7 +1199,7 @@ extension Parser {
11741199
} else {
11751200
secondName = nil
11761201
}
1177-
if isClosure {
1202+
if subject.isClosure {
11781203
unexpectedBeforeColon = nil
11791204
colon = self.consume(if: .colon)
11801205
shouldParseType = (colon != nil)
@@ -1314,7 +1339,7 @@ extension Parser {
13141339

13151340
@_spi(RawSyntax)
13161341
public mutating func parseFunctionSignature() -> RawFunctionSignatureSyntax {
1317-
let input = self.parseParameterClause()
1342+
let input = self.parseParameterClause(for: .functionParameters)
13181343

13191344
let async = self.consumeIfContextualKeyword("async")
13201345

@@ -1361,7 +1386,7 @@ extension Parser {
13611386
genericParameterClause = nil
13621387
}
13631388

1364-
let indices = self.parseParameterClause()
1389+
let indices = self.parseParameterClause(for: .indices)
13651390

13661391
let result: RawReturnClauseSyntax
13671392
if self.at(.arrow) {

Sources/SwiftParser/Expressions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,7 @@ extension Parser {
18281828
if !self.at(.inKeyword) {
18291829
if self.at(.leftParen) {
18301830
// Parse the closure arguments.
1831-
input = RawSyntax(self.parseParameterClause(isClosure: true))
1831+
input = RawSyntax(self.parseParameterClause(for: .closure))
18321832
} else {
18331833
var params = [RawClosureParamSyntax]()
18341834
var loopProgress = LoopProgressCondition()

Tests/SwiftParserTest/Declarations.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,14 @@ final class DeclarationTests: XCTestCase {
381381
}
382382

383383
func testEnumParsing() {
384+
AssertParse(
385+
"""
386+
enum Foo {
387+
@preconcurrency case custom(@Sendable () throws -> Void)
388+
}
389+
"""
390+
)
391+
384392
AssertParse(
385393
"""
386394
enum Content {

0 commit comments

Comments
 (0)