Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,31 @@ public struct ReducerMacro: MemberMacro {
var viewAction: EnumDeclSyntax?
var reducerAction: EnumDeclSyntax?

structDecl.memberBlock.members
.compactMap { $0.decl.as(EnumDeclSyntax.self) }
.forEach { enumDecl in
switch enumDecl.name.text {
case "ViewAction":
viewAction = enumDecl
case "ReducerAction":
reducerAction = enumDecl
default: return
try structDecl.memberBlock.members
.forEach {
guard let hasName = $0.hasName else {
return
}

let name = hasName.name.text

if let enumDecl = $0.decl.as(EnumDeclSyntax.self) {
switch name {
case "ViewAction":
viewAction = enumDecl
case "ReducerAction":
reducerAction = enumDecl
default: return
}
} else if name == "ViewAction" || name == "ReducerAction" {
// ViewAction and ReducerAction must be enum
throw DiagnosticsError(
diagnostics: [
ReducerMacroDiagnostic
.actionMustBeEnum(actionName: name)
.diagnose(at: hasName)
]
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum ReducerMacroDiagnostic {
case notStruct
case duplicatedCase
case noMatchInheritanceClause
case actionMustBeEnum(actionName: String)
}

extension ReducerMacroDiagnostic: DiagnosticMessage {
Expand All @@ -31,6 +32,9 @@ extension ReducerMacroDiagnostic: DiagnosticMessage {

case .noMatchInheritanceClause:
"The inheritance clause must match between ViewAction and ReducerAction"

case .actionMustBeEnum(let actionName):
"\(actionName) must be enum"
}
}

Expand All @@ -52,6 +56,9 @@ extension ReducerMacroDiagnostic: DiagnosticMessage {

case .noMatchInheritanceClause:
MessageID(domain: "ReducerMacroDiagnostic", id: "noMatchInheritanceClause")

case .actionMustBeEnum:
MessageID(domain: "ReducerMacroDiagnostic", id: "actionMustBeEnum")
}
}
}
86 changes: 86 additions & 0 deletions Tests/SimplexArchitectureTests/ReducerMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,46 @@ final class ReducerMacroTests: XCTestCase {
}
}

func testActionMustBeEnum() {
assertMacro {
"""
@Reducer
public struct MyReducer {
public struct ViewAction {}
}
"""
} matches: {
"""
@Reducer
public struct MyReducer {
public struct ViewAction {}
┬──────────────────────────
╰─ 🛑 ViewAction must be enum
}
"""
}

assertMacro {
"""
@Reducer
public struct MyReducer {
public enum ViewAction {}
public struct ReducerAction {}
}
"""
} matches: {
"""
@Reducer
public struct MyReducer {
public enum ViewAction {}
public struct ReducerAction {}
┬─────────────────────────────
╰─ 🛑 ReducerAction must be enum
}
"""
}
}

func testNoMatchInheritanceClause() {
assertMacro {
"""
Expand Down Expand Up @@ -630,5 +670,51 @@ final class ReducerMacroTests: XCTestCase {
}
"""
}

assertMacro {
"""
@Reducer
public struct MyReducer {
public enum ViewAction {
case decrement(arg1: String = "", arg2: Int? = nil)
}
public enum ReducerAction {
case decrement(arg1: String = "")
}
}
"""
} matches: {
"""
public struct MyReducer {
public enum ViewAction {
case decrement(arg1: String = "", arg2: Int? = nil)
}
public enum ReducerAction {
case decrement(arg1: String = "")
}

public enum Action: ActionProtocol {
case decrement(arg1: String = "", arg2: Int? = nil)

case decrement(arg1: String = "")
public init(viewAction: ViewAction) {
switch viewAction {
case .decrement(let arg1, let arg2):
self = .decrement(arg1: arg1, arg2: arg2)
}
}
public init(reducerAction: ReducerAction) {
switch reducerAction {
case .decrement(let arg1):
self = .decrement(arg1: arg1)
}
}
}
}

extension MyReducer: ReducerProtocol {
}
"""
}
}
}