Skip to content

Commit 7eb7c76

Browse files
authored
Merge pull request #958 from ahoppen/ahoppen/colon-and-expr-missing-in-ternary
Improve diagnostic if colon and expression are missing in ternary expression
2 parents d1495af + 3eea7ea commit 7eb7c76

File tree

5 files changed

+58
-28
lines changed

5 files changed

+58
-28
lines changed

Sources/SwiftParser/Diagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,22 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
633633
return .skipChildren
634634
}
635635
if node.colonMark.presence == .missing {
636-
addDiagnostic(node.colonMark, .missingColonInTernaryExprDiagnostic, handledNodes: [node.colonMark.id])
636+
if let siblings = node.parent?.children(viewMode: .all),
637+
let nextSibling = siblings[siblings.index(after: node.index)...].first,
638+
nextSibling.is(MissingExprSyntax.self) {
639+
addDiagnostic(node.colonMark, .missingColonAndExprInTernaryExpr, fixIts: [
640+
FixIt(message: InsertTokenFixIt(missingNodes: [Syntax(node.colonMark), Syntax(nextSibling)]), changes: [
641+
.makePresent(node: node.colonMark),
642+
.makePresent(node: nextSibling),
643+
])
644+
], handledNodes: [node.colonMark.id, nextSibling.id])
645+
} else {
646+
addDiagnostic(node.colonMark, .missingColonInTernaryExpr, fixIts: [
647+
FixIt(message: InsertTokenFixIt(missingNodes: [Syntax(node.colonMark)]), changes: [
648+
.makePresent(node: node.colonMark),
649+
])
650+
], handledNodes: [node.colonMark.id])
651+
}
637652
}
638653
return .visitChildren
639654
}

Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ public enum StaticParserError: String, DiagnosticMessage {
7676
case editorPlaceholderInSourceFile = "editor placeholder in source file"
7777
case expectedExpressionAfterTry = "expected expression after 'try'"
7878
case invalidFlagAfterPrecedenceGroupAssignment = "expected 'true' or 'false' after 'assignment'"
79-
case missingColonInTernaryExprDiagnostic = "expected ':' after '? ...' in ternary expression"
79+
case missingColonAndExprInTernaryExpr = "expected ':' and expression after '? ...' in ternary expression"
80+
case missingColonInTernaryExpr = "expected ':' after '? ...' in ternary expression"
8081
case operatorShouldBeDeclaredWithoutBody = "operator should not be declared with body"
8182
case standaloneSemicolonStatement = "standalone ';' statements are not allowed"
8283
case subscriptsCannotHaveNames = "subscripts cannot have a name"

Tests/SwiftParserTest/Declarations.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ final class DeclarationTests: XCTestCase {
189189
AssertParse(
190190
"_ = foo/* */?.description1️⃣",
191191
diagnostics: [
192-
DiagnosticSpec(message: "expected ':' after '? ...' in ternary expression"),
193-
DiagnosticSpec(message: "expected expression"),
192+
DiagnosticSpec(message: "expected ':' and expression after '? ...' in ternary expression"),
194193
]
195194
)
196195

Tests/SwiftParserTest/Expressions.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,8 +588,7 @@ final class ExpressionTests: XCTestCase {
588588
AssertParse(
589589
"foo ? 11️⃣",
590590
diagnostics: [
591-
DiagnosticSpec(message: "expected ':' after '? ...' in ternary expression"),
592-
DiagnosticSpec(message: "expected expression"),
591+
DiagnosticSpec(message: "expected ':' and expression after '? ...' in ternary expression"),
593592
]
594593
)
595594
}

Tests/SwiftParserTest/translated/InvalidIfExprTests.swift

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,46 @@ final class InvalidIfExprTests: XCTestCase {
66
func testInvalidIfExpr1() {
77
AssertParse(
88
"""
9-
func unbalanced_question(a: Bool, b: Bool, c: Bool, d: Bool) {
10-
(a ? b1️⃣)
11-
(a ? b : c ? d2️⃣)
12-
(a ? b ? c : d3️⃣)
13-
(a ? b ? c4️⃣)
14-
}
9+
(a ? b1️⃣)
1510
""",
1611
diagnostics: [
17-
// TODO: Old parser expected error on line 2: expected ':' after '? ...' in ternary
18-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected ':' after '? ...' in ternary expression"),
19-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in tuple"),
20-
// TODO: Old parser expected error on line 3: expected ':' after '? ...' in ternary
21-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ':' after '? ...' in ternary expression"),
22-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected expression in tuple"),
23-
// TODO: Old parser expected error on line 4: expected ':' after '? ...' in ternary
24-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected ':' after '? ...' in ternary expression"),
25-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected expression in tuple"),
26-
// TODO: Old parser expected error on line 5: expected ':' after '? ...' in ternary
27-
// TODO: Old parser expected error on line 5: expected ':' after '? ...' in ternary
28-
DiagnosticSpec(locationMarker: "4️⃣", message: "expected ':' after '? ...' in ternary expression"),
29-
DiagnosticSpec(locationMarker: "4️⃣", message: "expected expression in tuple"),
30-
DiagnosticSpec(locationMarker: "4️⃣", message: "expected ':' after '? ...' in ternary expression"),
31-
DiagnosticSpec(locationMarker: "4️⃣", message: "expected expression in tuple"),
32-
]
12+
DiagnosticSpec(message: "expected ':' and expression after '? ...' in ternary expression", fixIts: ["insert ':' and expression"]),
13+
], fixedSource: "(a ? b: <#expression#>)"
14+
)
15+
}
16+
17+
func testInvalidIfExpr2() {
18+
AssertParse(
19+
"""
20+
(a ? b : c ? d1️⃣)
21+
""",
22+
diagnostics: [
23+
DiagnosticSpec(message: "expected ':' and expression after '? ...' in ternary expression", fixIts: ["insert ':' and expression"]),
24+
], fixedSource: "(a ? b : c ? d: <#expression#>)"
25+
)
26+
}
27+
28+
func testInvalidIfExpr3() {
29+
AssertParse(
30+
"""
31+
(a ? b ? c : d1️⃣
32+
""",
33+
diagnostics: [
34+
DiagnosticSpec(message: "expected ':' and expression after '? ...' in ternary expression", fixIts: ["insert ':' and expression"]),
35+
DiagnosticSpec(message: "expected ')' to end tuple")
36+
], fixedSource: "(a ? b ? c : d: <#expression#>)"
37+
)
38+
}
39+
40+
func testInvalidIfExpr4() {
41+
AssertParse(
42+
"""
43+
(a ? b ? c1️⃣)
44+
""",
45+
diagnostics: [
46+
DiagnosticSpec(message: "expected ':' and expression after '? ...' in ternary expression", fixIts: ["insert ':' and expression"]),
47+
DiagnosticSpec(message: "expected ':' and expression after '? ...' in ternary expression", fixIts: ["insert ':' and expression"]),
48+
], fixedSource: "(a ? b ? c: <#expression#>: <#expression#>)"
3349
)
3450
}
3551

0 commit comments

Comments
 (0)