Skip to content

Commit bd5f7d6

Browse files
committed
Add correct diagnostic for missing parentheses
1 parent e340af4 commit bd5f7d6

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,7 +1746,9 @@ extension Parser {
17461746
var effectSpecifiers: RawTypeEffectSpecifiersSyntax?
17471747
var returnClause: RawReturnClauseSyntax? = nil
17481748
if !self.at(.keyword(.in)) {
1749-
if self.at(.leftParen) {
1749+
// If current token is not a leftParen but a colon it might be an failure
1750+
// as we need a left and right paren.
1751+
if self.at(.leftParen) || self.peek(isAt: .colon) {
17501752
// Parse the closure arguments.
17511753
let params = self.parseParameterClause(RawClosureParameterClauseSyntax.self) { parser in
17521754
parser.parseClosureParameter()
@@ -2427,7 +2429,7 @@ extension Parser.Lookahead {
24272429
}
24282430

24292431
// Parse pattern-tuple func-signature-result? 'in'.
2430-
if lookahead.consume(if: .leftParen) != nil { // Consume the ')'.
2432+
if lookahead.consume(if: .leftParen) != nil { // Consume the '('.
24312433

24322434
// While we don't have '->' or ')', eat balanced tokens.
24332435
var skipProgress = LoopProgressCondition()
@@ -2453,8 +2455,18 @@ extension Parser.Lookahead {
24532455
// Parse identifier (',' identifier)*
24542456
lookahead.consumeAnyToken()
24552457

2458+
func consumeTypesIfAvailable() -> Bool {
2459+
// If identifiers have types, we need to have parens around.
2460+
// If they are missing but colon an type is there, it might be an error.
2461+
if lookahead.consume(if: .colon) != nil {
2462+
return lookahead.canParseType() && lookahead.consume(if: .comma) != nil
2463+
} else {
2464+
return lookahead.consume(if: .comma) != nil
2465+
}
2466+
}
2467+
24562468
var parametersProgress = LoopProgressCondition()
2457-
while lookahead.consume(if: .comma) != nil && lookahead.hasProgressed(&parametersProgress) {
2469+
while consumeTypesIfAvailable() && lookahead.hasProgressed(&parametersProgress) {
24582470
if lookahead.at(.identifier) || lookahead.at(.wildcard) {
24592471
lookahead.consumeAnyToken()
24602472
continue
@@ -2463,6 +2475,10 @@ extension Parser.Lookahead {
24632475
return false
24642476
}
24652477

2478+
if lookahead.at(.rightParen) {
2479+
lookahead.consumeAnyToken()
2480+
}
2481+
24662482
lookahead.consumeEffectsSpecifiers()
24672483

24682484
// Parse the func-signature-result, if present.

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2689,4 +2689,49 @@ final class StatementExpressionTests: XCTestCase {
26892689
26902690
assertParse("_ = { (@Wrapper x) in }")
26912691
}
2692+
2693+
func testClosureWithMissingParentheses() {
2694+
assertParse(
2695+
"""
2696+
_ = { 1️⃣a: Int, b: Int 2️⃣in
2697+
}
2698+
""",
2699+
diagnostics: [
2700+
DiagnosticSpec(
2701+
locationMarker: "1️⃣",
2702+
message: "expected '(' to start parameter clause",
2703+
fixIts: ["insert '('"]
2704+
),
2705+
DiagnosticSpec(
2706+
locationMarker: "2️⃣",
2707+
message: "expected ')' to end parameter clause",
2708+
fixIts: ["insert ')'"]
2709+
),
2710+
],
2711+
fixedSource: """
2712+
_ = { (a: Int, b: Int) in
2713+
}
2714+
"""
2715+
)
2716+
}
2717+
2718+
func testClosureWithMissingLeftParenthese() {
2719+
assertParse(
2720+
"""
2721+
_ = { 1️⃣a: Int, b: Int) in
2722+
}
2723+
""",
2724+
diagnostics: [
2725+
DiagnosticSpec(
2726+
locationMarker: "1️⃣",
2727+
message: "expected '(' to start parameter clause",
2728+
fixIts: ["insert '('"]
2729+
)
2730+
],
2731+
fixedSource: """
2732+
_ = { (a: Int, b: Int) in
2733+
}
2734+
"""
2735+
)
2736+
}
26922737
}

0 commit comments

Comments
 (0)