Skip to content

Commit c179741

Browse files
committed
Add missing switch expression error
1 parent 734c911 commit c179741

File tree

4 files changed

+57
-32
lines changed

4 files changed

+57
-32
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2222,7 +2222,15 @@ extension Parser {
22222222
) -> RawSwitchExprSyntax {
22232223
let (unexpectedBeforeSwitchKeyword, switchKeyword) = self.eat(switchHandle)
22242224

2225-
let subject = self.parseExpression(.basic)
2225+
// If there is no expression, like `switch { default: return false }` then left brace would parsed as
2226+
// a `RawClosureExprSyntax` in the condition, which is most likely not what the user meant.
2227+
let subject: RawExprSyntax
2228+
if self.at(.leftBrace) {
2229+
subject = RawExprSyntax(RawMissingExprSyntax(arena: self.arena))
2230+
} else {
2231+
subject = self.parseExpression(.basic)
2232+
}
2233+
22262234
let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace)
22272235

22282236
let cases = self.parseSwitchCases(allowStandaloneStmtRecovery: !lbrace.isMissing)

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,18 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
10431043
return .visitChildren
10441044
}
10451045

1046+
public override func visit(_ node: SwitchExprSyntax) -> SyntaxVisitorContinueKind {
1047+
if shouldSkip(node) {
1048+
return .skipChildren
1049+
}
1050+
1051+
if node.expression.is(MissingExprSyntax.self) && !node.cases.isEmpty {
1052+
addDiagnostic(node.expression, .missingExpressionInSwitchStatement, handledNodes: [node.expression.id])
1053+
}
1054+
1055+
return .visitChildren
1056+
}
1057+
10461058
public override func visit(_ node: SwitchCaseSyntax) -> SyntaxVisitorContinueKind {
10471059
if shouldSkip(node) {
10481060
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ extension DiagnosticMessage where Self == StaticParserError {
164164
public static var missingConformanceRequirement: Self {
165165
.init("expected ':' or '==' to indicate a conformance or same-type requirement")
166166
}
167+
public static var missingExpressionInSwitchStatement: Self {
168+
.init("expected expression in 'switch' statement")
169+
}
167170
public static var misspelledAsync: Self {
168171
.init("expected async specifier; did you mean 'async'?")
169172
}

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -613,90 +613,92 @@ final class RecoveryTests: XCTestCase {
613613
switch1️⃣
614614
""",
615615
diagnostics: [
616-
DiagnosticSpec(message: "expected expression and '{}' to end 'switch' statement")
616+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression and '{}' to end 'switch' statement")
617617
]
618618
)
619619
}
620620

621621
func testRecovery46() {
622622
AssertParse(
623623
"""
624-
switch {
625-
}1️⃣
624+
switch 1️⃣{
625+
}
626626
""",
627627
diagnostics: [
628-
// TODO: Old parser expected error on line 1: expected expression in 'switch' statement
629-
// TODO: Old parser expected error on line 1: 'switch' statement body must have at least one 'case' or 'default' block
630-
DiagnosticSpec(message: "expected '{}' in 'switch' statement")
628+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in 'switch' statement")
631629
]
632630
)
633631
}
634632

635633
func testRecovery47() {
636634
AssertParse(
637635
"""
638-
switch
636+
switch 1️⃣
639637
{
640-
}1️⃣
638+
}
641639
""",
642640
diagnostics: [
643-
// TODO: Old parser expected error on line 1: expected expression in 'switch' statement
644-
// TODO: Old parser expected error on line 1: 'switch' statement body must have at least one 'case' or 'default' block
645-
DiagnosticSpec(message: "expected '{}' in 'switch' statement")
641+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in 'switch' statement")
646642
]
647643
)
648644
}
649645

650646
func testRecovery48() {
651647
AssertParse(
652648
"""
653-
switch {
654-
1️⃣case _: return
655-
}2️⃣
649+
switch 1️⃣{
650+
2️⃣case _: return
651+
}
656652
""",
657653
diagnostics: [
658-
// TODO: Old parser expected error on line 1: expected expression in 'switch' statement
659-
DiagnosticSpec(locationMarker: "1️⃣", message: "'case' can only appear inside a 'switch' statement or 'enum' declaration"),
660-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected '{}' in 'switch' statement"),
654+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in 'switch' statement")
661655
]
662656
)
663657
}
664658

665659
func testRecovery49() {
666660
AssertParse(
667661
"""
668-
switch {
669-
1️⃣case Int: return
670-
2️⃣case _: return
671-
}3️⃣
662+
switch 1️⃣{
663+
case Int: return
664+
case _: return
665+
}
672666
""",
673667
diagnostics: [
674-
// TODO: Old parser expected error on line 1: expected expression in 'switch' statement
668+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in 'switch' statement")
675669
// TODO: Old parser expected error on line 2: 'is' keyword required to pattern match against type name, Fix-It replacements: 10 - 10 = 'is '
676-
DiagnosticSpec(locationMarker: "1️⃣", message: "'case' can only appear inside a 'switch' statement or 'enum' declaration"),
677-
DiagnosticSpec(locationMarker: "2️⃣", message: "'case' can only appear inside a 'switch' statement or 'enum' declaration"),
678-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected '{}' in 'switch' statement"),
679670
]
680671
)
681672
}
682673

683674
func testRecovery50() {
684675
AssertParse(
685676
"""
686-
switch { 42 } {
687-
case _: return
677+
switch 1️⃣{ 2️⃣42 } {
678+
3️⃣case _: return
688679
}
689-
"""
680+
""",
681+
diagnostics: [
682+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in 'switch' statement"),
683+
DiagnosticSpec(locationMarker: "2️⃣", message: "all statements inside a switch must be covered by a 'case' or 'default' label"),
684+
DiagnosticSpec(locationMarker: "3️⃣", message: "'case' can only appear inside a 'switch' statement or 'enum' declaration"),
685+
]
690686
)
691687
}
692688

693689
func testRecovery51() {
694690
AssertParse(
695691
"""
696-
switch { 42 }() {
697-
case _: return
692+
switch 1️⃣{ 2️⃣42 }()3️⃣ {
693+
4️⃣case _: return
698694
}
699-
"""
695+
""",
696+
diagnostics: [
697+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in 'switch' statement"),
698+
DiagnosticSpec(locationMarker: "2️⃣", message: "all statements inside a switch must be covered by a 'case' or 'default' label"),
699+
DiagnosticSpec(locationMarker: "3️⃣", message: "consecutive statements on a line must be separated by ';'"),
700+
DiagnosticSpec(locationMarker: "4️⃣", message: "'case' can only appear inside a 'switch' statement or 'enum' declaration"),
701+
]
700702
)
701703
}
702704

0 commit comments

Comments
 (0)