Repository: pointfreeco/swift-case-paths (swift)

Issue:
Compiler Crash with generic called `Element`
**Describe the bug**
When you use a generic with the name `Element` within an enum with the `CasePathable` macro the compiler throws an error.

**To Reproduce**

To reproduce this error you can simply use a Generic with the name `Element` in a `CasePathable` enum

This causes a compiler error
```swift
import CasePaths

@CasePathable
enum Action<Element> {
   case elements(Element)
}
```

this on the other hand does not cause the compiler error
```swift
import CasePaths

@CasePathable
enum Action<ActionElement> {
   case elements(ActionElement)
}
```

It also happens if for example the parent type defines the generic
```swift
// fails
struct Parent<Element> {
    @CasePathable
    enum Action {
        case element(Element)
    }
}
// compiles just fine
struct Parent<ParentElement> {
    @CasePathable
    enum Action {
        case element(ParentElement)
    }
}
```

**Expected behavior**
Either for the `Element` variant to compile or for CasePathable to forbid using it with an error message.

**Environment**
  - swift-case-paths [1.5.1]
  - swift-syntax [600.0.0-prerelease-2024-06-12]
  - Xcode [15.4]
  - Swift [5.10]
  - OS: [iOS 17.5]

**Additional context**
To test this I simply created a new Xcode project in Xcode 15.4 added only `swift-case-paths` as a dependency and added the examples at the top.

<details><summary>Compiler Stacktrace</summary>
<p>

```
1.	Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
2.	Compiling with the current language version
3.	While evaluating request ASTLoweringRequest(Lowering AST to SIL for file "REDACTED")
4.	While silgen emitFunction SIL function "@$s19CasePathMinExample_6ActionO03AllA5PathsV8elements0aG003AnyaB0VyACyxGs010PartialKeyB0CyAG0A0VyAJGGGvg".
 for getter for elements (at @__swiftmacro_19CasePathMinExample_6Action0A8PathablefMm_.swift:8:16)
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  swift-frontend           0x00000001088a7f3c llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x00000001088a70f8 llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x00000001088a8544 SignalHandler(int) + 360
3  libsystem_platform.dylib 0x0000000184d13584 _sigtramp + 56
4  swift-frontend           0x00000001038db0f0 swift::ASTVisitor<(anonymous namespace)::StmtEmitter, void, void, void, void, void, void>::visit(swift::Stmt*) + 3604
5  swift-frontend           0x00000001038db0f0 swift::ASTVisitor<(anonymous namespace)::StmtEmitter, void, void, void, void, void, void>::visit(swift::Stmt*) + 3604
6  swift-frontend           0x00000001038db6f4 swift::ASTVisitor<(anonymous namespace)::StmtEmitter, void, void, void, void, void, void>::visit(swift::Stmt*) + 5144
7  swift-frontend           0x0000000103872538 swift::Lowering::SILGenFunction::emitFunction(swift::FuncDecl*) + 632
8  swift-frontend           0x00000001037c6e48 swift::Lowering::SILGenModule::emitFunctionDefinition(swift::SILDeclRef, swift::SILFunction*) + 8344
9  swift-frontend           0x00000001037c75bc swift::Lowering::SILGenModule::emitOrDelayFunction(swift::SILDeclRef) + 172
10 swift-frontend           0x00000001037c4d9c swift::Lowering::SILGenModule::emitFunction(swift::FuncDecl*) + 292
11 swift-frontend           0x00000001038ef988 void llvm::function_ref<void (swift::AccessorDecl*)>::callback_fn<(anonymous namespace)::SILGenType::visitAccessors(swift::AbstractStorageDecl*)::'lambda'(swift::AccessorDecl*)>(long, swift::AccessorDecl*) + 36
12 swift-frontend           0x00000001038ef918 (anonymous namespace)::SILGenType::visitAbstractStorageDecl(swift::AbstractStorageDecl*) + 364
13 swift-frontend           0x00000001038ef794 (anonymous namespace)::SILGenType::visitVarDecl(swift::VarDecl*) + 832
14 swift-frontend           0x00000001038eb6d8 (anonymous namespace)::SILGenType::emitType() + 188
15 swift-frontend           0x00000001038eb6c8 (anonymous namespace)::SILGenType::emitType() + 172
16 swift-frontend           0x00000001037c49b8 swift::ASTVisitor<swift::Lowering::SILGenModule, void, void, void, void, void, void>::visit(swift::Decl*) + 104
17 swift-frontend           0x00000001037cab54 swift::ASTLoweringRequest::evaluate(swift::Evaluator&, swift::ASTLoweringDescriptor) const + 1668
18 swift-frontend           0x00000001038d9f1c swift::SimpleRequest<swift::ASTLoweringRequest, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>> (swift::ASTLoweringDescriptor), (swift::RequestFlags)9>::evaluateRequest(swift::ASTLoweringRequest const&, swift::Evaluator&) + 196
19 swift-frontend           0x00000001037cdf44 llvm::Expected<swift::ASTLoweringRequest::OutputType> swift::Evaluator::getResultUncached<swift::ASTLoweringRequest>(swift::ASTLoweringRequest const&) + 584
20 swift-frontend           0x00000001031a5408 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 548
21 swift-frontend           0x00000001031a9694 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1448
22 swift-frontend           0x00000001031a76d0 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 4968
23 swift-frontend           0x0000000103136e8c swift::mainEntry(int, char const**) + 2612
24 dyld                     0x000000018495a0e0 start + 2360
``` 

