Skip to content

Commit 3d54574

Browse files
committed
improve diagnostic for unnamed closure parameters
- instead of treating as unrecognized, try to parse the remaining tokens as a type even if the preceding colon is missing
1 parent 70e3741 commit 3d54574

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

Sources/SwiftParser/Parameters.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,23 @@ extension Parser {
160160
let misplacedSpecifiers = parseMisplacedSpecifiers()
161161

162162
let names = self.parseParameterNames()
163-
let colon = self.consume(if: .colon)
163+
var colon = self.consume(if: .colon)
164+
// try to parse the type regardless of the presence of the preceding colon
165+
// because the parameter may be unnamed
166+
// e.g. [X] or (:[X])
167+
let canParseType = withLookahead { $0.canParseType() }
164168
let type: RawTypeSyntax?
165-
if colon != nil {
169+
if canParseType {
166170
type = self.parseType(misplacedSpecifiers: misplacedSpecifiers)
171+
if colon == nil {
172+
// mark the preceding colon as missing if the parameter is unnamed
173+
// e.g. [X]
174+
colon = missingToken(.colon)
175+
}
176+
} else if colon != nil {
177+
// mark the type as missing if the preceding colon is present
178+
// e.g. (_:)
179+
type = RawTypeSyntax(RawMissingTypeSyntax(arena: self.arena))
167180
} else {
168181
type = nil
169182
}

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2875,6 +2875,56 @@ final class StatementExpressionTests: ParserTestCase {
28752875
)
28762876
}
28772877
2878+
func testClosureWithMalformedParameters() {
2879+
assertParse(
2880+
"""
2881+
test { (1️⃣[X]) in }
2882+
""",
2883+
diagnostics: [
2884+
DiagnosticSpec(message: "expected identifier and ':' in parameter", fixIts: ["insert identifier and ':'"])
2885+
],
2886+
fixedSource: """
2887+
test { (<#identifier#>: [X]) in }
2888+
"""
2889+
)
2890+
2891+
assertParse(
2892+
"""
2893+
test { (1️⃣: [X]) in }
2894+
""",
2895+
diagnostics: [
2896+
DiagnosticSpec(message: "expected identifier in parameter", fixIts: ["insert identifier"])
2897+
],
2898+
fixedSource: """
2899+
test { (<#identifier#>: [X]) in }
2900+
"""
2901+
)
2902+
2903+
assertParse(
2904+
"""
2905+
test { (foo1️⃣ @bar baz) in }
2906+
""",
2907+
diagnostics: [
2908+
DiagnosticSpec(message: "expected ':' in parameter", fixIts: ["insert ':'"])
2909+
],
2910+
fixedSource: """
2911+
test { (foo: @bar baz) in }
2912+
"""
2913+
)
2914+
2915+
assertParse(
2916+
"""
2917+
test { (x: 1️⃣) in }
2918+
""",
2919+
diagnostics: [
2920+
DiagnosticSpec(message: "expected type in parameter", fixIts: ["insert type"])
2921+
],
2922+
fixedSource: """
2923+
test { (x: <#type#>) in }
2924+
"""
2925+
)
2926+
}
2927+
28782928
func testTypedThrowsDisambiguation() {
28792929
assertParse(
28802930
"[() throws(MyError) 1️⃣async -> Void]()",

0 commit comments

Comments
 (0)