Swift 5.9宏报错:Declaration name未被associatedValues宏覆盖
解决Swift 5.9 @associatedValues宏编译报错:生成的属性未被宏覆盖
问题背景
我尝试用Swift 5.9的宏功能实现@associatedValues,为带关联值的枚举自动生成对应的可选属性。例如给枚举添加宏后:
@associatedValues enum Fruit { case banana(Banana) case grape(Grape) }
期望自动生成bananaValue、grapeValue这类属性,但编译时出现错误:
Declaration name 'bananaValue' is not covered by macro 'associatedValues' Declaration name 'grapeValue' is not covered by macro 'associatedValues'
自动补全能识别这些属性,但编译无法通过,宏实现代码如下:
public static func expansion<Declaration, Context>( of node: SwiftSyntax.AttributeSyntax, providingMembersOf declaration: Declaration, in context: Context ) throws -> [SwiftSyntax.DeclSyntax] where Declaration: SwiftSyntax.DeclGroupSyntax, Context: SwiftSyntaxMacros.MacroExpansionContext { guard let enumDeclaration = declaration.as(EnumDeclSyntax.self) else { throw AssociatedValuesMacroError.onlyApplicableToEnum } return enumDeclaration.memberBlock.members .compactMap { $0.decl.as(EnumCaseDeclSyntax.self) } .map { (_case: EnumCaseDeclSyntax) in let _element: EnumCaseElementListSyntax.Element = _case.elements.first! let caseName = _element.identifier let typeName = _element.associatedValue!.parameterList.first!.description return """ var \(raw: caseName)Value: \(raw: typeName)? { if case let .\(raw: caseName)(value) = self { return value } else { return nil } } """ } }
解决方案
问题出在宏的声明和实现的安全处理上,需要两个关键修改:
1. 宏声明添加names: arbitrary
由于宏生成的成员名称是动态的(基于枚举case名称),默认的宏声明会限制生成固定名称的成员,因此需要在@attached属性中指定names: arbitrary,告知编译器该宏可以生成任意名称的成员:
@attached(member, names: arbitrary) public struct AssociatedValuesMacro: MemberMacro { // 宏实现代码 }
2. 替换强制解包为安全的可选处理
原代码中使用!强制解包枚举case元素和关联值,遇到无关联值或多参数的case时会崩溃,建议用guard语句做安全处理:
修改后的宏实现代码:
public static func expansion<Declaration, Context>( of node: SwiftSyntax.AttributeSyntax, providingMembersOf declaration: Declaration, in context: Context ) throws -> [SwiftSyntax.DeclSyntax] where Declaration: SwiftSyntax.DeclGroupSyntax, Context: SwiftSyntaxMacros.MacroExpansionContext { guard let enumDeclaration = declaration.as(EnumDeclSyntax.self) else { throw AssociatedValuesMacroError.onlyApplicableToEnum } var generatedMembers: [DeclSyntax] = [] for member in enumDeclaration.memberBlock.members { // 只处理带单个关联值参数的枚举case guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self), let caseElement = caseDecl.elements.first, let associatedValue = caseElement.associatedValue, let param = associatedValue.parameterList.first else { continue } let caseName = caseElement.identifier.text let typeName = param.type.description let propertyDecl = """ var \(raw: caseName)Value: \(raw: typeName)? { if case let .\(raw: caseName)(value) = self { return value } return nil } """ generatedMembers.append(DeclSyntax(stringLiteral: propertyDecl)) } return generatedMembers }
说明
names: arbitrary是解决编译报错的核心:它允许宏生成任意名称的成员,编译器不会再检查生成的名称是否属于宏预定义范围。- 用
guard替代强制解包,让宏在遇到不符合预期的枚举case时跳过处理,避免运行时崩溃。
内容的提问来源于stack exchange,提问作者Nuno Gonçalves
相关产品推荐
相关产品推荐

