如何通过Swift宏为枚举新增case并添加默认初始化方法?
Swift宏为枚举新增case及初始化器时编译失败的修复方案
问题场景
我尝试实现一个Swift宏@AddEnumCase,功能是给枚举自动添加developer case,同时生成自定义init(rawValue:)初始化器,将未匹配的原始值默认映射到developer。
原枚举代码:
@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: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) 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 }
错误原因分析
- 枚举case遍历逻辑错误:
cases变量是[EnumCaseElementListSyntax]类型(数组嵌套数组),直接遍历会导致每个name是枚举元素列表而非单个元素,生成的代码存在语法错误。 - SwitchCase语法构建不规范:直接通过字符串插值生成
SwitchCaseSyntax时,没有正确处理枚举元素的语法结构,且缺少必要的缩进和代码块包裹。 - 初始化器语法不完整:原代码中
sitch语句缺少闭合大括号,导致生成的代码语法不完整。
修复后的宏代码
import SwiftSyntax import SwiftSyntaxMacros 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 [] } // 展平枚举case元素,获取单个枚举元素对象 let enumElements = enumDecl.memberBlock.members .compactMap { $0.decl.as(EnumCaseDeclSyntax.self) } .flatMap { $0.elements } // 构建新增的developer case let newCase = EnumCaseDeclSyntax { EnumCaseElementSyntax(identifier: .identifier("developer")) } // 构建自定义初始化器 let initializer = try InitializerDeclSyntax("init(rawValue: \(inheritanceType.trimmed))") { // 构建switch匹配语句 SwitchExprSyntax(expression: IdentifierExprSyntax(identifier: .identifier("rawValue"))) { // 为每个原有枚举case生成匹配分支 for element in enumElements { SwitchCaseSyntax( label: .case( CaseItemSyntax( pattern: ExprPatternSyntax( expression: MemberAccessExprSyntax( base: MemberAccessExprSyntax(base: TypeExprSyntax(type: enumDecl.name), name: element.name), name: .identifier("rawValue") ) ) ) ), statements: CodeBlockItemListSyntax { CodeBlockItemSyntax( item: .stmt( AssignExprSyntax( leftHandSide: ExprSyntax(IdentifierExprSyntax(identifier: .identifier("self"))), rightHandSide: ExprSyntax(MemberAccessExprSyntax(base: nil, name: element.name)) ) ) ) } ) } // 默认分支,匹配失败时赋值为developer SwitchCaseSyntax( label: .default, statements: CodeBlockItemListSyntax { CodeBlockItemSyntax( item: .stmt( AssignExprSyntax( leftHandSide: ExprSyntax(IdentifierExprSyntax(identifier: .identifier("self"))), rightHandSide: ExprSyntax(MemberAccessExprSyntax(base: nil, name: .identifier("developer"))) ) ) } ) ) } } return [DeclSyntax(newCase), DeclSyntax(initializer)] } } @attached(member, names: arbitrary) public macro AddEnumCase() = #externalMacro(module: "MyMacros", type: "AddEnumCase")
验证说明
修复后的宏会生成完全符合语法规范的代码,以Employee枚举为例,展开结果与预期完全一致。此时在main.swift中使用该宏,编译不会再出现错误。
内容的提问来源于stack exchange,提问作者bubu
相关产品推荐
相关产品推荐

