You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+135-1Lines changed: 135 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,8 +31,11 @@ The codebase follows a clear separation between public API and implementation:
31
31
-`Sources/SpyableMacro/` - Macro implementation
32
32
-`Macro/SpyableMacro.swift` - Main macro entry point
33
33
-`Factories/` - Code generation logic split by concern
34
+
-`VariablePrefixFactory.swift` - Generates unique variable prefixes with polymorphism support
34
35
-`Extractors/` - Protocol syntax extraction
35
36
-`Extensions/` - SwiftSyntax utilities
37
+
-`Helpers/` - Utility classes
38
+
-`TypeSanitizer.swift` - Type name sanitization for variable naming
36
39
-`Diagnostics/` - Error handling
37
40
38
41
### Key Design Patterns
@@ -50,6 +53,131 @@ For a protocol `MyProtocol`, the macro generates `MyProtocolSpy` with:
50
53
-`{method}ReturnValue` - Stubbed return value (non-void methods)
51
54
-`{method}ThrowableError` - Error to throw (throwing methods)
52
55
56
+
## Polymorphism Support
57
+
58
+
Swift-Spyable automatically handles polymorphic functions (methods with the same name but different parameter or return types) by generating descriptive variable names that include type information. This ensures each method overload gets unique spy variables without naming conflicts.
59
+
60
+
### Implementation Architecture
61
+
62
+
The polymorphism detection system consists of three main components:
63
+
64
+
#### 1. VariablePrefixFactory
65
+
Located in `Sources/SpyableMacro/Factories/VariablePrefixFactory.swift`, this factory generates unique textual representations for function declarations:
66
+
67
+
-**Non-descriptive mode** (default): Uses function name + parameter names (e.g., `displayTextName`)
68
+
-**Descriptive mode** (polymorphism detected): Includes parameter and return types (e.g., `displayTextStringNameStringString`)
69
+
70
+
The factory automatically switches to descriptive mode when `SpyFactory` detects multiple functions with the same non-descriptive prefix.
71
+
72
+
#### 2. TypeSanitizer Helper
73
+
Located in `Sources/SpyableMacro/Helpers/TypeSanitizer.swift`, this utility sanitizes Swift type names for use in variable identifiers:
- Processes function attributes: `@escaping` becomes `escaping`, `@Sendable` becomes `Sendable`
78
+
- Sanitizes complex nested types like `[String: [Int]]` → `StringInt`
79
+
80
+
#### 3. SpyFactory Integration
81
+
The main `SpyFactory` orchestrates polymorphism detection by:
82
+
83
+
1. Pre-scanning all functions to build a frequency map of non-descriptive prefixes
84
+
2. Identifying functions that would have naming conflicts (frequency > 1)
85
+
3. Automatically enabling descriptive mode for conflicting functions
86
+
4. Generating unique variable names for each method overload
87
+
88
+
### Polymorphism Examples
89
+
90
+
Given these polymorphic methods:
91
+
```swift
92
+
protocolDisplayService {
93
+
funcdisplay(text: Int, name: String)
94
+
funcdisplay(text: String, name: String)
95
+
funcdisplay(text: String, name: String) ->String
96
+
}
97
+
```
98
+
99
+
Swift-Spyable generates:
100
+
```swift
101
+
classDisplayServiceSpy: DisplayService {
102
+
// For display(text: Int, name: String)
103
+
var displayTextIntNameStringCalled =false
104
+
var displayTextIntNameStringCallsCount =0
105
+
// ... other spy variables
106
+
107
+
// For display(text: String, name: String)
108
+
var displayTextStringNameStringCalled =false
109
+
var displayTextStringNameStringCallsCount =0
110
+
// ... other spy variables
111
+
112
+
// For display(text: String, name: String) -> String
113
+
var displayTextStringNameStringStringCalled =false
114
+
var displayTextStringNameStringStringCallsCount =0
115
+
var displayTextStringNameStringStringReturnValue: String!
116
+
// ... other spy variables
117
+
}
118
+
```
119
+
120
+
### Testing Strategy for Polymorphic Functions
121
+
122
+
#### Unit Tests
123
+
-`Tests/SpyableMacroTests/Factories/UT_VariablePrefixFactory.swift` - Comprehensive tests for variable prefix generation
124
+
-`Tests/SpyableMacroTests/Helpers/UT_TypeSanitizer.swift` - Type sanitization edge cases
125
+
126
+
#### Test Categories
127
+
1.**Basic Polymorphism**: Same function name with different parameter types
128
+
2.**Return Type Polymorphism**: Same signature with different return types
129
+
3.**Complex Type Handling**: Generics, optionals, collections, protocol compositions
130
+
4.**Edge Cases**: Function attributes (`@escaping`, `@Sendable`), nested optionals, custom types
131
+
132
+
#### Integration Testing
133
+
The Examples project provides real-world usage scenarios for polymorphic protocols, ensuring the generated spies compile and work correctly in practice.
Swift-Spyable supports method overloading (polymorphism) by generating unique spy properties for each method signature. This allows you to independently test and mock different overloads of the same method.
14
+
15
+
### How Polymorphism Works
16
+
17
+
When you have overloaded methods in your protocol:
18
+
19
+
```swift
20
+
@Spyable
21
+
protocolDataProcessor {
22
+
funccompute(value: String) ->String
23
+
funccompute(value: Int) ->String
24
+
funccompute(value: Bool) ->String
25
+
}
26
+
```
27
+
28
+
Swift-Spyable generates unique spy properties by incorporating the parameter and return types into the property names:
29
+
30
+
```swift
31
+
classDataProcessorSpy: DataProcessor {
32
+
// For compute(value: String) -> String
33
+
var computeValueStringStringCallsCount =0
34
+
var computeValueStringStringCalled: Bool { ... }
35
+
var computeValueStringStringReceivedValue: String?
1.**Independent Tracking**: Each method overload is tracked separately with its own call counts, arguments, and return values
125
+
2.**Type Safety**: The generated spy properties maintain type safety for parameters and return values
126
+
3.**Comprehensive Testing**: You can test complex scenarios where the same method name behaves differently based on parameter types
127
+
4.**Real-world Compatibility**: Supports common Swift patterns like async/await, throwing functions, and generic constraints
128
+
129
+
This polymorphism support makes Swift-Spyable suitable for testing complex protocols with overloaded methods, which is common in real-world Swift applications.
0 commit comments