Repository: apple/swift-syntax (swift)

Issue:
Bad diagnostic for let foo: [Int] []
### Issue Kind

Bad Diagnostic Produced

### Source Code

```swift
let foo: [Int] []
```


### Description

This currently produces the following. We should be suggesting to add a `=` if a variable declaration is followed by an expression on the same line without a `=` in between.

```
1 │ let foo: [Int] []
  │               ╰─ error: consecutive statements on a line must be separated by ';'
```

rdar://108623515

Pull request:
When a variable declaration is followed by an expression on the same line, it should be suggested to insert an `equal` token.

Resolves #1610

Patch:
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
         }


Test patch:
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ℹ️<not 1️⃣a2️⃣ type [skip 3️⃣this greater: >] >4️⃣, b : Int
+        var a : Containerℹ️<not 1️⃣2️⃣a type [skip 3️⃣this greater: >] >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 : Container<not>a
-          type [skip this greater: >] > <#expression#>, b : Int
-          b = 5 // no-warning
-          a.exists()
-        }
-        """
-    )
-  }
-
-  func testRecovery7b() {
-    assertParse(
-      """
-      func useContainer() -> () {
-        var a : Containerℹ️<not 1️⃣a2️⃣ type [skip 3️⃣this greater: >] >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 : Container<not>a; type [skip this greater: >] > <#expression#>, b : Int
+          var a : Container<not> a type [skip this greater: >] > <#expression#>, b : Int
           b = 5 // no-warning
           a.exists()
         }