Skip to content

Commit

Permalink
Fix macro compiler bug in release mode. (#2827)
Browse files Browse the repository at this point in the history
* Fix macro compiler bug in release mode.

* few more tests
  • Loading branch information
mbrandonw authored Feb 19, 2024
1 parent 115fb55 commit da1cd10
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 63 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-custom-dump",
"state" : {
"revision" : "aedcf6f4cd486ccef5b312ccac85d4b3f6e58605",
"version" : "1.1.2"
"revision" : "6ea3b1b6a4957806d72030a32360d4bcb155a0d2",
"version" : "1.2.0"
}
},
{
Expand Down
29 changes: 26 additions & 3 deletions Sources/ComposableArchitectureMacros/ReducerMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ extension ReducerMacro: MemberMacro {
var reducerScopes: [String] = []
var storeCases: [String] = []
var storeScopes: [String] = []
var reducerTypeScopes: [String] = []

for enumCaseElement in enumCaseElements {
let element = enumCaseElement.element
Expand Down Expand Up @@ -285,6 +286,11 @@ extension ReducerMacro: MemberMacro {
return .\(name)(store.scope(state: \\.\(name), action: \\.\(name))!)
"""
)
reducerTypeScopes.append(
"""
Scope<Self.State, Self.Action, \(type.trimmed)>
"""
)
}
} else {
stateCaseDecls.append("case \(element.trimmedDescription)")
Expand Down Expand Up @@ -361,13 +367,30 @@ extension ReducerMacro: MemberMacro {
)
}
if !hasBody {
var staticVarBody = ""
if reducerTypeScopes.isEmpty {
staticVarBody = "EmptyReducer<Self.State, Self.Action>"
} else if reducerTypeScopes.count == 1 {
staticVarBody = reducerTypeScopes[0]
} else {
for _ in 1...(reducerTypeScopes.count - 1) {
staticVarBody.append("ReducerBuilder<Self.State, Self.Action>._Sequence<")
}
staticVarBody.append(reducerTypeScopes[0])
staticVarBody.append(", ")
for type in reducerTypeScopes.dropFirst() {
staticVarBody.append(type)
staticVarBody.append(">, ")
}
staticVarBody.removeLast(2)
}

decls.append(
"""
\(access)static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
ComposableArchitecture.CombineReducers {
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
\(access)static var body: \(raw: staticVarBody) {
\(raw: reducerScopes.joined(separator: "\n"))
}
}
"""
)
}
Expand Down
135 changes: 100 additions & 35 deletions Tests/ComposableArchitectureMacrosTests/ReducerMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,9 @@
}
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
ComposableArchitecture.CombineReducers {
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
static var body: EmptyReducer<Self.State, Self.Action> {
}
}
enum CaseScope {
Expand All @@ -260,6 +259,7 @@
"""
@Reducer
enum Destination {
case activity(Activity)
case timeline(Timeline)
case tweet(Tweet)
case alert(AlertState<Alert>)
Expand All @@ -272,6 +272,7 @@
} expansion: {
#"""
enum Destination {
case activity(Activity)
case timeline(Timeline)
case tweet(Tweet)
@ReducerCaseEphemeral
Expand All @@ -286,37 +287,44 @@
@ObservableState
enum State: ComposableArchitecture.CaseReducerState {
typealias StateReducer = Destination
case activity(Activity.State)
case timeline(Timeline.State)
case tweet(Tweet.State)
case alert(AlertState<Alert>)
}
@CasePathable
enum Action {
case activity(Activity.Action)
case timeline(Timeline.Action)
case tweet(Tweet.Action)
case alert(AlertState<Alert>.Action)
}
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
ComposableArchitecture.CombineReducers {
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
Timeline()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.tweet, action: \Self.Action.Cases.tweet) {
Tweet()
}
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
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>> {
ComposableArchitecture.Scope(state: \Self.State.Cases.activity, action: \Self.Action.Cases.activity) {
Activity()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
Timeline()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.tweet, action: \Self.Action.Cases.tweet) {
Tweet()
}
}
enum CaseScope {
case activity(ComposableArchitecture.StoreOf<Activity>)
case timeline(ComposableArchitecture.StoreOf<Timeline>)
case tweet(ComposableArchitecture.StoreOf<Tweet>)
case alert(AlertState<Alert>)
}
static func scope(_ store: ComposableArchitecture.Store<Self.State, Self.Action>) -> CaseScope {
switch store.state {
case .activity:
return .activity(store.scope(state: \.activity, action: \.activity)!)
case .timeline:
return .timeline(store.scope(state: \.timeline, action: \.timeline)!)
case .tweet:
Expand All @@ -333,6 +341,67 @@
}
}

func testEnum_TwoCases() {
assertMacro {
"""
@Reducer
enum Destination {
case activity(Activity)
case timeline(Timeline)
}
"""
} expansion: {
#"""
enum Destination {
case activity(Activity)
case timeline(Timeline)
@CasePathable
@dynamicMemberLookup
@ObservableState
enum State: ComposableArchitecture.CaseReducerState {
typealias StateReducer = Destination
case activity(Activity.State)
case timeline(Timeline.State)
}
@CasePathable
enum Action {
case activity(Activity.Action)
case timeline(Timeline.Action)
}
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
static var body: ReducerBuilder<Self.State, Self.Action>._Sequence<Scope<Self.State, Self.Action, Activity>, Scope<Self.State, Self.Action, Timeline>> {
ComposableArchitecture.Scope(state: \Self.State.Cases.activity, action: \Self.Action.Cases.activity) {
Activity()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
Timeline()
}
}
enum CaseScope {
case activity(ComposableArchitecture.StoreOf<Activity>)
case timeline(ComposableArchitecture.StoreOf<Timeline>)
}
static func scope(_ store: ComposableArchitecture.Store<Self.State, Self.Action>) -> CaseScope {
switch store.state {
case .activity:
return .activity(store.scope(state: \.activity, action: \.activity)!)
case .timeline:
return .timeline(store.scope(state: \.timeline, action: \.timeline)!)
}
}
}
extension Destination: ComposableArchitecture.CaseReducer, ComposableArchitecture.Reducer {
}
"""#
}
}

func testEnum_CaseIgnored() {
assertMacro {
"""
Expand Down Expand Up @@ -364,11 +433,10 @@
case timeline(Timeline.Action)
}
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
ComposableArchitecture.CombineReducers {
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
Timeline()
}
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
static var body: Scope<Self.State, Self.Action, Timeline> {
ComposableArchitecture.Scope(state: \Self.State.Cases.timeline, action: \Self.Action.Cases.timeline) {
Timeline()
}
}
Expand Down Expand Up @@ -429,10 +497,9 @@
case dialog(ConfirmationDialogState<Dialog>.Action)
}
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
ComposableArchitecture.CombineReducers {
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
static var body: EmptyReducer<Self.State, Self.Action> {
}
}
enum CaseScope {
Expand Down Expand Up @@ -493,17 +560,16 @@
case sheet(Counter.Action)
}
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
ComposableArchitecture.CombineReducers {
ComposableArchitecture.Scope(state: \Self.State.Cases.drillDown, action: \Self.Action.Cases.drillDown) {
Counter()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.popover, action: \Self.Action.Cases.popover) {
Counter()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.sheet, action: \Self.Action.Cases.sheet) {
Counter()
}
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
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>> {
ComposableArchitecture.Scope(state: \Self.State.Cases.drillDown, action: \Self.Action.Cases.drillDown) {
Counter()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.popover, action: \Self.Action.Cases.popover) {
Counter()
}
ComposableArchitecture.Scope(state: \Self.State.Cases.sheet, action: \Self.Action.Cases.sheet) {
Counter()
}
}
Expand Down Expand Up @@ -557,11 +623,10 @@
case feature(Nested.Feature.Action)
}
static var body: some ComposableArchitecture.Reducer<Self.State, Self.Action> {
ComposableArchitecture.CombineReducers {
ComposableArchitecture.Scope(state: \Self.State.Cases.feature, action: \Self.Action.Cases.feature) {
Nested.Feature()
}
@ComposableArchitecture.ReducerBuilder<Self.State, Self.Action>
static var body: Scope<Self.State, Self.Action, Nested.Feature> {
ComposableArchitecture.Scope(state: \Self.State.Cases.feature, action: \Self.Action.Cases.feature) {
Nested.Feature()
}
}
Expand Down
64 changes: 41 additions & 23 deletions Tests/ComposableArchitectureTests/MacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,46 @@
}
}

// enum TestEnumReducer_Basics {
// @Reducer struct Feature {}
// @Reducer
// enum Destination {
// case feature(Feature)
// }
// }
enum TestEnumReducer_Basics {
@Reducer struct Feature {}
@Reducer
enum Destination1 {
case feature1(Feature)
}
@Reducer
enum Destination2 {
case feature1(Feature)
case feature2(Feature)
}
@Reducer
enum Destination3 {
case feature1(Feature)
case feature2(Feature)
case feature3(Feature)
}
@Reducer
enum Destination4 {
case feature1(Feature)
case feature2(Feature)
case feature3(Feature)
case feature4(Feature)
}
}

// enum TestEnumReducer_SynthesizedConformances {
// @Reducer
// struct Feature {
// }
// @Reducer(
// state: .codable, .decodable, .encodable, .equatable, .hashable, .sendable,
// action: .equatable, .hashable, .sendable
// )
// enum Destination {
// case feature(Feature)
// }
// func stateRequirements(_: some Codable & Equatable & Hashable & Sendable) {}
// func actionRequirements(_: some Equatable & Hashable & Sendable) {}
// func givenState(_ state: Destination.State) { stateRequirements(state) }
// func givenAction(_ action: Destination.Action) { actionRequirements(action) }
// }
enum TestEnumReducer_SynthesizedConformances {
@Reducer
struct Feature {
}
@Reducer(
state: .codable, .decodable, .encodable, .equatable, .hashable, .sendable,
action: .equatable, .hashable, .sendable
)
enum Destination {
case feature(Feature)
}
func stateRequirements(_: some Codable & Equatable & Hashable & Sendable) {}
func actionRequirements(_: some Equatable & Hashable & Sendable) {}
func givenState(_ state: Destination.State) { stateRequirements(state) }
func givenAction(_ action: Destination.Action) { actionRequirements(action) }
}
#endif

0 comments on commit da1cd10

Please sign in to comment.