Skip to content

Commit 1785fa0

Browse files
authored
Support optional binding implicit nil initializing (#18)
1 parent 88e3937 commit 1785fa0

File tree

3 files changed

+175
-36
lines changed

3 files changed

+175
-36
lines changed

Sources/StateStruct/Source.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ public macro COWTrackingProperty() =
2929

3030
#if DEBUG
3131

32+
@Tracking
33+
struct OptinalPropertyState {
34+
35+
var name: String
36+
var stored_1: Int?
37+
38+
init() {
39+
self.name = ""
40+
}
41+
42+
}
43+
3244
@Tracking
3345
struct LetState {
3446

@@ -90,6 +102,8 @@ public macro COWTrackingProperty() =
90102

91103
var stored_2: Int
92104

105+
var stored_3: Int?
106+
93107
var name = ""
94108

95109
init(stored: Int) {

Sources/StateStructMacros/COWTrackingPropertyMacro.swift

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import SwiftCompilerPlugin
22
import SwiftSyntax
33
import SwiftSyntaxBuilder
4-
import SwiftSyntaxMacros
54
import SwiftSyntaxMacroExpansion
5+
import SwiftSyntaxMacros
66

77
public struct COWTrackingPropertyMacro {
88

@@ -44,15 +44,34 @@ extension COWTrackingPropertyMacro: PeerMacro {
4444
var _variableDecl = variableDecl.trimmed
4545
_variableDecl.attributes = [.init(.init(stringLiteral: "@TrackingIgnored"))]
4646

47-
_variableDecl = _variableDecl
48-
.renamingIdentifier(with: "_backing_")
49-
.modifyingTypeAnnotation({ type in
50-
return "_BackingStorage<\(type.trimmed)>"
51-
})
52-
.modifyingInit({ initializer in
53-
return .init(value: "_BackingStorage.init(\(initializer.value))" as ExprSyntax)
54-
})
55-
47+
if variableDecl.isOptional {
48+
_variableDecl =
49+
_variableDecl
50+
.renamingIdentifier(with: "_backing_")
51+
.modifyingTypeAnnotation({ type in
52+
return "_BackingStorage<\(type.trimmed)>"
53+
})
54+
55+
_variableDecl = _variableDecl.with(
56+
\.bindings,
57+
.init(
58+
_variableDecl.bindings.map { binding in
59+
binding.with(\.initializer, .init(value: "_BackingStorage.init(nil)" as ExprSyntax))
60+
})
61+
)
62+
63+
} else {
64+
_variableDecl =
65+
_variableDecl
66+
.renamingIdentifier(with: "_backing_")
67+
.modifyingTypeAnnotation({ type in
68+
return "_BackingStorage<\(type.trimmed)>"
69+
})
70+
.modifyingInit({ initializer in
71+
return .init(value: "_BackingStorage.init(\(initializer.value))" as ExprSyntax)
72+
})
73+
}
74+
5675
newMembers.append(DeclSyntax(_variableDecl))
5776

5877
return newMembers
@@ -88,7 +107,7 @@ extension COWTrackingPropertyMacro: AccessorMacro {
88107
}
89108
"""
90109
)
91-
110+
92111
let readAccessor = AccessorDeclSyntax(
93112
"""
94113
_read {
@@ -113,7 +132,7 @@ extension COWTrackingPropertyMacro: AccessorMacro {
113132
} else {
114133
\(raw: backingName).value = newValue
115134
}
116-
135+
117136
}
118137
"""
119138
)
@@ -132,17 +151,19 @@ extension COWTrackingPropertyMacro: AccessorMacro {
132151
}
133152
"""
134153
)
135-
154+
136155
var accessors: [AccessorDeclSyntax] = []
137-
156+
157+
if binding.initializer == nil {
158+
accessors.append(initAccessor)
159+
}
160+
138161
accessors.append(readAccessor)
162+
139163
if !isConstant {
140-
accessors.append(setAccessor)
164+
accessors.append(setAccessor)
141165
accessors.append(modifyAccessor)
142166
}
143-
if binding.initializer == nil {
144-
accessors.append(initAccessor)
145-
}
146167

147168
return accessors
148169

@@ -153,28 +174,28 @@ extension COWTrackingPropertyMacro: AccessorMacro {
153174
extension VariableDeclSyntax {
154175
func renamingIdentifier(with newName: String) -> VariableDeclSyntax {
155176
let newBindings = self.bindings.map { binding -> PatternBindingSyntax in
156-
177+
157178
if let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) {
158-
179+
159180
let propertyName = identifierPattern.identifier.text
160-
181+
161182
let newIdentifierPattern = identifierPattern.with(
162183
\.identifier, "\(raw: newName)\(raw: propertyName)")
163184
return binding.with(\.pattern, .init(newIdentifierPattern))
164185
}
165186
return binding
166187
}
167-
188+
168189
return self.with(\.bindings, .init(newBindings))
169190
}
170191
}
171192

172193
extension VariableDeclSyntax {
173194
func withPrivateModifier() -> VariableDeclSyntax {
174-
195+
175196
let privateModifier = DeclModifierSyntax.init(
176197
name: .keyword(.private), trailingTrivia: .spaces(1))
177-
198+
178199
var modifiers = self.modifiers
179200
if modifiers.contains(where: { $0.name.tokenKind == .keyword(.private) }) {
180201
return self
@@ -185,7 +206,15 @@ extension VariableDeclSyntax {
185206
}
186207

187208
extension VariableDeclSyntax {
188-
209+
210+
var isOptional: Bool {
211+
212+
return self.bindings.contains(where: {
213+
$0.typeAnnotation?.type.is(OptionalTypeSyntax.self) ?? false
214+
})
215+
216+
}
217+
189218
func modifyingTypeAnnotation(_ modifier: (TypeSyntax) -> TypeSyntax) -> VariableDeclSyntax {
190219
let newBindings = self.bindings.map { binding -> PatternBindingSyntax in
191220
if let typeAnnotation = binding.typeAnnotation {
@@ -195,21 +224,23 @@ extension VariableDeclSyntax {
195224
}
196225
return binding
197226
}
198-
227+
199228
return self.with(\.bindings, .init(newBindings))
200229
}
201-
202-
func modifyingInit(_ modifier: (InitializerClauseSyntax) -> InitializerClauseSyntax) -> VariableDeclSyntax {
203-
204-
let newBindings = self.bindings.map { binding -> PatternBindingSyntax in
230+
231+
func modifyingInit(_ modifier: (InitializerClauseSyntax) -> InitializerClauseSyntax)
232+
-> VariableDeclSyntax
233+
{
234+
235+
let newBindings = self.bindings.map { binding -> PatternBindingSyntax in
205236
if let initializer = binding.initializer {
206237
let newInitializer = modifier(initializer)
207238
return binding.with(\.initializer, newInitializer)
208239
}
209240
return binding
210241
}
211-
242+
212243
return self.with(\.bindings, .init(newBindings))
213244
}
214-
245+
215246
}

Tests/StateStructMacroTests/COWTrackingProperyMacroTests.swift

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,113 @@ final class COWTrackingProperyMacroTests: XCTestCase {
1717
super.invokeTest()
1818
}
1919
}
20-
20+
21+
func test_optional_init() {
22+
assertMacro {
23+
"""
24+
struct OptinalPropertyState {
25+
26+
@COWTrackingProperty
27+
var stored_1: Int?
28+
29+
init() {
30+
}
31+
32+
}
33+
"""
34+
} expansion: {
35+
"""
36+
struct OptinalPropertyState {
37+
38+
var stored_1: Int? {
39+
40+
@storageRestrictions(initializes: _backing_stored_1)
41+
42+
init(initialValue) {
43+
44+
_backing_stored_1 = .init(initialValue)
45+
46+
}
47+
48+
_read {
49+
50+
(_backing_stored_1.value as? TrackingObject)?._tracking_context.path = _tracking_context.path?.pushed(.init("stored_1"))
51+
52+
_Tracking._tracking_modifyStorage {
53+
54+
$0.accessorRead(path: _tracking_context.path?.pushed(.init("stored_1")))
55+
56+
}
57+
58+
yield _backing_stored_1.value
59+
60+
}
61+
62+
set {
63+
64+
(_backing_stored_1.value as? TrackingObject)?._tracking_context.path = _tracking_context.path?.pushed(.init("stored_1"))
65+
66+
_Tracking._tracking_modifyStorage {
67+
68+
$0.accessorSet(path: _tracking_context.path?.pushed(.init("stored_1")))
69+
70+
}
71+
72+
if !isKnownUniquelyReferenced(&_backing_stored_1) {
73+
74+
_backing_stored_1 = .init(newValue)
75+
76+
} else {
77+
78+
_backing_stored_1.value = newValue
79+
80+
}
81+
82+
83+
}
84+
85+
_modify {
86+
87+
(_backing_stored_1.value as? TrackingObject)?._tracking_context.path = _tracking_context.path?.pushed(.init("stored_1"))
88+
89+
_Tracking._tracking_modifyStorage {
90+
91+
$0.accessorModify(path: _tracking_context.path?.pushed(.init("stored_1")))
92+
93+
}
94+
95+
if !isKnownUniquelyReferenced(&_backing_stored_1) {
96+
97+
_backing_stored_1 = .init(_backing_stored_1.value)
98+
99+
}
100+
101+
yield &_backing_stored_1.value
102+
103+
}
104+
}
105+
var _backing_stored_1: _BackingStorage<Int?> = _BackingStorage.init(nil)
106+
107+
init() {
108+
}
109+
110+
}
111+
"""
112+
}
113+
}
114+
21115
func test_macro() {
22116

23117
assertMacro {
24118
"""
25119
struct MyState {
26-
120+
27121
@COWTrackingProperty
28122
private var stored_0: Int = 18
29-
123+
30124
@COWTrackingProperty
31125
public var stored_1: Int = 18
32-
126+
33127
func compute() {
34128
}
35129
}

0 commit comments

Comments
 (0)