Skip to content

Commit da1cd10

Browse files
authored
Fix macro compiler bug in release mode. (#2827)
* Fix macro compiler bug in release mode. * few more tests
1 parent 115fb55 commit da1cd10

File tree

4 files changed

+169
-63
lines changed

4 files changed

+169
-63
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/ComposableArchitectureMacros/ReducerMacro.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ extension ReducerMacro: MemberMacro {
250250
var reducerScopes: [String] = []
251251
var storeCases: [String] = []
252252
var storeScopes: [String] = []
253+
var reducerTypeScopes: [String] = []
253254

254255
for enumCaseElement in enumCaseElements {
255256
let element = enumCaseElement.element
@@ -285,6 +286,11 @@ extension ReducerMacro: MemberMacro {
285286
return .\(name)(store.scope(state: \\.\(name), action: \\.\(name))!)
286287
"""
287288
)
289+
reducerTypeScopes.append(
290+
"""
291+
Scope<Self.State, Self.Action, \(type.trimmed)>
292+
"""
293+
)
288294
}
289295
} else {
290296
stateCaseDecls.append("case \(element.trimmedDescription)")
@@ -361,13 +367,30 @@ extension ReducerMacro: MemberMacro {
361367
)
362368
}
363369
if !hasBody {
370+
var staticVarBody = ""
371+
if reducerTypeScopes.isEmpty {
372+
staticVarBody = "EmptyReducer<Self.State, Self.Action>"
373+
} else if reducerTypeScopes.count == 1 {
374+
staticVarBody = reducerTypeScopes[0]
375+
} else {
376+
for _ in 1...(reducerTypeScopes.count - 1) {
377+
staticVarBody.append("ReducerBuilder<Self.State, Self.Action>._Sequence<")
378+
}
379+
staticVarBody.append(reducerTypeScopes[0])
380+
staticVarBody.append(", ")
381+
for type in reducerTypeScopes.dropFirst() {
382+
staticVarBody.append(type)
383+
staticVarBody.append(">, ")
384+
}
385+
staticVarBody.removeLast(2)
386+
}
387+
364388
decls.append(
365389
"""
366-
\(access)static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
367-
ComposableArchitecture.CombineReducers {
390+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
391+
\(access)static var body: \(raw: staticVarBody) {
368392
\(raw: reducerScopes.joined(separator: "\n"))
369393
}
370-
}
371394
"""
372395
)
373396
}

Tests/ComposableArchitectureMacrosTests/ReducerMacroTests.swift

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,9 @@
233233
234234
}
235235
236-
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
237-
ComposableArchitecture.CombineReducers {
236+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
237+
static var body: EmptyReducer<Self.State, Self.Action> {
238238
239-
}
240239
}
241240
242241
enum CaseScope {
@@ -260,6 +259,7 @@
260259
"""
261260
@Reducer
262261
enum Destination {
262+
case activity(Activity)
263263
case timeline(Timeline)
264264
case tweet(Tweet)
265265
case alert(AlertState<Alert>)
@@ -272,6 +272,7 @@
272272
} expansion: {
273273
#"""
274274
enum Destination {
275+
case activity(Activity)
275276
case timeline(Timeline)
276277
case tweet(Tweet)
277278
@ReducerCaseEphemeral
@@ -286,37 +287,44 @@
286287
@ObservableState
287288
enum State: ComposableArchitecture.CaseReducerState {
288289
typealias StateReducer = Destination
290+
case activity(Activity.State)
289291
case timeline(Timeline.State)
290292
case tweet(Tweet.State)
291293
case alert(AlertState<Alert>)
292294
}
293295
294296
@CasePathable
295297
enum Action {
298+
case activity(Activity.Action)
296299
case timeline(Timeline.Action)
297300
case tweet(Tweet.Action)
298301
case alert(AlertState<Alert>.Action)
299302
}
300303
301-
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
302-
ComposableArchitecture.CombineReducers {
303-
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
304-
Timeline()
305-
}
306-
ComposableArchitecture.Scope(state: \Self.State.Cases.tweet, action: \Self.Action.Cases.tweet) {
307-
Tweet()
308-
}
304+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
305+
static var body: ReducerBuilder<Self.State, Self.Action>._Sequence<ReducerBuilder<Self.State, Self.Action>._Sequence<Scope<Self.State, Self.Action, Activity>, Scope<Self.State, Self.Action, Timeline>>, Scope<Self.State, Self.Action, Tweet>> {
306+
ComposableArchitecture.Scope(state: \Self.State.Cases.activity, action: \Self.Action.Cases.activity) {
307+
Activity()
308+
}
309+
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
310+
Timeline()
311+
}
312+
ComposableArchitecture.Scope(state: \Self.State.Cases.tweet, action: \Self.Action.Cases.tweet) {
313+
Tweet()
309314
}
310315
}
311316
312317
enum CaseScope {
318+
case activity(ComposableArchitecture.StoreOf<Activity>)
313319
case timeline(ComposableArchitecture.StoreOf<Timeline>)
314320
case tweet(ComposableArchitecture.StoreOf<Tweet>)
315321
case alert(AlertState<Alert>)
316322
}
317323
318324
static func scope(_ store: ComposableArchitecture.Store<Self.State, Self.Action>) -> CaseScope {
319325
switch store.state {
326+
case .activity:
327+
return .activity(store.scope(state: \.activity, action: \.activity)!)
320328
case .timeline:
321329
return .timeline(store.scope(state: \.timeline, action: \.timeline)!)
322330
case .tweet:
@@ -333,6 +341,67 @@
333341
}
334342
}
335343

344+
func testEnum_TwoCases() {
345+
assertMacro {
346+
"""
347+
@Reducer
348+
enum Destination {
349+
case activity(Activity)
350+
case timeline(Timeline)
351+
}
352+
"""
353+
} expansion: {
354+
#"""
355+
enum Destination {
356+
case activity(Activity)
357+
case timeline(Timeline)
358+
359+
@CasePathable
360+
@dynamicMemberLookup
361+
@ObservableState
362+
enum State: ComposableArchitecture.CaseReducerState {
363+
typealias StateReducer = Destination
364+
case activity(Activity.State)
365+
case timeline(Timeline.State)
366+
}
367+
368+
@CasePathable
369+
enum Action {
370+
case activity(Activity.Action)
371+
case timeline(Timeline.Action)
372+
}
373+
374+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
375+
static var body: ReducerBuilder<Self.State, Self.Action>._Sequence<Scope<Self.State, Self.Action, Activity>, Scope<Self.State, Self.Action, Timeline>> {
376+
ComposableArchitecture.Scope(state: \Self.State.Cases.activity, action: \Self.Action.Cases.activity) {
377+
Activity()
378+
}
379+
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
380+
Timeline()
381+
}
382+
}
383+
384+
enum CaseScope {
385+
case activity(ComposableArchitecture.StoreOf<Activity>)
386+
case timeline(ComposableArchitecture.StoreOf<Timeline>)
387+
}
388+
389+
static func scope(_ store: ComposableArchitecture.Store<Self.State, Self.Action>) -> CaseScope {
390+
switch store.state {
391+
case .activity:
392+
return .activity(store.scope(state: \.activity, action: \.activity)!)
393+
case .timeline:
394+
return .timeline(store.scope(state: \.timeline, action: \.timeline)!)
395+
}
396+
}
397+
}
398+
399+
extension Destination: ComposableArchitecture.CaseReducer, ComposableArchitecture.Reducer {
400+
}
401+
"""#
402+
}
403+
}
404+
336405
func testEnum_CaseIgnored() {
337406
assertMacro {
338407
"""
@@ -364,11 +433,10 @@
364433
case timeline(Timeline.Action)
365434
}
366435
367-
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
368-
ComposableArchitecture.CombineReducers {
369-
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
370-
Timeline()
371-
}
436+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
437+
static var body: Scope<Self.State, Self.Action, Timeline> {
438+
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
439+
Timeline()
372440
}
373441
}
374442
@@ -429,10 +497,9 @@
429497
case dialog(ConfirmationDialogState<Dialog>.Action)
430498
}
431499
432-
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
433-
ComposableArchitecture.CombineReducers {
500+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
501+
static var body: EmptyReducer<Self.State, Self.Action> {
434502
435-
}
436503
}
437504
438505
enum CaseScope {
@@ -493,17 +560,16 @@
493560
case sheet(Counter.Action)
494561
}
495562
496-
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
497-
ComposableArchitecture.CombineReducers {
498-
ComposableArchitecture.Scope(state: \Self.State.Cases.drillDown, action: \Self.Action.Cases.drillDown) {
499-
Counter()
500-
}
501-
ComposableArchitecture.Scope(state: \Self.State.Cases.popover, action: \Self.Action.Cases.popover) {
502-
Counter()
503-
}
504-
ComposableArchitecture.Scope(state: \Self.State.Cases.sheet, action: \Self.Action.Cases.sheet) {
505-
Counter()
506-
}
563+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
564+
static var body: ReducerBuilder<Self.State, Self.Action>._Sequence<ReducerBuilder<Self.State, Self.Action>._Sequence<Scope<Self.State, Self.Action, Counter>, Scope<Self.State, Self.Action, Counter>>, Scope<Self.State, Self.Action, Counter>> {
565+
ComposableArchitecture.Scope(state: \Self.State.Cases.drillDown, action: \Self.Action.Cases.drillDown) {
566+
Counter()
567+
}
568+
ComposableArchitecture.Scope(state: \Self.State.Cases.popover, action: \Self.Action.Cases.popover) {
569+
Counter()
570+
}
571+
ComposableArchitecture.Scope(state: \Self.State.Cases.sheet, action: \Self.Action.Cases.sheet) {
572+
Counter()
507573
}
508574
}
509575
@@ -557,11 +623,10 @@
557623
case feature(Nested.Feature.Action)
558624
}
559625
560-
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
561-
ComposableArchitecture.CombineReducers {
562-
ComposableArchitecture.Scope(state: \Self.State.Cases.feature, action: \Self.Action.Cases.feature) {
563-
Nested.Feature()
564-
}
626+
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
627+
static var body: Scope<Self.State, Self.Action, Nested.Feature> {
628+
ComposableArchitecture.Scope(state: \Self.State.Cases.feature, action: \Self.Action.Cases.feature) {
629+
Nested.Feature()
565630
}
566631
}
567632

Tests/ComposableArchitectureTests/MacroTests.swift

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,46 @@
5555
}
5656
}
5757

58-
// enum TestEnumReducer_Basics {
59-
// @Reducer struct Feature {}
60-
// @Reducer
61-
// enum Destination {
62-
// case feature(Feature)
63-
// }
64-
// }
58+
enum TestEnumReducer_Basics {
59+
@Reducer struct Feature {}
60+
@Reducer
61+
enum Destination1 {
62+
case feature1(Feature)
63+
}
64+
@Reducer
65+
enum Destination2 {
66+
case feature1(Feature)
67+
case feature2(Feature)
68+
}
69+
@Reducer
70+
enum Destination3 {
71+
case feature1(Feature)
72+
case feature2(Feature)
73+
case feature3(Feature)
74+
}
75+
@Reducer
76+
enum Destination4 {
77+
case feature1(Feature)
78+
case feature2(Feature)
79+
case feature3(Feature)
80+
case feature4(Feature)
81+
}
82+
}
6583

66-
// enum TestEnumReducer_SynthesizedConformances {
67-
// @Reducer
68-
// struct Feature {
69-
// }
70-
// @Reducer(
71-
// state: .codable, .decodable, .encodable, .equatable, .hashable, .sendable,
72-
// action: .equatable, .hashable, .sendable
73-
// )
74-
// enum Destination {
75-
// case feature(Feature)
76-
// }
77-
// func stateRequirements(_: some Codable & Equatable & Hashable & Sendable) {}
78-
// func actionRequirements(_: some Equatable & Hashable & Sendable) {}
79-
// func givenState(_ state: Destination.State) { stateRequirements(state) }
80-
// func givenAction(_ action: Destination.Action) { actionRequirements(action) }
81-
// }
84+
enum TestEnumReducer_SynthesizedConformances {
85+
@Reducer
86+
struct Feature {
87+
}
88+
@Reducer(
89+
state: .codable, .decodable, .encodable, .equatable, .hashable, .sendable,
90+
action: .equatable, .hashable, .sendable
91+
)
92+
enum Destination {
93+
case feature(Feature)
94+
}
95+
func stateRequirements(_: some Codable & Equatable & Hashable & Sendable) {}
96+
func actionRequirements(_: some Equatable & Hashable & Sendable) {}
97+
func givenState(_ state: Destination.State) { stateRequirements(state) }
98+
func givenAction(_ action: Destination.Action) { actionRequirements(action) }
99+
}
82100
#endif

0 commit comments

Comments
 (0)