You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:43:16