From f504798c617eef5d9c3a35e383eed68b2cd70855 Mon Sep 17 00:00:00 2001 From: Chris White <84482442+whiteio@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:05:13 +0100 Subject: [PATCH] Suggest to add missing `equal` token to variable declarations When a variable declaration is followed by an expression on the same line, it should be suggested to insert an `equal` token --- Sources/SwiftParser/Declarations.swift | 8 +++ Tests/SwiftParserTest/DeclarationTests.swift | 10 ++++ Tests/SwiftParserTest/RegexLiteralTests.swift | 21 ++++--- .../ConsecutiveStatementsTests.swift | 53 ++++++----------- .../translated/ForwardSlashRegexTests.swift | 7 ++- .../translated/RecoveryTests.swift | 59 ++----------------- 6 files changed, 57 insertions(+), 101 deletions(-) diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index d110c6f6a1a..545810e2ff0 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -1405,6 +1405,14 @@ extension Parser { value: initExpr, arena: self.arena ) + } else if self.atStartOfExpression(), !self.at(.leftBrace), !self.currentToken.flags.contains(.isAtStartOfLine) { + let missingEqual = RawTokenSyntax(missing: .equal, arena: self.arena) + let expr = self.parseExpression() + initializer = RawInitializerClauseSyntax( + equal: missingEqual, + value: expr, + arena: self.arena + ) } else { initializer = nil } diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index c6084fd3833..2493853e711 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -2530,4 +2530,14 @@ final class DeclarationTests: XCTestCase { """ ) } + + func testMissingEqualInVariableDeclaration() { + assertParse( + "let foo: [Int] 1️⃣[]", + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "expected '=' in variable", fixIts: ["insert '='"]) + ], + fixedSource: "let foo: [Int] = []" + ) + } } diff --git a/Tests/SwiftParserTest/RegexLiteralTests.swift b/Tests/SwiftParserTest/RegexLiteralTests.swift index f98df0324bb..3348b1a0a50 100644 --- a/Tests/SwiftParserTest/RegexLiteralTests.swift +++ b/Tests/SwiftParserTest/RegexLiteralTests.swift @@ -1211,13 +1211,13 @@ final class RegexLiteralTests: XCTestCase { func testPrefixOpSplitting2a() { assertParse( """ - let x1️⃣ .2️⃣/abc/ + let x 1️⃣.2️⃣/abc/ """, diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] + message: "expected '=' in variable", + fixIts: ["insert '='"] ), DiagnosticSpec( locationMarker: "2️⃣", @@ -1225,10 +1225,9 @@ final class RegexLiteralTests: XCTestCase { fixIts: ["insert name"] ), ], - applyFixIts: ["insert newline", "insert name"], + applyFixIts: ["insert '='", "insert name"], fixedSource: """ - let x - .<#identifier#>/abc/ + let x = .<#identifier#>/abc/ """ ) } @@ -1236,13 +1235,13 @@ final class RegexLiteralTests: XCTestCase { func testPrefixOpSplitting2b() { assertParse( """ - let x1️⃣ .2️⃣/abc/ + let x 1️⃣.2️⃣/abc/ """, diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] + message: "expected '=' in variable", + fixIts: ["insert '='"] ), DiagnosticSpec( locationMarker: "2️⃣", @@ -1250,9 +1249,9 @@ final class RegexLiteralTests: XCTestCase { fixIts: ["insert name"] ), ], - applyFixIts: ["insert ';'", "insert name"], + applyFixIts: ["insert '='", "insert name"], fixedSource: """ - let x; .<#identifier#>/abc/ + let x = .<#identifier#>/abc/ """ ) } diff --git a/Tests/SwiftParserTest/translated/ConsecutiveStatementsTests.swift b/Tests/SwiftParserTest/translated/ConsecutiveStatementsTests.swift index a2168a47b98..09e00f036ab 100644 --- a/Tests/SwiftParserTest/translated/ConsecutiveStatementsTests.swift +++ b/Tests/SwiftParserTest/translated/ConsecutiveStatementsTests.swift @@ -70,7 +70,7 @@ final class ConsecutiveStatementsTests: XCTestCase { if i != j { i = j } // Errors i = j1️⃣ j = i - let r : Int2️⃣ i = j + let r : Int 2️⃣i = j let s : Int3️⃣ let t : Int _ = r; _ = s; _ = t } @@ -83,8 +83,8 @@ final class ConsecutiveStatementsTests: XCTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] + message: "expected '=' in variable", + fixIts: ["insert '='"] ), DiagnosticSpec( locationMarker: "3️⃣", @@ -92,7 +92,7 @@ final class ConsecutiveStatementsTests: XCTestCase { fixIts: ["insert newline", "insert ';'"] ), ], - applyFixIts: ["insert newline"], + applyFixIts: ["insert newline", "insert '='"], fixedSource: """ // Within a function func test(i: inout Int, j: inout Int) { @@ -102,8 +102,7 @@ final class ConsecutiveStatementsTests: XCTestCase { // Errors i = j j = i - let r : Int - i = j + let r : Int = i = j let s : Int let t : Int _ = r; _ = s; _ = t @@ -122,7 +121,7 @@ final class ConsecutiveStatementsTests: XCTestCase { if i != j { i = j } // Errors i = j1️⃣ j = i - let r : Int2️⃣ i = j + let r : Int 2️⃣i = j let s : Int3️⃣ let t : Int _ = r; _ = s; _ = t } @@ -135,8 +134,8 @@ final class ConsecutiveStatementsTests: XCTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] + message: "expected '=' in variable", + fixIts: ["insert '='"] ), DiagnosticSpec( locationMarker: "3️⃣", @@ -153,7 +152,7 @@ final class ConsecutiveStatementsTests: XCTestCase { if i != j { i = j } // Errors i = j; j = i - let r : Int; i = j + let r : Int i = j let s : Int; let t : Int _ = r; _ = s; _ = t } @@ -468,26 +467,18 @@ final class ConsecutiveStatementsTests: XCTestCase { assertParse( """ // At the top level - var i, j : Int1️⃣ i = j2️⃣ j = i + var i, j : Int 1️⃣i = j2️⃣ j = i """, diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), - DiagnosticSpec( - locationMarker: "2️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), + message: "expected '=' in variable", + fixIts: ["insert '='"] + ) ], - applyFixIts: ["insert newline"], fixedSource: """ // At the top level - var i, j : Int - i = j - j = i + var i, j : Int = i = j j = i """ ) } @@ -496,24 +487,18 @@ final class ConsecutiveStatementsTests: XCTestCase { assertParse( """ // At the top level - var i, j : Int1️⃣ i = j2️⃣ j = i + var i, j : Int 1️⃣i = j j = i """, diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), - DiagnosticSpec( - locationMarker: "2️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), + message: "expected '=' in variable", + fixIts: ["insert '='"] + ) ], - applyFixIts: ["insert ';'"], fixedSource: """ // At the top level - var i, j : Int; i = j; j = i + var i, j : Int = i = j j = i """ ) } diff --git a/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift b/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift index c5a636adc18..2216e62022e 100644 --- a/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift +++ b/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift @@ -964,15 +964,16 @@ final class ForwardSlashRegexTests: XCTestCase { assertParse( """ do { - let 1️⃣/x/ + let 1️⃣/x/ } """, diagnostics: [ - DiagnosticSpec(message: "expected pattern in variable", fixIts: ["insert pattern"]) + DiagnosticSpec(message: "expected pattern in variable", fixIts: ["insert pattern"]), + DiagnosticSpec(message: "expected '=' in variable", fixIts: ["insert '='"]), ], fixedSource: """ do { - let <#pattern#>/x/ + let <#pattern#> = /x/ } """ ) diff --git a/Tests/SwiftParserTest/translated/RecoveryTests.swift b/Tests/SwiftParserTest/translated/RecoveryTests.swift index 62595356355..98980b0ec58 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -43,11 +43,11 @@ final class RecoveryTests: XCTestCase { ) } - func testRecovery7a() { + func testRecovery7() { assertParse( """ func useContainer() -> () { - var a : Containerℹ️] >4️⃣, b : Int + var a : Containerℹ️] >4️⃣, b : Int b = 5 // no-warning a.exists() } @@ -61,55 +61,8 @@ final class RecoveryTests: XCTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), - DiagnosticSpec( - locationMarker: "3️⃣", - message: "unexpected code 'this greater: >' in subscript" - ), - DiagnosticSpec( - locationMarker: "4️⃣", - message: "expected expression after operator", - fixIts: ["insert expression"] - ), - DiagnosticSpec( - locationMarker: "4️⃣", - message: "unexpected code in function" - ), - ], - applyFixIts: ["insert '>'", "insert newline", "insert expression"], - fixedSource: """ - func useContainer() -> () { - var a : Containera - type [skip this greater: >] > <#expression#>, b : Int - b = 5 // no-warning - a.exists() - } - """ - ) - } - - func testRecovery7b() { - assertParse( - """ - func useContainer() -> () { - var a : Containerℹ️] >4️⃣, b : Int - b = 5 // no-warning - a.exists() - } - """, - diagnostics: [ - DiagnosticSpec( - locationMarker: "1️⃣", - message: "expected '>' to end generic argument clause", - notes: [NoteSpec(message: "to match this opening '<'")], - fixIts: ["insert '>'"] - ), - DiagnosticSpec( - locationMarker: "2️⃣", - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] + message: "expected '=' in variable", + fixIts: ["insert '='"] ), DiagnosticSpec( locationMarker: "3️⃣", @@ -125,10 +78,10 @@ final class RecoveryTests: XCTestCase { message: "unexpected code in function" ), ], - applyFixIts: ["insert '>'", "insert ';'", "insert expression"], + applyFixIts: ["insert '>'", "insert expression"], fixedSource: """ func useContainer() -> () { - var a : Containera; type [skip this greater: >] > <#expression#>, b : Int + var a : Container a type [skip this greater: >] > <#expression#>, b : Int b = 5 // no-warning a.exists() }