Skip to content

Commit 1c89f84

Browse files
committed
Add missing comma diagnostic if it's inside an array or dictionary
1 parent f974452 commit 1c89f84

File tree

3 files changed

+103
-18
lines changed

3 files changed

+103
-18
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1500,8 +1500,24 @@ extension Parser {
15001500
COLLECTION_LOOP: while self.hasProgressed(&collectionProgress) {
15011501
elementKind = self.parseCollectionElement(elementKind)
15021502

1503+
var hasElementKindError: Bool {
1504+
switch elementKind! {
1505+
case .dictionary(let key, _, let colon, let value):
1506+
return key.hasError || colon.hasError || value.hasError
1507+
case .array(let rawExprSyntax):
1508+
return rawExprSyntax.hasError
1509+
}
1510+
}
1511+
15031512
// Parse the ',' if exists.
1504-
let comma = self.consume(if: .comma)
1513+
let comma: Token?
1514+
if let token = self.consume(if: .comma) {
1515+
comma = token
1516+
} else if !self.atStartOfLine, !self.at(.rightSquare, .endOfFile), !hasElementKindError {
1517+
comma = missingToken(.comma)
1518+
} else {
1519+
comma = nil
1520+
}
15051521

15061522
switch elementKind! {
15071523
case .array(let el):

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,4 +2826,65 @@ final class StatementExpressionTests: ParserTestCase {
28262826
experimentalFeatures: .typedThrows
28272827
)
28282828
}
2829+
2830+
func testArrayExprWithNoCommas() {
2831+
assertParse("[() ()]")
2832+
2833+
assertParse(
2834+
"[1 1️⃣2]",
2835+
diagnostics: [
2836+
DiagnosticSpec(
2837+
message: "expected ',' in array element",
2838+
fixIts: ["insert ','"]
2839+
)
2840+
],
2841+
fixedSource: "[1, 2]"
2842+
)
2843+
2844+
assertParse(
2845+
#"["hello" 1️⃣"world"]"#,
2846+
diagnostics: [
2847+
DiagnosticSpec(
2848+
message: "expected ',' in array element",
2849+
fixIts: ["insert ','"]
2850+
)
2851+
],
2852+
fixedSource: #"["hello", "world"]"#
2853+
)
2854+
}
2855+
2856+
func testDictionaryExprWithNoCommas() {
2857+
assertParse(
2858+
"[1: () 1️⃣2: ()]",
2859+
diagnostics: [
2860+
DiagnosticSpec(
2861+
message: "expected ',' in dictionary element",
2862+
fixIts: ["insert ','"]
2863+
)
2864+
],
2865+
fixedSource: #"[1: (), 2: ()]"#
2866+
)
2867+
2868+
assertParse(
2869+
#"["foo": 1 1️⃣"bar": 2]"#,
2870+
diagnostics: [
2871+
DiagnosticSpec(
2872+
message: "expected ',' in dictionary element",
2873+
fixIts: ["insert ','"]
2874+
)
2875+
],
2876+
fixedSource: #"["foo": 1, "bar": 2]"#
2877+
)
2878+
2879+
assertParse(
2880+
#"[1: "hello" 1️⃣2: "world"]"#,
2881+
diagnostics: [
2882+
DiagnosticSpec(
2883+
message: "expected ',' in dictionary element",
2884+
fixIts: ["insert ','"]
2885+
)
2886+
],
2887+
fixedSource: #"[1: "hello", 2: "world"]"#
2888+
)
2889+
}
28292890
}

Tests/SwiftParserTest/translated/ObjectLiteralsTests.swift

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,41 +18,44 @@ final class ObjectLiteralsTests: ParserTestCase {
1818
func testObjectLiterals1a() {
1919
assertParse(
2020
"""
21-
let _ = [#Color(colorLiteralRed: red, green: green, blue: blue, alpha: alpha)#1️⃣]
21+
let _ = [#Color(colorLiteralRed: red, green: green, blue: blue, alpha: alpha)1️⃣#2️⃣]
2222
""",
2323
diagnostics: [
24-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"])
24+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected ',' in array element", fixIts: ["insert ','"]),
25+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"]),
2526
],
2627
fixedSource: """
27-
let _ = [#Color(colorLiteralRed: red, green: green, blue: blue, alpha: alpha)#<#identifier#>]
28+
let _ = [#Color(colorLiteralRed: red, green: green, blue: blue, alpha: alpha), #<#identifier#>]
2829
"""
2930
)
3031
}
3132

3233
func testObjectLiterals1b() {
3334
assertParse(
3435
"""
35-
let _ = [#Image(imageLiteral: localResourceNameAsString)#1️⃣]
36+
let _ = [#Image(imageLiteral: localResourceNameAsString)1️⃣#2️⃣]
3637
""",
3738
diagnostics: [
38-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"])
39+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected ',' in array element", fixIts: ["insert ','"]),
40+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"]),
3941
],
4042
fixedSource: """
41-
let _ = [#Image(imageLiteral: localResourceNameAsString)#<#identifier#>]
43+
let _ = [#Image(imageLiteral: localResourceNameAsString), #<#identifier#>]
4244
"""
4345
)
4446
}
4547

4648
func testObjectLiterals1c() {
4749
assertParse(
4850
"""
49-
let _ = [#FileReference(fileReferenceLiteral: localResourceNameAsString)#1️⃣]
51+
let _ = [#FileReference(fileReferenceLiteral: localResourceNameAsString)1️⃣#2️⃣]
5052
""",
5153
diagnostics: [
52-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"])
54+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected ',' in array element", fixIts: ["insert ','"]),
55+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"]),
5356
],
5457
fixedSource: """
55-
let _ = [#FileReference(fileReferenceLiteral: localResourceNameAsString)#<#identifier#>]
58+
let _ = [#FileReference(fileReferenceLiteral: localResourceNameAsString), #<#identifier#>]
5659
"""
5760
)
5861
}
@@ -123,11 +126,10 @@ final class ObjectLiteralsTests: ParserTestCase {
123126
func testObjectLiterals5() {
124127
assertParse(
125128
"""
126-
let _ = ℹ️[#Color(_: 1, green: 1, 2)2️⃣
129+
let _ = ℹ️[#Color(_: 1, green: 1, 2)1️⃣
127130
""",
128131
diagnostics: [
129132
DiagnosticSpec(
130-
locationMarker: "2️⃣",
131133
message: "expected ']' to end array",
132134
notes: [NoteSpec(message: "to match this opening '['")],
133135
fixIts: ["insert ']'"]
@@ -142,37 +144,43 @@ final class ObjectLiteralsTests: ParserTestCase {
142144
func testObjectLiterals6() {
143145
assertParse(
144146
"""
145-
let _ = ℹ️[1️⃣#Color(red: 1, green: 1, blue: 1)#2️⃣3️⃣
147+
let _ = ℹ️[#Color(red: 1, green: 1, blue: 1)1️⃣#2️⃣
146148
""",
147149
diagnostics: [
150+
DiagnosticSpec(
151+
locationMarker: "1️⃣",
152+
message: "expected ',' in array element",
153+
fixIts: ["insert ','"]
154+
),
148155
DiagnosticSpec(
149156
locationMarker: "2️⃣",
150157
message: "expected identifier in macro expansion",
151158
fixIts: ["insert identifier"]
152159
),
153160
DiagnosticSpec(
154-
locationMarker: "3️⃣",
161+
locationMarker: "2️⃣",
155162
message: "expected ']' to end array",
156163
notes: [NoteSpec(message: "to match this opening '['")],
157164
fixIts: ["insert ']'"]
158165
),
159166
],
160167
fixedSource: """
161-
let _ = [#Color(red: 1, green: 1, blue: 1)#<#identifier#>]
168+
let _ = [#Color(red: 1, green: 1, blue: 1), #<#identifier#>]
162169
"""
163170
)
164171
}
165172

166173
func testObjectLiterals7() {
167174
assertParse(
168175
"""
169-
let _ = [#Color(withRed: 1, green: 1, whatever: 2)#1️⃣]
176+
let _ = [#Color(withRed: 1, green: 1, whatever: 2)1️⃣#2️⃣]
170177
""",
171178
diagnostics: [
172-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"])
179+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected ',' in array element", fixIts: ["insert ','"]),
180+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected identifier in macro expansion", fixIts: ["insert identifier"]),
173181
],
174182
fixedSource: """
175-
let _ = [#Color(withRed: 1, green: 1, whatever: 2)#<#identifier#>]
183+
let _ = [#Color(withRed: 1, green: 1, whatever: 2), #<#identifier#>]
176184
"""
177185
)
178186
}

0 commit comments

Comments
 (0)