Skip to content

Commit 195ed92

Browse files
committed
Add newline fix-it after consecutive declarations
1 parent af10e84 commit 195ed92

File tree

6 files changed

+275
-27
lines changed

6 files changed

+275
-27
lines changed

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ fileprivate func getTokens(between first: TokenSyntax, and second: TokenSyntax)
5050
return tokens
5151
}
5252

53+
fileprivate func getIndentation(for token: TokenSyntax) -> Trivia {
54+
var ancestor: Syntax = Syntax(token)
55+
while let parent = ancestor.parent {
56+
ancestor = parent
57+
if !ancestor.leadingTrivia.isEmpty {
58+
return ancestor.leadingTrivia
59+
}
60+
}
61+
62+
return []
63+
}
64+
5365
fileprivate extension TokenSyntax {
5466
/// Assuming this token is a `poundAvailableKeyword` or `poundUnavailableKeyword`
5567
/// returns the opposite keyword.
@@ -1160,8 +1172,16 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
11601172
position: position,
11611173
.consecutiveDeclarationsOnSameLine,
11621174
fixIts: [
1163-
FixIt(message: .insertSemicolon, changes: .makePresent(semicolon))
1164-
],
1175+
node.lastToken(viewMode: .sourceAccurate).map {
1176+
FixIt(
1177+
message: .insertNewline,
1178+
changes: [
1179+
.replaceTrailingTrivia(token: $0, newTrivia: $0.trailingTrivia + .newlines(1) + getIndentation(for: $0))
1180+
]
1181+
)
1182+
},
1183+
FixIt(message: .insertSemicolon, changes: .makePresent(semicolon)),
1184+
].compactMap { $0 },
11651185
handledNodes: [semicolon.id]
11661186
)
11671187
} else {

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ extension DiagnosticMessage where Self == StaticParserError {
106106
.init("'class' constraint can only appear on protocol declarations")
107107
}
108108
public static var consecutiveDeclarationsOnSameLine: Self {
109-
.init("consecutive declarations on a line must be separated by ';'")
109+
.init("consecutive declarations on a line must be separated by newline or ';'")
110110
}
111111
public static var consecutiveStatementsOnSameLine: Self {
112112
.init("consecutive statements on a line must be separated by ';'")

Tests/SwiftParserTest/Assertions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ class FixItApplier: SyntaxRewriter {
275275
var changes: [FixIt.Change]
276276

277277
init(diagnostics: [Diagnostic], withMessages messages: [String]?) {
278-
let messages = messages ?? diagnostics.compactMap { $0.fixIts.first?.message.message }
278+
let messages = messages ?? diagnostics.compactMap { $0.fixIts.first?.message.message }
279279

280280
self.changes =
281281
diagnostics

Tests/SwiftParserTest/translated/ConsecutiveStatementsTests.swift

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ final class ConsecutiveStatementsTests: XCTestCase {
102102
}
103103
""",
104104
diagnostics: [
105-
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by ';'", fixIts: ["insert ';'"]),
106-
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive declarations on a line must be separated by ';'", fixIts: ["insert ';'"]),
105+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"]),
106+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"]),
107107
DiagnosticSpec(locationMarker: "3️⃣", message: "consecutive statements on a line must be separated by ';'", fixIts: ["insert ';'"]),
108108
DiagnosticSpec(locationMarker: "4️⃣", message: "consecutive statements on a line must be separated by ';'", fixIts: ["insert ';'"]),
109109
DiagnosticSpec(locationMarker: "5️⃣", message: "consecutive statements on a line must be separated by ';'", fixIts: ["insert ';'"]),
@@ -129,7 +129,7 @@ final class ConsecutiveStatementsTests: XCTestCase {
129129
)
130130
}
131131

132-
func testConsecutiveStatements4() {
132+
func testConsecutiveStatements4a() {
133133
assertParse(
134134
"""
135135
class C {
@@ -142,8 +142,39 @@ final class ConsecutiveStatementsTests: XCTestCase {
142142
}
143143
""",
144144
diagnostics: [
145-
DiagnosticSpec(message: "consecutive declarations on a line must be separated by ';'", fixIts: ["insert ';'"])
145+
DiagnosticSpec(message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"])
146146
],
147+
applyFixIts: ["insert newline"],
148+
fixedSource: """
149+
class C {
150+
// In a sequence of declarations.
151+
var a, b : Int
152+
func d() -> Int {}
153+
init() {
154+
a = 0
155+
b = 0
156+
}
157+
}
158+
"""
159+
)
160+
}
161+
162+
func testConsecutiveStatements4b() {
163+
assertParse(
164+
"""
165+
class C {
166+
// In a sequence of declarations.
167+
var a, b : Int1️⃣ func d() -> Int {}
168+
init() {
169+
a = 0
170+
b = 0
171+
}
172+
}
173+
""",
174+
diagnostics: [
175+
DiagnosticSpec(message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"])
176+
],
177+
applyFixIts: ["insert ';'"],
147178
fixedSource: """
148179
class C {
149180
// In a sequence of declarations.
@@ -157,16 +188,37 @@ final class ConsecutiveStatementsTests: XCTestCase {
157188
)
158189
}
159190

160-
func testConsecutiveStatements5() {
191+
func testConsecutiveStatements5a() {
192+
assertParse(
193+
"""
194+
protocol P {
195+
func a()1️⃣ func b()
196+
}
197+
""",
198+
diagnostics: [
199+
DiagnosticSpec(message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"])
200+
],
201+
applyFixIts: ["insert newline"],
202+
fixedSource: """
203+
protocol P {
204+
func a()
205+
func b()
206+
}
207+
"""
208+
)
209+
}
210+
211+
func testConsecutiveStatements5b() {
161212
assertParse(
162213
"""
163214
protocol P {
164215
func a()1️⃣ func b()
165216
}
166217
""",
167218
diagnostics: [
168-
DiagnosticSpec(message: "consecutive declarations on a line must be separated by ';'", fixIts: ["insert ';'"])
219+
DiagnosticSpec(message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"])
169220
],
221+
applyFixIts: ["insert ';'"],
170222
fixedSource: """
171223
protocol P {
172224
func a(); func b()
@@ -175,7 +227,31 @@ final class ConsecutiveStatementsTests: XCTestCase {
175227
)
176228
}
177229

178-
func testConsecutiveStatements6() {
230+
func testConsecutiveStatements6a() {
231+
assertParse(
232+
"""
233+
enum Color {
234+
case Red1️⃣ case Blue
235+
func a() {}2️⃣ func b() {}
236+
}
237+
""",
238+
diagnostics: [
239+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"]),
240+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"]),
241+
],
242+
applyFixIts: ["insert newline"],
243+
fixedSource: """
244+
enum Color {
245+
case Red
246+
case Blue
247+
func a() {}
248+
func b() {}
249+
}
250+
"""
251+
)
252+
}
253+
254+
func testConsecutiveStatements6b() {
179255
assertParse(
180256
"""
181257
enum Color {
@@ -184,9 +260,10 @@ final class ConsecutiveStatementsTests: XCTestCase {
184260
}
185261
""",
186262
diagnostics: [
187-
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by ';'", fixIts: ["insert ';'"]),
188-
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive declarations on a line must be separated by ';'", fixIts: ["insert ';'"]),
263+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"]),
264+
DiagnosticSpec(locationMarker: "2️⃣", message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"]),
189265
],
266+
applyFixIts: ["insert ';'"],
190267
fixedSource: """
191268
enum Color {
192269
case Red; case Blue
@@ -212,4 +289,24 @@ final class ConsecutiveStatementsTests: XCTestCase {
212289
"""
213290
)
214291
}
292+
293+
func testConsecutiveStatements8() {
294+
assertParse(
295+
"""
296+
class Foo {
297+
func a() {}1️⃣/* some comment */ func b() {}
298+
}
299+
""",
300+
diagnostics: [
301+
DiagnosticSpec(locationMarker: "1️⃣", message: "consecutive declarations on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"])
302+
],
303+
applyFixIts: ["insert newline"],
304+
fixedSource: """
305+
class Foo {
306+
func a() {}/* some comment */
307+
func b() {}
308+
}
309+
"""
310+
)
311+
}
215312
}

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ final class RecoveryTests: XCTestCase {
938938
)
939939
}
940940

941-
func testRecovery62() {
941+
func testRecovery62a() {
942942
assertParse(
943943
"""
944944
enum EE 1️⃣EE<T> where T : Multi {
@@ -954,11 +954,44 @@ final class RecoveryTests: XCTestCase {
954954
),
955955
DiagnosticSpec(
956956
locationMarker: "2️⃣",
957-
message: "consecutive declarations on a line must be separated by ';'",
958-
fixIts: ["insert ';'"]
957+
message: "consecutive declarations on a line must be separated by newline or ';'",
958+
fixIts: ["insert newline", "insert ';'"]
959+
),
960+
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code 'a' before enum case"),
961+
],
962+
applyFixIts: ["join the identifiers together", "insert newline"],
963+
fixedSource: """
964+
enum EEEE<T> where T : Multi {
965+
case a
966+
a
967+
case b
968+
}
969+
"""
970+
)
971+
}
972+
973+
func testRecovery62b() {
974+
assertParse(
975+
"""
976+
enum EE 1️⃣EE<T> where T : Multi {
977+
case a2️⃣ 3️⃣a
978+
case b
979+
}
980+
""",
981+
diagnostics: [
982+
DiagnosticSpec(
983+
locationMarker: "1️⃣",
984+
message: "found an unexpected second identifier in enum; is there an accidental break?",
985+
fixIts: ["join the identifiers together"]
986+
),
987+
DiagnosticSpec(
988+
locationMarker: "2️⃣",
989+
message: "consecutive declarations on a line must be separated by newline or ';'",
990+
fixIts: ["insert newline", "insert ';'"]
959991
),
960992
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code 'a' before enum case"),
961993
],
994+
applyFixIts: ["join the identifiers together", "insert ';'"],
962995
fixedSource: """
963996
enum EEEE<T> where T : Multi {
964997
case a;a
@@ -1565,8 +1598,8 @@ final class RecoveryTests: XCTestCase {
15651598
diagnostics: [
15661599
DiagnosticSpec(
15671600
locationMarker: "1️⃣",
1568-
message: "consecutive declarations on a line must be separated by ';'",
1569-
fixIts: ["insert ';'"]
1601+
message: "consecutive declarations on a line must be separated by newline or ';'",
1602+
fixIts: ["insert newline", "insert ';'"]
15701603
),
15711604
DiagnosticSpec(
15721605
locationMarker: "1️⃣",
@@ -1605,7 +1638,8 @@ final class RecoveryTests: XCTestCase {
16051638
],
16061639
fixedSource: """
16071640
struct ErrorTypeInVarDeclDictionaryType {
1608-
let a1: String;:
1641+
let a1: String
1642+
:
16091643
let a2: [String: Int]
16101644
let a3: [String: [Int]]
16111645
let a4: [String: Int]

0 commit comments

Comments
 (0)