Skip to content

Commit 290f25e

Browse files
committed
Improve diagnostics for missing expresions in a sequence expression
Fixes #824 rdar://100214285
1 parent 03f748b commit 290f25e

File tree

11 files changed

+124
-43
lines changed

11 files changed

+124
-43
lines changed

CodeGeneration/Sources/SyntaxSupport/gyb_generated/ExprNodes.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public let EXPR_NODES: [Node] = [
197197
elementName: "Expression"),
198198

199199
Node(name: "PrefixOperatorExpr",
200-
nameForDiagnostics: "prefix operator expression",
200+
nameForDiagnostics: "operator",
201201
kind: "Expr",
202202
children: [
203203
Child(name: "OperatorToken",
@@ -208,7 +208,7 @@ public let EXPR_NODES: [Node] = [
208208
]),
209209

210210
Node(name: "BinaryOperatorExpr",
211-
nameForDiagnostics: nil,
211+
nameForDiagnostics: "operator",
212212
kind: "Expr",
213213
children: [
214214
Child(name: "OperatorToken",
@@ -363,7 +363,7 @@ public let EXPR_NODES: [Node] = [
363363
]),
364364

365365
Node(name: "UnresolvedTernaryExpr",
366-
nameForDiagnostics: nil,
366+
nameForDiagnostics: "ternary operator",
367367
kind: "Expr",
368368
children: [
369369
Child(name: "QuestionMark",
@@ -407,15 +407,15 @@ public let EXPR_NODES: [Node] = [
407407
]),
408408

409409
Node(name: "UnresolvedIsExpr",
410-
nameForDiagnostics: nil,
410+
nameForDiagnostics: "'is'",
411411
kind: "Expr",
412412
children: [
413413
Child(name: "IsTok",
414414
kind: .token(choices: [.keyword(text: "is")]))
415415
]),
416416

417417
Node(name: "IsExpr",
418-
nameForDiagnostics: "'is' expression",
418+
nameForDiagnostics: "'is'",
419419
kind: "Expr",
420420
children: [
421421
Child(name: "Expression",
@@ -427,7 +427,7 @@ public let EXPR_NODES: [Node] = [
427427
]),
428428

429429
Node(name: "UnresolvedAsExpr",
430-
nameForDiagnostics: nil,
430+
nameForDiagnostics: "'as'",
431431
kind: "Expr",
432432
children: [
433433
Child(name: "AsTok",
@@ -438,7 +438,7 @@ public let EXPR_NODES: [Node] = [
438438
]),
439439

440440
Node(name: "AsExpr",
441-
nameForDiagnostics: "'as' expression",
441+
nameForDiagnostics: "'as'",
442442
kind: "Expr",
443443
children: [
444444
Child(name: "Expression",

Sources/SwiftParserDiagnostics/DiagnosticExtensions.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,11 @@ extension FixIt.Changes {
9696
),
9797
.replaceLeadingTrivia(token: nextToken, newTrivia: []),
9898
]
99-
} else if let firstToken = node.firstToken(viewMode: .all),
99+
} else if node.leadingTrivia?.isEmpty ?? true,
100100
let previousToken = node.previousToken(viewMode: .fixedUp),
101-
!firstToken.tokenKind.isPunctuation,
102-
!previousToken.tokenKind.isPunctuation,
103-
firstToken.leadingTrivia.isEmpty,
104-
(previousToken.presence == .missing ? BasicFormat().visit(previousToken).trailingTrivia : previousToken.trailingTrivia).isEmpty,
101+
previousToken.presence == .present,
102+
previousToken.trailingTrivia.isEmpty,
103+
BasicFormat().requiresTrailingSpace(previousToken),
105104
leadingTrivia == nil
106105
{
107106
/// If neither this nor the previous token are punctionation make sure they

Sources/SwiftParserDiagnostics/MissingNodesError.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,25 @@ public struct MissingNodesError: ParserError {
223223
}
224224
}
225225

226+
let missingExpr: ExprSyntax?
227+
if let expr = firstMissingNode.as(MissingExprSyntax.self) {
228+
missingExpr = ExprSyntax(expr)
229+
} else if let typeExpr = firstMissingNode.parent?.as(TypeExprSyntax.self) {
230+
missingExpr = ExprSyntax(typeExpr)
231+
} else {
232+
missingExpr = nil
233+
}
234+
if let missingExpr = missingExpr,
235+
let exprList = missingExpr.parent?.as(ExprListSyntax.self),
236+
exprList.parent?.is(SequenceExprSyntax.self) ?? false,
237+
let previousSiblingIndex = exprList.index(missingExpr.index, offsetBy: -1, limitedBy: exprList.startIndex)
238+
{
239+
let previousSibling = exprList[previousSiblingIndex]
240+
if let previousSiblingName = previousSibling.nodeTypeNameForDiagnostics(allowBlockNames: false) {
241+
return "after \(previousSiblingName)"
242+
}
243+
}
244+
226245
return nil
227246
}
228247

@@ -261,8 +280,7 @@ public struct MissingNodesError: ParserError {
261280
var message = "expected \(description)"
262281
if let afterClause = afterClause {
263282
message += " \(afterClause)"
264-
}
265-
if let parentContextClause = parentContextClause(anchor: anchor?.parent ?? findCommonAncestor(missingNodes)) {
283+
} else if let parentContextClause = parentContextClause(anchor: anchor?.parent ?? findCommonAncestor(missingNodes)) {
266284
message += " \(parentContextClause)"
267285
}
268286
return message

Sources/SwiftSyntax/generated/Misc.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ extension SyntaxKind {
836836
case .arrowExpr:
837837
return nil
838838
case .asExpr:
839-
return "'as' expression"
839+
return "'as'"
840840
case .assignmentExpr:
841841
return nil
842842
case .associatedtypeDecl:
@@ -868,7 +868,7 @@ extension SyntaxKind {
868868
case .backDeployAttributeSpecList:
869869
return "'@_backDeploy' arguments"
870870
case .binaryOperatorExpr:
871-
return nil
871+
return "operator"
872872
case .booleanLiteralExpr:
873873
return "bool literal"
874874
case .borrowExpr:
@@ -1082,7 +1082,7 @@ extension SyntaxKind {
10821082
case .integerLiteralExpr:
10831083
return "integer literal"
10841084
case .isExpr:
1085-
return "'is' expression"
1085+
return "'is'"
10861086
case .isTypePattern:
10871087
return "'is' pattern"
10881088
case .keyPathComponentList:
@@ -1202,7 +1202,7 @@ extension SyntaxKind {
12021202
case .precedenceGroupRelation:
12031203
return "'relation' property of precedencegroup"
12041204
case .prefixOperatorExpr:
1205-
return "prefix operator expression"
1205+
return "operator"
12061206
case .primaryAssociatedTypeClause:
12071207
return "primary associated type clause"
12081208
case .primaryAssociatedTypeList:
@@ -1302,13 +1302,13 @@ extension SyntaxKind {
13021302
case .unexpectedNodes:
13031303
return nil
13041304
case .unresolvedAsExpr:
1305-
return nil
1305+
return "'as'"
13061306
case .unresolvedIsExpr:
1307-
return nil
1307+
return "'is'"
13081308
case .unresolvedPatternExpr:
13091309
return nil
13101310
case .unresolvedTernaryExpr:
1311-
return nil
1311+
return "ternary operator"
13121312
case .valueBindingPattern:
13131313
return "value binding pattern"
13141314
case .variableDecl:

Tests/SwiftDiagnosticsTest/DiagnosticsFormatterTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ final class DiagnosticsFormatterTests: XCTestCase {
3030
"""
3131
let expectedOutput = """
3232
1 │ var foo = bar +
33-
∣ ╰─ expected expression in variable
33+
∣ ╰─ expected expression after operator
3434
3535
"""
36-
AssertStringsEqualWithDiff(expectedOutput, annotate(source: source))
36+
AssertStringsEqualWithDiff(annotate(source: source), expectedOutput)
3737
}
3838

3939
func testMultipleDiagnosticsInOneLine() {
@@ -47,7 +47,7 @@ final class DiagnosticsFormatterTests: XCTestCase {
4747
∣ ╰─ expected name in member access
4848
4949
"""
50-
AssertStringsEqualWithDiff(expectedOutput, annotate(source: source))
50+
AssertStringsEqualWithDiff(annotate(source: source), expectedOutput)
5151
}
5252

5353
func testLineSkipping() {
@@ -78,7 +78,7 @@ final class DiagnosticsFormatterTests: XCTestCase {
7878
∣ ╰─ expected value and ')' to end function call
7979
8080
"""
81-
AssertStringsEqualWithDiff(expectedOutput, annotate(source: source))
81+
AssertStringsEqualWithDiff(annotate(source: source), expectedOutput)
8282
}
8383

8484
func testTwoDiagnosticsAtSameLocation() throws {
@@ -91,7 +91,7 @@ final class DiagnosticsFormatterTests: XCTestCase {
9191
9292
"""
9393

94-
AssertStringsEqualWithDiff(expectedOutput, annotate(source: source))
94+
AssertStringsEqualWithDiff(annotate(source: source), expectedOutput)
9595
}
9696

9797
func testAddsColoringToSingleErrorDiagnostic() {
@@ -101,10 +101,10 @@ final class DiagnosticsFormatterTests: XCTestCase {
101101

102102
let expectedOutput = """
103103
1 │ var foo = bar +
104-
∣ ╰─ \u{001B}[1;31merror: expected expression in variable\u{001B}[0;0m
104+
∣ ╰─ \u{001B}[1;31merror: expected expression after operator\u{001B}[0;0m
105105
106106
"""
107-
AssertStringsEqualWithDiff(expectedOutput, annotate(source: source, colorize: true))
107+
AssertStringsEqualWithDiff(annotate(source: source, colorize: true), expectedOutput)
108108
}
109109

110110
func testAddsColoringToMultipleDiagnosticsInOneLine() {
@@ -118,6 +118,6 @@ final class DiagnosticsFormatterTests: XCTestCase {
118118
∣ ╰─ \u{001B}[1;31merror: expected name in member access\u{001B}[0;0m
119119
120120
"""
121-
AssertStringsEqualWithDiff(expectedOutput, annotate(source: source, colorize: true))
121+
AssertStringsEqualWithDiff(annotate(source: source, colorize: true), expectedOutput)
122122
}
123123
}

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ final class DeclarationTests: XCTestCase {
597597
}
598598
""",
599599
diagnostics: [
600-
DiagnosticSpec(message: "expected declaration after 'public' modifier in struct")
600+
DiagnosticSpec(message: "expected declaration after 'public' modifier")
601601
]
602602
)
603603
}
@@ -929,7 +929,7 @@ final class DeclarationTests: XCTestCase {
929929
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in struct"),
930930
DiagnosticSpec(locationMarker: "2️⃣", message: "expected condition in conditional compilation clause"),
931931
DiagnosticSpec(locationMarker: "3️⃣", message: "expected name in attribute"),
932-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected declaration after attribute in conditional compilation clause"),
932+
DiagnosticSpec(locationMarker: "3️⃣", message: "expected declaration after attribute"),
933933
DiagnosticSpec(locationMarker: "3️⃣", message: "expected '#endif' in conditional compilation block"),
934934
DiagnosticSpec(locationMarker: "3️⃣", message: "expected '}' to end struct"),
935935
]
@@ -1394,6 +1394,20 @@ final class DeclarationTests: XCTestCase {
13941394
"""
13951395
)
13961396
}
1397+
1398+
func testMissingExpressionInVariableAssignment() {
1399+
AssertParse(
1400+
"""
1401+
let a =1️⃣
1402+
""",
1403+
diagnostics: [
1404+
DiagnosticSpec(message: "expected expression in variable")
1405+
],
1406+
fixedSource: """
1407+
let a = <#expression#>
1408+
"""
1409+
)
1410+
}
13971411
}
13981412

13991413
extension Parser.DeclAttributes {

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ final class ExpressionTests: XCTestCase {
2727
AssertParse(
2828
"a ? b :1️⃣",
2929
diagnostics: [
30-
DiagnosticSpec(message: "expected expression")
30+
DiagnosticSpec(message: "expected expression after ternary operator")
3131
]
3232
)
3333
}
@@ -788,7 +788,7 @@ final class ExpressionTests: XCTestCase {
788788
}
789789
""",
790790
diagnostics: [
791-
DiagnosticSpec(message: "expected expression in 'do' statement")
791+
DiagnosticSpec(message: "expected expression after ternary operator")
792792
]
793793
)
794794
}
@@ -1099,6 +1099,56 @@ final class ExpressionTests: XCTestCase {
10991099
"""#
11001100
)
11011101
}
1102+
1103+
func testMissingExpresssionInSequenceExpression() {
1104+
AssertParse(
1105+
"""
1106+
a ? b :1️⃣
1107+
""",
1108+
diagnostics: [
1109+
DiagnosticSpec(message: "expected expression after ternary operator")
1110+
],
1111+
fixedSource: """
1112+
a ? b : <#expression#>
1113+
"""
1114+
)
1115+
1116+
AssertParse(
1117+
"""
1118+
a +1️⃣
1119+
""",
1120+
diagnostics: [
1121+
DiagnosticSpec(message: "expected expression after operator")
1122+
],
1123+
fixedSource: """
1124+
a + <#expression#>
1125+
"""
1126+
)
1127+
1128+
AssertParse(
1129+
"""
1130+
a as1️⃣
1131+
""",
1132+
diagnostics: [
1133+
DiagnosticSpec(message: "expected type after 'as'")
1134+
],
1135+
fixedSource: """
1136+
a as <#type#>
1137+
"""
1138+
)
1139+
1140+
AssertParse(
1141+
"""
1142+
a is1️⃣
1143+
""",
1144+
diagnostics: [
1145+
DiagnosticSpec(message: "expected type after 'is'")
1146+
],
1147+
fixedSource: """
1148+
a is <#type#>
1149+
"""
1150+
)
1151+
}
11021152
}
11031153

11041154
final class MemberExprTests: XCTestCase {

Tests/SwiftParserTest/translated/ConflictMarkersTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ final class ConflictMarkersTests: XCTestCase {
8282
diagnostics: [
8383
// TODO: Old parser expected error on line 1: source control conflict marker in source file
8484
DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '<<<<<<< HEAD:conflict_markers.swift' before variable"),
85-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected expression in variable"),
85+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected expression after operator"),
8686
DiagnosticSpec(locationMarker: "3️⃣", message: "'b' is not a valid digit in integer literal"),
8787
DiagnosticSpec(locationMarker: "4️⃣", message: "extraneous code at top level"),
8888
]

Tests/SwiftParserTest/translated/ErrorsTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ final class ErrorsTests: XCTestCase {
8787
""",
8888
diagnostics: [
8989
// TODO: Old parser expected error on line 3: expected expression after '? ... :' in ternary expression
90-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression in 'do' statement")
90+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected expression after ternary operator")
9191
]
9292
)
9393
}

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ final class RecoveryTests: XCTestCase {
8080
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '>' to end generic argument clause"),
8181
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive statements on a line must be separated by ';'"),
8282
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code 'this greater: >' in subscript"),
83-
DiagnosticSpec(locationMarker: "4️⃣", message: "expected expression in function"),
83+
DiagnosticSpec(locationMarker: "4️⃣", message: "expected expression after operator"),
8484
DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code in function"),
8585
]
8686
)

0 commit comments

Comments
 (0)