Skip to content

Commit 1db511e

Browse files
Expand trailing comma implementation
1 parent 371ea75 commit 1db511e

File tree

8 files changed

+169
-14
lines changed

8 files changed

+169
-14
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,10 @@ extension Parser {
376376
}
377377
case nil:
378378
return parseAttribute(argumentMode: .customAttribute) { parser in
379-
let arguments = parser.parseArgumentListElements(pattern: .none, allowTrailingComma: false)
379+
let arguments = parser.parseArgumentListElements(
380+
pattern: .none,
381+
allowTrailingComma: parser.experimentalFeatures.contains(.trailingComma)
382+
)
380383
return .argumentList(RawLabeledExprListSyntax(elements: arguments, arena: parser.arena))
381384
}
382385
}

Sources/SwiftParser/Availability.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,17 @@ extension Parser {
4747
arena: self.arena
4848
)
4949
)
50-
} while keepGoing != nil && self.hasProgressed(&availabilityArgumentProgress)
50+
} while keepGoing != nil && !self.atAvailabilitySpecListTerminator()
51+
&& self.hasProgressed(&availabilityArgumentProgress)
5152
}
5253

5354
return RawAvailabilityArgumentListSyntax(elements: elements, arena: self.arena)
5455
}
5556

57+
mutating func atAvailabilitySpecListTerminator() -> Bool {
58+
return self.experimentalFeatures.contains(.trailingComma) && self.at(.rightParen)
59+
}
60+
5661
enum AvailabilityArgumentKind: TokenSpecSet {
5762
case message
5863
case renamed

Sources/SwiftParser/Declarations.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ extension Parser {
492492
arena: self.arena
493493
)
494494
)
495-
} while keepGoing != nil && self.hasProgressed(&loopProgress)
495+
} while keepGoing != nil && !atGenericParametersListTerminator() && self.hasProgressed(&loopProgress)
496496
}
497497

498498
let whereClause: RawGenericWhereClauseSyntax?
@@ -519,6 +519,10 @@ extension Parser {
519519
)
520520
}
521521

522+
mutating func atGenericParametersListTerminator() -> Bool {
523+
return self.experimentalFeatures.contains(.trailingComma) && self.at(prefix: ">")
524+
}
525+
522526
mutating func parseGenericWhereClause() -> RawGenericWhereClauseSyntax {
523527
let (unexpectedBeforeWhereKeyword, whereKeyword) = self.expect(.keyword(.where))
524528

@@ -700,7 +704,7 @@ extension Parser {
700704
arena: self.arena
701705
)
702706
)
703-
} while keepGoing != nil && self.hasProgressed(&loopProgress)
707+
} while keepGoing != nil && !self.atWhereClauseListTerminator() && self.hasProgressed(&loopProgress)
704708
}
705709

706710
return RawGenericWhereClauseSyntax(
@@ -710,6 +714,10 @@ extension Parser {
710714
arena: self.arena
711715
)
712716
}
717+
718+
mutating func atWhereClauseListTerminator() -> Bool {
719+
return self.at(.leftBrace)
720+
}
713721
}
714722

