Skip to content

Commit f0d2d35

Browse files
authored
Merge pull request #2811 from mateusrodriguesxyz/improve-unexpected-tokens-diagnostics
Improve unexpected tokens diagnostics
2 parents fe1a312 + b02a7f1 commit f0d2d35

File tree

3 files changed

+60
-173
lines changed

3 files changed

+60
-173
lines changed

Sources/SwiftParser/Recovery.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,33 @@ extension Parser.Lookahead {
123123
if currentTokenPrecedence >= recoveryPrecedence {
124124
break
125125
}
126-
self.consumeAnyToken()
127126
if let closingDelimiter = currentTokenPrecedence.closingTokenKind {
128127
let closingDelimiterSpec = TokenSpec(closingDelimiter)
128+
let canCloseAtSameLine: Int? = self.withLookahead { lookahead in
129+
var tokensToSkip = 0
130+
while !lookahead.at(.endOfFile), !lookahead.currentToken.isAtStartOfLine {
131+
tokensToSkip += 1
132+
if lookahead.at(closingDelimiterSpec) {
133+
return tokensToSkip
134+
} else {
135+
lookahead.consumeAnyToken()
136+
}
137+
}
138+
return nil
139+
}
140+
if let tokensToSkip = canCloseAtSameLine {
141+
for _ in 0..<tokensToSkip {
142+
self.consumeAnyToken()
143+
}
144+
continue
145+
}
146+
self.consumeAnyToken()
129147
guard self.canRecoverTo(closingDelimiterSpec) != nil else {
130-
break
148+
continue
131149
}
132150
self.eat(closingDelimiterSpec)
151+
} else {
152+
self.consumeAnyToken()
133153
}
134154
}
135155

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,47 +1301,13 @@ final class DeclarationTests: ParserTestCase {
13011301
func testExpressionMember() {
13021302
assertParse(
13031303
"""
1304-
struct S 1️⃣{2️⃣
1305-
3️⃣/4️⃣ ###line 25 "line-directive.swift"5️⃣
1306-
6️⃣}
1304+
struct S {
1305+
1️⃣/ ###line 25 "line-directive.swift"
1306+
}
13071307
""",
13081308
diagnostics: [
1309-
DiagnosticSpec(
1310-
locationMarker: "2️⃣",
1311-
message: "expected '}' to end struct",
1312-
notes: [NoteSpec(locationMarker: "1️⃣", message: "to match this opening '{'")],
1313-
fixIts: ["insert '}'"]
1314-
),
1315-
DiagnosticSpec(
1316-
locationMarker: "4️⃣",
1317-
message: "bare slash regex literal may not start with space",
1318-
fixIts: [
1319-
"convert to extended regex literal with '#'",
1320-
#"insert '\'"#,
1321-
]
1322-
),
1323-
DiagnosticSpec(
1324-
locationMarker: "5️⃣",
1325-
message: "expected '/' to end regex literal",
1326-
notes: [NoteSpec(locationMarker: "3️⃣", message: "to match this opening '/'")],
1327-
fixIts: ["insert '/'"]
1328-
),
1329-
DiagnosticSpec(
1330-
locationMarker: "6️⃣",
1331-
message: "extraneous brace at top level"
1332-
),
1333-
],
1334-
applyFixIts: [
1335-
"insert '}'",
1336-
#"insert '\'"#,
1337-
"insert '/'",
1338-
],
1339-
fixedSource: #"""
1340-
struct S {
1341-
}
1342-
/\ ###line 25 "line-directive.swift"/
1343-
}
1344-
"""#
1309+
DiagnosticSpec(message: #"unexpected code '/ ###line 25 "line-directive.swift"' in struct"#)
1310+
]
13451311
)
13461312
}
13471313

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 33 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,44 +1838,31 @@ final class RecoveryTests: ParserTestCase {
18381838
func testRecovery100() {
18391839
assertParse(
18401840
"""
1841-
struct ErrorInFunctionSignatureResultArrayType1 1️⃣{
1842-
func foo() -> Int2️⃣[ {
1841+
struct ErrorInFunctionSignatureResultArrayType1 {
1842+
func foo() -> Int1️⃣[ {
18431843
return [0]
1844-
}3️⃣
1845-
func bar() -> Int4️⃣] {
1844+
}
1845+
func bar() -> Int2️⃣] {
18461846
return [0]
18471847
}
1848-
5️⃣}
1848+
}
18491849
""",
18501850
diagnostics: [
18511851
DiagnosticSpec(
1852-
locationMarker: "2️⃣",
1853-
message: "expected '}' to end struct",
1854-
notes: [NoteSpec(locationMarker: "1️⃣", message: "to match this opening '{'")],
1855-
fixIts: ["insert '}'"]
1856-
),
1857-
DiagnosticSpec(
1858-
locationMarker: "3️⃣",
1859-
message: "expected ']' to end array",
1860-
notes: [NoteSpec(locationMarker: "2️⃣", message: "to match this opening '['")],
1861-
fixIts: ["insert ']'"]
1852+
locationMarker: "1️⃣",
1853+
message: "unexpected code '[' in function"
18621854
),
18631855
DiagnosticSpec(
1864-
locationMarker: "4️⃣",
1856+
locationMarker: "2️⃣",
18651857
message: "unexpected ']' in type; did you mean to write an array type?",
18661858
fixIts: ["insert '['"]
18671859
),
1868-
DiagnosticSpec(
1869-
locationMarker: "5️⃣",
1870-
message: "extraneous brace at top level"
1871-
),
18721860
],
18731861
fixedSource: """
18741862
struct ErrorInFunctionSignatureResultArrayType1 {
1875-
func foo() -> Int
1876-
}[ {
1863+
func foo() -> Int[ {
18771864
return [0]
1878-
}]
1865+
}
18791866
func bar() -> [Int] {
18801867
return [0]
18811868
}
@@ -1887,45 +1874,15 @@ final class RecoveryTests: ParserTestCase {
18871874
func testRecovery101() {
18881875
assertParse(
18891876
"""
1890-
struct ErrorInFunctionSignatureResultArrayType2 1️⃣{
1891-
func foo() -> Int2️⃣[03️⃣ {
1877+
struct ErrorInFunctionSignatureResultArrayType2 {
1878+
func foo() -> Int1️⃣[0 {
18921879
return [0]
1893-
}4️⃣
1894-
5️⃣}
1880+
}
1881+
}
18951882
""",
18961883
diagnostics: [
1897-
// TODO: Old parser expected error to add `]` on line 2, but we should just recover to
1898-
// `{` with `[0` becoming unexpected.
1899-
DiagnosticSpec(
1900-
locationMarker: "2️⃣",
1901-
message: "expected '}' to end struct",
1902-
notes: [NoteSpec(locationMarker: "1️⃣", message: "to match this opening '{'")],
1903-
fixIts: ["insert '}'"]
1904-
),
1905-
DiagnosticSpec(
1906-
locationMarker: "3️⃣",
1907-
message: "expected ',' in array element",
1908-
fixIts: ["insert ','"]
1909-
),
1910-
DiagnosticSpec(
1911-
locationMarker: "4️⃣",
1912-
message: "expected ']' to end array",
1913-
notes: [NoteSpec(locationMarker: "2️⃣", message: "to match this opening '['")],
1914-
fixIts: ["insert ']'"]
1915-
),
1916-
DiagnosticSpec(
1917-
locationMarker: "5️⃣",
1918-
message: "extraneous brace at top level"
1919-
),
1920-
],
1921-
fixedSource: """
1922-
struct ErrorInFunctionSignatureResultArrayType2 {
1923-
func foo() -> Int
1924-
}[0, {
1925-
return [0]
1926-
}]
1927-
}
1928-
"""
1884+
DiagnosticSpec(message: "unexpected code '[0' in function")
1885+
]
19291886
)
19301887
}
19311888

@@ -1977,37 +1934,14 @@ final class RecoveryTests: ParserTestCase {
19771934
func testRecovery105() {
19781935
assertParse(
19791936
"""
1980-
struct ErrorInFunctionSignatureResultArrayType11 ℹ️{
1981-
func foo() -> Int1️⃣[(a){a++}]2️⃣ {
1937+
struct ErrorInFunctionSignatureResultArrayType11 {
1938+
func foo() -> Int1️⃣[(a){a++}] {
19821939
}
1983-
3️⃣}
1940+
}
19841941
""",
19851942
diagnostics: [
1986-
// TODO: We should just recover to `{` with `[(a){a++}]` becoming unexpected.
1987-
DiagnosticSpec(
1988-
locationMarker: "1️⃣",
1989-
message: "expected '}' to end struct",
1990-
notes: [NoteSpec(message: "to match this opening '{'")],
1991-
fixIts: ["insert '}'"]
1992-
),
1993-
DiagnosticSpec(
1994-
locationMarker: "2️⃣",
1995-
message: "consecutive statements on a line must be separated by newline or ';'",
1996-
fixIts: ["insert newline", "insert ';'"]
1997-
),
1998-
DiagnosticSpec(
1999-
locationMarker: "3️⃣",
2000-
message: "extraneous brace at top level"
2001-
),
2002-
],
2003-
fixedSource: """
2004-
struct ErrorInFunctionSignatureResultArrayType11 {
2005-
func foo() -> Int
2006-
}[(a){a++}]
2007-
{
2008-
}
2009-
}
2010-
"""
1943+
DiagnosticSpec(message: "unexpected code '[(a){a++}]' in function")
1944+
]
20111945
)
20121946
}
20131947

@@ -2513,50 +2447,32 @@ final class RecoveryTests: ParserTestCase {
25132447
assertParse(
25142448
"""
25152449
#if true
2516-
struct Foo19605164 1️⃣{
2517-
func a2️⃣(s: S3️⃣[{{g4️⃣) -> Int {}
2518-
}}5️⃣}
2450+
struct Foo19605164 {
2451+
func a1️⃣(s: S2️⃣3️⃣[{{g4️⃣) -> Int {}
2452+
}}}
25192453
#endif
25202454
""",
25212455
diagnostics: [
25222456
DiagnosticSpec(
2523-
locationMarker: "3️⃣",
2457+
locationMarker: "2️⃣",
25242458
message: "expected ')' to end parameter clause",
2525-
notes: [NoteSpec(locationMarker: "2️⃣", message: "to match this opening '('")],
2459+
notes: [NoteSpec(locationMarker: "1️⃣", message: "to match this opening '('")],
25262460
fixIts: ["insert ')'"]
25272461
),
25282462
DiagnosticSpec(
25292463
locationMarker: "3️⃣",
2530-
message: "expected '}' to end struct",
2531-
notes: [NoteSpec(locationMarker: "1️⃣", message: "to match this opening '{'")],
2532-
fixIts: ["insert '}'"]
2464+
message: "unexpected code '[' in function"
25332465
),
25342466
DiagnosticSpec(
25352467
locationMarker: "4️⃣",
25362468
message: "unexpected code ') -> Int {}' in closure"
25372469
),
2538-
DiagnosticSpec(
2539-
locationMarker: "5️⃣",
2540-
message: "expected ',' in array element",
2541-
fixIts: ["insert ','"]
2542-
),
2543-
DiagnosticSpec(
2544-
locationMarker: "5️⃣",
2545-
message: "expected ']' to end array",
2546-
notes: [NoteSpec(locationMarker: "3️⃣", message: "to match this opening '['")],
2547-
fixIts: ["insert ']'"]
2548-
),
2549-
DiagnosticSpec(
2550-
locationMarker: "5️⃣",
2551-
message: "unexpected brace in conditional compilation block"
2552-
),
25532470
],
25542471
fixedSource: """
25552472
#if true
25562473
struct Foo19605164 {
2557-
func a(s: S)
2558-
}[{{g) -> Int {}
2559-
}},]}
2474+
func a(s: S) [{{g) -> Int {}
2475+
}}}
25602476
#endif
25612477
"""
25622478
)
@@ -3211,28 +3127,13 @@ final class RecoveryTests: ParserTestCase {
32113127
func testRecovery179() {
32123128
assertParse(
32133129
"""
3214-
func testSkipUnbalancedParen() ℹ️{1️⃣
3215-
2️⃣?(
3130+
func testSkipUnbalancedParen() {
3131+
1️⃣?(
32163132
}
32173133
""",
32183134
diagnostics: [
3219-
DiagnosticSpec(
3220-
locationMarker: "1️⃣",
3221-
message: "expected '}' to end function",
3222-
notes: [NoteSpec(message: "to match this opening '{'")],
3223-
fixIts: ["insert '}'"]
3224-
),
3225-
DiagnosticSpec(
3226-
locationMarker: "2️⃣",
3227-
message: "extraneous code at top level"
3228-
),
3229-
],
3230-
fixedSource: """
3231-
func testSkipUnbalancedParen() {
3232-
}
3233-
?(
3234-
}
3235-
"""
3135+
DiagnosticSpec(message: "unexpected code '?(' in function")
3136+
]
32363137
)
32373138
}
32383139

0 commit comments

Comments
 (0)