我正在尝试使用宏和 init 将案例添加到枚举中,以将其默认为添加的案例。例如,假设我的枚举是:
@AddEnumCase
enum Employee: String {
case manager
}
那么扩展版本应该给出:
enum Employee: String {
case manager
case developer
init(rawValue: String) {
switch rawValue {
case Self.manager.rawValue:
self = .manager
default:
self = .developer
}
这是到目前为止我的代码。当 main.swift 上没有输入任何内容时,它就会成功构建,并且测试也成功。但是当在 main.swift 中使用宏时,它无法构建:
Command SwiftCompile failed with a nonzero exit code
public struct AddEnumCase: MemberMacro {
public static func expansion(of node: ....) throws -> [SwiftSyntax.DeclSyntax] {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { return [] }
guard let inheritanceType = enumDecl.inheritanceClause?.inheritedTypes.first?.type else { return [] }
let cases = enumDecl.memberBlock.members.compactMap { $0.decl.as(EnumCaseDeclSyntax.self)?.elements) }
let newElement = try EnumCaseDeclSyntax("case developer")
let initializer = try InitializerDeclSyntax("init(rawValue: \(inheritanceType.trimmed))") {
try SwitchExprSyntax("switch rawValue") {
for name in cases {
SwitchCaseSyntax("""
case Self.\(raw: name).rawValue:
self = .\(raw: name)
""")
}
SwitchCaseSyntax("""
default:
self = .developer
""")
}
}
return [DeclSyntax(newElement), DeclSyntax(initializer)]
}
}
@attached(member, names: arbitrary)
public macro AddEnumCase() = #externalMacro(module: "MyMacros", type: "AddEnumCase")
以下代码在 main.swift 上失败
@AddEnumCase
enum Employee: String {
case Manager
}
最佳答案
查看崩溃日志,我可以发现在为 RawRepresentable
一致性生成 rawValue
时出现了问题。生成的 rawValue
中的 switch
可能并不详尽,因为它没有考虑您的新情况。
Swift 宏扩展不应依赖于扩展顺序。所有宏都被赋予原始 AST,并且它们“同时”扩展。我认为这也可能适用于 : String
的降低方式。您依赖 : String
降低宏扩展后的,但这里显然不是这种情况。
这基本上意味着您还必须生成 rawValue
实现。
这是一个示例,仅适用于String
原始值:
public struct AddEnumCase: MemberMacro {
public static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { return [] }
guard let inheritanceType = enumDecl.inheritanceClause?.inheritedTypes.first?.type else { return [] }
let cases = enumDecl.memberBlock.members.compactMap { $0.decl.as(EnumCaseDeclSyntax.self)?.elements }
let newElement = try EnumCaseDeclSyntax("case developer")
let initializer = try InitializerDeclSyntax("init(rawValue: \(String.self))") {
try SwitchExprSyntax("switch rawValue") {
for caseList in cases {
for `case` in caseList {
SwitchCaseSyntax("""
case Self.\(raw: `case`.name).rawValue:
self = .\(raw: `case`.name)
""")
}
}
SwitchCaseSyntax("""
default:
self = .developer
""")
}
}
let rawValue = try VariableDeclSyntax("var rawValue: \(String.self)") {
try SwitchExprSyntax("switch self") {
for caseList in cases {
for `case` in caseList {
if let rawValueExpr = `case`.rawValue?.value {
SwitchCaseSyntax("""
case .\(raw: `case`.name):
return \(rawValueExpr)
""")
} else {
SwitchCaseSyntax("""
case .\(raw: `case`.name):
return \(literal: `case`.name.text)
""")
}
}
}
}
}
return [DeclSyntax(newElement), DeclSyntax(initializer), DeclSyntax(rawValue)]
}
}
对于数字原始值,您需要保留一个针对每种情况递增的计数器,这有点复杂。要 100% 复制 Swift 生成的内容,您还需要将计数器设置为任何显式声明的原始值。这留给读者作为练习。 (:D)
关于swift - 如何向枚举添加新案例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77304529/