</p>
</details>

Pull request:
Fixes #182.

Patch:
diff --git a/Package.resolved b/Package.resolved
index 007eb449..acd506a4 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -23,8 +23,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/pointfreeco/swift-macro-testing",
       "state" : {
-        "revision" : "5c4a1b9d7c23cd5c08ea50677d8e89080365cb00",
-        "version" : "0.4.0"
+        "revision" : "a35257b7e9ce44e92636447003a8eeefb77b145c",
+        "version" : "0.5.1"
       }
     },
     {
@@ -32,8 +32,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/pointfreeco/swift-snapshot-testing",
       "state" : {
-        "revision" : "625ccca8570773dd84a34ee51a81aa2bc5a4f97a",
-        "version" : "1.16.0"
+        "revision" : "c097f955b4e724690f0fc8ffb7a6d4b881c9c4e3",
+        "version" : "1.17.2"
       }
     },
     {
diff --git a/Sources/CasePaths/Macros.swift b/Sources/CasePaths/Macros.swift
index 255332b2..5957a883 100644
--- a/Sources/CasePaths/Macros.swift
+++ b/Sources/CasePaths/Macros.swift
@@ -26,7 +26,7 @@
 /// \UserAction.Cases.settings  // CasePath<UserAction, SettingsAction>
 /// ```
 @attached(extension, conformances: CasePathable, CasePathIterable)
-@attached(member, names: named(AllCasePaths), named(allCasePaths))
+@attached(member, names: named(AllCasePaths), named(allCasePaths), named(_$Element))
 public macro CasePathable() =
   #externalMacro(
     module: "CasePathsMacros", type: "CasePathableMacro"
diff --git a/Sources/CasePathsMacros/CasePathableMacro.swift b/Sources/CasePathsMacros/CasePathableMacro.swift
index 8c51d5c6..261441f7 100644
--- a/Sources/CasePathsMacros/CasePathableMacro.swift
+++ b/Sources/CasePathsMacros/CasePathableMacro.swift
@@ -98,7 +98,7 @@ extension CasePathableMacro: MemberMacro {
 
     let subscriptReturn = allCases.isEmpty ? #"\.never"# : #"return \.never"#
 
-    return [
+    var decls: [DeclSyntax] = [
       """
       public struct AllCasePaths: CasePaths.CasePathReflectable, Sendable, Sequence {
       public subscript(root: \(enumName)) -> CasePaths.PartialCaseKeyPath<\(enumName)> {
@@ -115,6 +115,15 @@ extension CasePathableMacro: MemberMacro {
       public static var allCasePaths: AllCasePaths { AllCasePaths() }
       """
     ]
+
+    let hasElementGeneric = enumDecl.genericParameterClause?.parameters
+      .contains { $0.name.text == "Element" }
+      ?? false
+    if hasElementGeneric {
+      decls.append("public typealias _$Element = Element")
+    }
+
+    return decls
   }
 
   static func generateCases(
@@ -170,7 +179,9 @@ extension CasePathableMacro: MemberMacro {
   ) -> [DeclSyntax] {
     decl.elements.map {
       let caseName = $0.name.trimmed
-      let associatedValueName = $0.trimmedTypeDescription
+      let associatedValueName = $0.trimmedTypeDescription == "Element"
+        ? "_$Element"
+        : $0.trimmedTypeDescription
       let hasPayload = $0.parameterClause.map { !$0.parameters.isEmpty } ?? false
       let embedNames: String
       let bindingNames: String


Test patch:
diff --git a/Tests/CasePathsMacrosTests/CasePathableMacroTests.swift b/Tests/CasePathsMacrosTests/CasePathableMacroTests.swift
index 3d13ce4f..3c2f568f 100644
--- a/Tests/CasePathsMacrosTests/CasePathableMacroTests.swift
+++ b/Tests/CasePathsMacrosTests/CasePathableMacroTests.swift
@@ -6,7 +6,7 @@ import XCTest
 final class CasePathableMacroTests: XCTestCase {
   override func invokeTest() {
     withMacroTesting(
-      // isRecording: true,
+      //record: .failed,
       macros: [CasePathableMacro.self]
     ) {
       super.invokeTest()
@@ -1215,4 +1215,53 @@ final class CasePathableMacroTests: XCTestCase {
       """#
     }
   }
+
+  func testElementGeneric() {
+    assertMacro {
+      """
+      @CasePathable enum Action<Element> {
+        case element(Element)
+      }
+      """
+    } expansion: {
+      #"""
+      enum Action<Element> {
+        case element(Element)
+
+        public struct AllCasePaths: CasePaths.CasePathReflectable, Sendable, Sequence {
+          public subscript(root: Action) -> CasePaths.PartialCaseKeyPath<Action> {
+            if root.is(\.element) {
+              return \.element
+            }
+            return \.never
+          }
+          public var element: CasePaths.AnyCasePath<Action, _$Element> {
+            CasePaths.AnyCasePath<Action, _$Element>(
+              embed: {
+                Action.element($0)
+              },
+              extract: {
+                guard case let .element(v0) = $0 else {
+                  return nil
+                }
+                return v0
+              }
+            )
+          }
+          public func makeIterator() -> IndexingIterator<[CasePaths.PartialCaseKeyPath<Action>]> {
+            var allCasePaths: [CasePaths.PartialCaseKeyPath<Action>] = []
+            allCasePaths.append(\.element)
+            return allCasePaths.makeIterator()
+          }
+        }
+        public static var allCasePaths: AllCasePaths { AllCasePaths() }
+
+        public typealias _$Element = Element
+      }
+
+      extension Action: CasePaths.CasePathable, CasePaths.CasePathIterable {
+      }
+      """#
+    }
+  }
 }
diff --git a/Tests/CasePathsTests/MacroTests.swift b/Tests/CasePathsTests/MacroTests.swift
index d9f670b6..192dab0b 100644
--- a/Tests/CasePathsTests/MacroTests.swift
+++ b/Tests/CasePathsTests/MacroTests.swift
@@ -14,3 +14,7 @@ private enum Comments {
 enum Action {
   case alert(Never)
 }
+
+@CasePathable enum EnumWithElementGeneric<Element> {
+  case element(Element)
+}