Skip to content

Commit 31e351e

Browse files
authored
fix(patch): Optimize AST traversal in EquatableMacro (#19)
1 parent c9d85b0 commit 31e351e

File tree

1 file changed

+18
-40
lines changed

1 file changed

+18
-40
lines changed

Sources/EquatableMacros/EquatableMacro.swift

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public struct EquatableMacro: ExtensionMacro {
9999
"WKExtensionDelegateAdaptor"
100100
]
101101

102-
// swiftlint:disable:next cyclomatic_complexity function_body_length
102+
// swiftlint:disable:next function_body_length
103103
public static func expansion(
104104
of node: AttributeSyntax,
105105
attachedTo declaration: some DeclGroupSyntax,
@@ -118,53 +118,35 @@ public struct EquatableMacro: ExtensionMacro {
118118
}
119119

120120
// Extract stored properties
121-
let storedProperties = structDecl.memberBlock.members.compactMap { member -> (name: String, type: TypeSyntax?)? in
121+
var storedProperties: [(name: String, type: TypeSyntax?)] = []
122+
for member in structDecl.memberBlock.members {
122123
guard let varDecl = member.decl.as(VariableDeclSyntax.self),
123124
let binding = varDecl.bindings.first,
124125
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text,
125-
binding.accessorBlock == nil else {
126-
return nil
126+
binding.accessorBlock == nil,
127+
!varDecl.isStatic else {
128+
continue
127129
}
128130

129-
// Skip properties with SwiftUI attributes (like @State, @Binding, etc.) or if they are marked with @EqutableIgnored
130131
if Self.shouldSkip(varDecl) {
131-
return nil
132+
continue
132133
}
133134

134-
// Skip static properties
135-
if varDecl.isStatic {
136-
return nil
135+
if isMarkedWithEquatableIgnoredUnsafeClosure(varDecl) {
136+
continue
137137
}
138138

139-
// Skip computed properties
140-
let isStoredProperty = binding.accessorBlock == nil
139+
// Check if it's a closure that should trigger diagnostic
140+
let isClosureProperty = (binding.typeAnnotation?.type).map(isClosure) == true ||
141+
(binding.initializer?.value.is(ClosureExprSyntax.self) ?? false)
141142

142-
if !isStoredProperty {
143-
return nil
143+
if isClosureProperty {
144+
let diagnostic = Self.makeClosureDiagnostic(for: varDecl)
145+
context.diagnose(diagnostic)
146+
continue
144147
}
145148

146-
// if it's a closure marked with @EquatableIgnoredUnsafeClosure allow it but don't compare
147-
if isMarkedWithEquatableIgnoredUnsafeClosure(varDecl) {
148-
return nil
149-
} else {
150-
// If it's a closure and not marked with @EquatableIgnoredUnsafeClosure throw a diagnostic
151-
if let typeAnnotation = binding.typeAnnotation?.type {
152-
if isClosure(type: typeAnnotation) {
153-
let diagnostic = Self.makeClosureDiagnostic(for: varDecl)
154-
context.diagnose(diagnostic)
155-
return nil
156-
}
157-
} else if let initializer = binding.initializer?.value {
158-
// Check if the initializer is a closure expression
159-
if initializer.is(ClosureExprSyntax.self) {
160-
let diagnostic = Self.makeClosureDiagnostic(for: varDecl)
161-
context.diagnose(diagnostic)
162-
return nil
163-
}
164-
}
165-
}
166-
167-
return (name: identifier, type: binding.typeAnnotation?.type)
149+
storedProperties.append((name: identifier, type: binding.typeAnnotation?.type))
168150
}
169151

170152
// Sort properties: "id" first, then by type complexity
@@ -179,17 +161,14 @@ public struct EquatableMacro: ExtensionMacro {
179161
return []
180162
}
181163

182-
// Check if the type conforms to `Hashable`
164+
// If the type conforms to `Hashable`, always generate a corresponding hash function aligned with the `Equatable` implementation
183165
if structDecl.isHashable {
184-
// If the type conforms to `Hashable` we need to generate the `Hashable` conformance to match
185-
// the properties used in `Equatable` implementation
186166
guard let hashableExtensionSyntax = Self.generateHashableExtensionSyntax(
187167
sortedProperties: sortedProperties,
188168
type: type
189169
) else {
190170
return [extensionSyntax]
191171
}
192-
193172
return [extensionSyntax, hashableExtensionSyntax]
194173
} else {
195174
return [extensionSyntax]
@@ -244,7 +223,6 @@ extension EquatableMacro {
244223
if let attributeName = attribute.as(AttributeSyntax.self)?.attributeName.as(IdentifierTypeSyntax.self)?.name.text {
245224
return attributeName == "EquatableIgnoredUnsafeClosure"
246225
}
247-
248226
return false
249227
})
250228
}

0 commit comments

Comments
 (0)