Repository: apple/swift-syntax (swift)

Issue:
Bad diagnostic for generic parameter list on enum case
### Issue Kind

Bad Diagnostic Produced

### Source Code

```swift
enum Foo {
    case bar<T>(baz: T)
}
```
```
/Users/mac/Desktop/test.swift:106:13: error: consecutive declarations on a line must be separated by newline or ';' [bridged_error]
    case bar<T>(baz: T)
            ^
/Users/mac/Desktop/test.swift:106:13: note: insert newline [bridged_note]
    case bar<T>(baz: T)
            ^
/Users/mac/Desktop/test.swift:106:13: note: insert ';' [bridged_note]
    case bar<T>(baz: T)
            ^
            ;
/Users/mac/Desktop/test.swift:106:13: error: expected 'func' in function [bridged_error]
    case bar<T>(baz: T)
            ^
/Users/mac/Desktop/test.swift:106:13: note: insert 'func' [bridged_note]
    case bar<T>(baz: T)
            ^
            func 
/Users/mac/Desktop/test.swift:106:14: error: unexpected code 'T>' before parameter clause [bridged_error]
    case bar<T>(baz: T)
```

### Description

Twin of https://github.com/apple/swift/issues/69036.

Pull request description:
Fixes #2273

swift-syntax version of https://github.com/apple/swift/pull/69055

Patch:
diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift
index 7075fcb0d7e..6cd0a74cb34 100644
--- a/Sources/SwiftParser/Declarations.swift
+++ b/Sources/SwiftParser/Declarations.swift
@@ -808,6 +808,14 @@ extension Parser {
         let unexpectedPeriod = self.consume(if: .period)
         let (unexpectedBeforeName, name) = self.expectIdentifier(keywordRecovery: true)
 
+        let unexpectedGenericParameters: RawUnexpectedNodesSyntax?
+        if self.at(prefix: "<") {
+          let genericParameters = self.parseGenericParameters()
+          unexpectedGenericParameters = RawUnexpectedNodesSyntax([genericParameters], arena: self.arena)
+        } else {
+          unexpectedGenericParameters = nil
+        }
+
         let parameterClause: RawEnumCaseParameterClauseSyntax?
         if self.at(TokenSpec(.leftParen)) {
           parameterClause = self.parseParameterClause(RawEnumCaseParameterClauseSyntax.self) { parser in
@@ -836,6 +844,7 @@ extension Parser {
           RawEnumCaseElementSyntax(
             RawUnexpectedNodesSyntax(combining: unexpectedPeriod, unexpectedBeforeName, arena: self.arena),
             name: name,
+            unexpectedGenericParameters,
             parameterClause: parameterClause,
             rawValue: rawValue,
             trailingComma: keepGoing,
diff --git a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift
index 067ae580b55..ed1d63a6e75 100644
--- a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift
+++ b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift
@@ -1074,6 +1074,24 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
     return .visitChildren
   }
 
+  public override func visit(_ node: EnumCaseElementSyntax) -> SyntaxVisitorContinueKind {
+    if shouldSkip(node) {
+      return .skipChildren
+    }
+
+    if let unexpectedBetweenNameAndParameterClause = node.unexpectedBetweenNameAndParameterClause,
+      let genericParameter = unexpectedBetweenNameAndParameterClause.compactMap({ $0.as(GenericParameterClauseSyntax.self) }).only
+    {
+      addDiagnostic(
+        genericParameter,
+        .genericParamCantBeUsedInEnumCaseDecl,
+        handledNodes: [unexpectedBetweenNameAndParameterClause.id]
+      )
+    }
+
+    return .visitChildren
+  }
+
   public override func visit(_ node: IfConfigClauseSyntax) -> SyntaxVisitorContinueKind {
     if shouldSkip(node) {
       return .skipChildren
diff --git a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift
index 0f2f5916a04..75955a37219 100644
--- a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift
+++ b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift
@@ -167,6 +167,9 @@ extension DiagnosticMessage where Self == StaticParserError {
   public static var forbiddenInterpolatedString: Self {
     return .init("argument cannot be an interpolated string literal")
   }
+  public static var genericParamCantBeUsedInEnumCaseDecl: Self {
+    return .init("generic signature cannot be declared in enum 'case'")
+  }
   public static var initializerInPattern: Self {
     .init("unexpected initializer in pattern; did you mean to use '='?")
   }


Test patch:
diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift
index de2bd77be3e..ee24ab0d121 100644
--- a/Tests/SwiftParserTest/DeclarationTests.swift
+++ b/Tests/SwiftParserTest/DeclarationTests.swift
@@ -2946,4 +2946,117 @@ final class DeclarationTests: ParserTestCase {
       ]
     )
   }
+
+  // https://github.com/apple/swift-syntax/issues/2273
+  func testEnumCaseWithGenericParameter() {
+    assertParse(
+      """
+      enum Foo {
+        case foo1️⃣<T>(T)
+      }
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          locationMarker: "1️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        )
+      ]
+    )
+
+    assertParse(
+      """
+      enum Foo {
+        case bar1️⃣<T>(param: T)
+      }
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          locationMarker: "1️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        )
+      ]
+    )
+
+    assertParse(
+      """
+      enum Foo {
+        case baz1️⃣<T>
+      }
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          locationMarker: "1️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        )
+      ]
+    )
+
+    assertParse(
+      """
+      enum Foo {
+        case one, two1️⃣<T>
+      }
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          locationMarker: "1️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        )
+      ]
+    )
+
+    assertParse(
+      """
+      enum Foo {
+        case three1️⃣<T>, four
+      }
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          locationMarker: "1️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        )
+      ]
+    )
+
+    assertParse(
+      """
+      enum Foo {
+        case five1️⃣<T>(param: T), six
+      }
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          locationMarker: "1️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        )
+      ]
+    )
+
+    assertParse(
+      """
+      enum Foo {
+        case seven1️⃣<T>, eight2️⃣<U>
+      }
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          locationMarker: "1️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        ),
+        DiagnosticSpec(
+          locationMarker: "2️⃣",
+          message: "generic signature cannot be declared in enum 'case'"
+        ),
+      ]
+    )
+
+    assertParse(
+      """
+      enum Foo<T> {
+        case five(param: T), six
+      }
+      """
+    )
+  }
 }