715723
extension Parser {
@@ -2029,7 +2037,10 @@ extension Parser {
20292037
let unexpectedBeforeRightParen: RawUnexpectedNodesSyntax?
20302038
let rightParen: RawTokenSyntax?
20312039
if leftParen != nil {
2032-
args = parseArgumentListElements(pattern: .none, allowTrailingComma: false)
2040+
args = parseArgumentListElements(
2041+
pattern: .none,
2042+
allowTrailingComma: self.experimentalFeatures.contains(.trailingComma)
2043+
)
20332044
(unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen)
20342045
} else {
20352046
args = []

Sources/SwiftParser/Expressions.swift

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,10 @@ extension Parser {
786786
if self.at(.rightSquare) {
787787
args = []
788788
} else {
789-
args = self.parseArgumentListElements(pattern: pattern, allowTrailingComma: false)
789+
args = self.parseArgumentListElements(
790+
pattern: pattern,
791+
allowTrailingComma: self.experimentalFeatures.contains(.trailingComma)
792+
)
790793
}
791794
let (unexpectedBeforeRSquare, rsquare) = self.expect(.rightSquare)
792795

@@ -1019,7 +1022,10 @@ extension Parser {
10191022
if self.at(.rightSquare) {
10201023
args = []
10211024
} else {
1022-
args = self.parseArgumentListElements(pattern: pattern, allowTrailingComma: false)
1025+
args = self.parseArgumentListElements(
1026+
pattern: pattern,
1027+
allowTrailingComma: self.experimentalFeatures.contains(.trailingComma)
1028+
)
10231029
}
10241030
let (unexpectedBeforeRSquare, rsquare) = self.expect(.rightSquare)
10251031

@@ -1314,7 +1320,10 @@ extension Parser {
13141320
let unexpectedBeforeRightParen: RawUnexpectedNodesSyntax?
13151321
let rightParen: RawTokenSyntax?
13161322
if leftParen != nil {
1317-
args = parseArgumentListElements(pattern: pattern, allowTrailingComma: false)
1323+
args = parseArgumentListElements(
1324+
pattern: pattern,
1325+
allowTrailingComma: self.experimentalFeatures.contains(.trailingComma)
1326+
)
13181327
(unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen)
13191328
} else {
13201329
args = []
@@ -1740,7 +1749,7 @@ extension Parser {
17401749
arena: self.arena
17411750
)
17421751
)
1743-
} while keepGoing != nil && self.hasProgressed(&loopProgress)
1752+
} while keepGoing != nil && !self.atCaptureListTerminator() && self.hasProgressed(&loopProgress)
17441753
}
17451754
// We were promised a right square bracket, so we're going to get it.
17461755
var unexpectedNodes = [RawSyntax]()
@@ -1827,6 +1836,10 @@ extension Parser {
18271836
)
18281837
}
18291838

1839+
mutating func atCaptureListTerminator() -> Bool {
1840+
return self.at(.rightSquare)
1841+
}
1842+
18301843
mutating func parseClosureCaptureSpecifiers() -> RawClosureCaptureSpecifierSyntax? {
18311844
// Check for the strength specifier: "weak", "unowned", or
18321845
// "unowned(safe/unsafe)".
@@ -1938,7 +1951,7 @@ extension Parser {
19381951
}
19391952

19401953
mutating func atArgumentListTerminator(_ allowTrailingComma: Bool) -> Bool {
1941-
return allowTrailingComma && self.at(.rightParen)
1954+
return allowTrailingComma && (self.at(.rightParen) || self.at(.rightSquare))
19421955
}
19431956
}
19441957

@@ -2383,7 +2396,7 @@ extension Parser {
23832396
} else {
23842397
unexpectedPrePatternCase = nil
23852398
}
2386-
} while keepGoing != nil && self.hasProgressed(&loopProgress)
2399+
} while keepGoing != nil && !self.atSwitchCaseListTerminator() && self.hasProgressed(&loopProgress)
23872400
}
23882401
let (unexpectedBeforeColon, colon) = self.expect(.colon)
23892402
return RawSwitchCaseLabelSyntax(
@@ -2396,6 +2409,10 @@ extension Parser {
23962409
)
23972410
}
23982411

2412+
mutating func atSwitchCaseListTerminator() -> Bool {
2413+
return self.experimentalFeatures.contains(.trailingComma) && self.at(.colon)
2414+
}
2415+
23992416
/// Parse a switch case with a 'default' label.
24002417
mutating func parseSwitchDefaultLabel(
24012418
_ handle: RecoveryConsumptionHandle

Sources/SwiftParser/Nominals.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ extension Parser {
315315
arena: self.arena
316316
)
317317
)
318-
} while keepGoing != nil && self.hasProgressed(&loopProgress)
318+
} while keepGoing != nil && !self.atInheritanceListTerminator() && self.hasProgressed(&loopProgress)
319319
}
320320

321321
let unexpectedAfterInheritedTypeCollection: RawUnexpectedNodesSyntax?
@@ -339,6 +339,10 @@ extension Parser {
339339
)
340340
}
341341

342+
mutating func atInheritanceListTerminator() -> Bool {
343+
return self.experimentalFeatures.contains(.trailingComma) && (self.at(.leftBrace) || self.at(.keyword(.where)))
344+
}
345+
342346
mutating func parsePrimaryAssociatedTypes() -> RawPrimaryAssociatedTypeClauseSyntax {
343347
let langle = self.consumePrefix("<", as: .leftAngle)
344348
var associatedTypes = [RawPrimaryAssociatedTypeSyntax]()

Sources/SwiftParser/StringLiterals.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,10 @@ extension Parser {
551551
)
552552
let leftParen = self.expectWithoutRecoveryOrLeadingTrivia(.leftParen)
553553
let expressions = RawLabeledExprListSyntax(
554-
elements: self.parseArgumentListElements(pattern: .none, allowTrailingComma: false),
554+
elements: self.parseArgumentListElements(
555+
pattern: .none,
556+
allowTrailingComma: self.experimentalFeatures.contains(.trailingComma)
557+
),
555558
arena: self.arena
556559
)
557560

Sources/SwiftParser/Types.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,10 @@ extension Parser {
10251025
}
10261026
case nil: // Custom attribute
10271027
return parseAttribute(argumentMode: .customAttribute) { parser in
1028-
let arguments = parser.parseArgumentListElements(pattern: .none, allowTrailingComma: false)
1028+
let arguments = parser.parseArgumentListElements(
1029+
pattern: .none,
1030+
allowTrailingComma: parser.experimentalFeatures.contains(.trailingComma)
1031+
)
10291032
return .argumentList(RawLabeledExprListSyntax(elements: arguments, arena: parser.arena))
10301033
}
10311034

Tests/SwiftParserTest/TrailingCommaTests.swift

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,113 @@ final class TrailingCommaTests: ParserTestCase {
231231
func testWhileConditions() {
232232
assertParse("while true, { print(0) }")
233233
}
234+
235+
func testSubscriptArguments() {
236+
assertParse("d[1, 2,]")
237+
}
238+
239+
func testStringLiteralInterpolation() {
240+
assertParse(#""\(1, 2,)""#)
241+
}
242+
243+
func testKeyPathExpressionArguments() {
244+
assertParse(
245+
"""
246+
\\Foo.bar[0,]
247+
"""
248+
)
249+
}
250+
251+
func testCustomAttributes() {
252+
assertParse("@Foo(a, b, c,) struct S { }")
253+
254+
assertParse("f(_: @foo(1, 2,) Int)")
255+
}
256+
257+
func testTypeAttribute() {
258+
assertParse("f(_: @foo(1, 2,) Int)")
259+
}
260+
261+
func testMacroExpansionExpressionArguments() {
262+
assertParse(
263+
"""
264+
#foo(1, 2,)
265+
"""
266+
)
267+
}
268+
269+
func testMacroExpansionDeclarationArguments() {
270+
assertParse(
271+
"""
272+
struct S {
273+
#foo(1, 2,)
274+
}
275+
"""
276+
)
277+
}
278+
279+
func testGenericParameters() {
280+
assertParse(
281+
"""
282+
struct S<T1, T2,> { }
283+
"""
284+
)
285+
}
286+
287+
func testSwitchCaseLabel() {
288+
assertParse(
289+
"""
290+
switch number {
291+
case 1, 2,:
292+
break
293+
default:
294+
break
295+
}
296+
"""
297+
)
298+
}
299+
300+
func testClosureCaptureList() {
301+
assertParse(
302+
"""
303+
{ [obj1, obj2,] in }
304+
"""
305+
)
306+
}
307+
308+
func testInheritance() {
309+
assertParse(
310+
"""
311+
struct T: P1, P2, { }
312+
"""
313+
)
314+
315+
assertParse(
316+
"""
317+
struct T: P1, P2, where P1: Equatable, P2: Equatable { }
318+
"""
319+
)
320+
}
321+
322+
func testGenericWhereClause() {
323+
assertParse(
324+
"""
325+
struct T: P1, P2, where P1: Equatable, P2: Equatable, { }
326+
"""
327+
)
328+
}
329+
330+
func testAvailabilityArgumentSpecList() {
331+
assertParse(
332+
"""
333+
if #available(iOS 15, watchOS 9, *,) { }
334+
"""
335+
)
336+
337+
assertParse(
338+
"""
339+
if #unavailable(iOS 15, watchOS 9,) { }
340+
"""
341+
)
342+
}
234343
}

0 commit comments

Comments
 (0)