通过ExtensionMacro的协议符合,隐式的resultBuilder会丢失

移动开发 2026-07-12

我很确定这件事现在还不可能,但我还是发帖,希望我错了。

一个简单的 protocol,它使用一个简单的 resultBuilder

@resultBuilder
 public enum MyResultBuilder {
   public static func buildBlock ( _ components: String... ) -> [ String ] { components }
 }

 public protocol MyProtocol {
   @MyResultBuilder var strings: [ String ] { get }
 }

...被制作成一个 ExtensionMacroConformanceMacro 已弃用:https://github.com/swiftlang/swift-evolution/blob/main/proposals/0402-extension-macros.md

@attached( extension , conformances: MyProtocol )
 public macro MyMacro ( ) = #externalMacro ( module: "MacroDefinitions" , type: "MyConformanceMacro" )

 public struct MyConformanceMacro: ExtensionMacro {
   public static func expansion (
 of node: SwiftSyntax.AttributeSyntax ,
 attachedTo declaration: some SwiftSyntax.DeclGroupSyntax ,
 providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol ,
 conformingTo protocols: [ SwiftSyntax.TypeSyntax ] ,
 in context: some SwiftSyntaxMacros.MacroExpansionContext )
 throws -> [ SwiftSyntax.ExtensionDeclSyntax ] {
     let r: SwiftSyntax.ExtensionDeclSyntax = try .init ( "extension \(type): MyProtocol { }" )
     return [ r ]
   }
 }

细节

变量 strings 旨在由开发者动态定义。

问题

当调用 @MyMacro 时,展开会生成对 MyProtocol 的扩展符合性,在此过程中预定义的 @MyResultBuilder 会丢失。

这会导致错误。

修复方法

  1. inline 宏并在目标对象内部定义 strings 变量。 // 宏开销大,不可行。
  2. 让用户在 var strings: [ String ] 前手动追加 @MyResultBuilder。 // 呃

问题

如何编写一个实现协议符合性的宏,在其中隐含的 @MyResultBuilder 不会丢失?

目标

 @MyMacro
 struct MyStruct {

   var strings: [ String ] { // no errors
     "one"
     "two"
     "three"
   }
 }

错误图片

解决方案

我的建议是让宏的使用者显式地写出对协议的符合性。也就是说,用户需要写成:

@MyMacro
struct MyStruct: MyProtocol {
//             ^^^^^^^^^^^^
    var strings: [String] {
        "one"
        "two"
        "three"
    }
}

这比让他们写 @MyResultBuilder 稍好,因为

  1. 即使存在多项协议要求并且使用了结果构建器,用户也只需要再多写一件事。
  2. 宏实现可以检查是否显式写入了协议符合性;若未写明,则给出有用的错误信息。另一方面,忘记写 @MyResultBuilder 的错误信息并不会告诉用户应该怎么做。

这种检查可以这样写:

public struct MyConformanceMacro: ExtensionMacro {
    public static func expansion (
        of node: AttributeSyntax ,
        attachedTo declaration: some DeclGroupSyntax ,
        providingExtensionsOf type: some TypeSyntaxProtocol ,
        conformingTo protocols: [TypeSyntax] ,
        in context: some MacroExpansionContext )
    throws -> [ExtensionDeclSyntax] {
        if protocols.contains(where: { $0.as(IdentifierTypeSyntax.self)?.name.text == "MyProtocol" }) {
            context.addDiagnostics(from: MacroExpansionErrorMessage("You must declare conformance to MyProtocol explicitly!"), node: declaration)
            return []
        }
        return try [
            ExtensionDeclSyntax("extension \(type)") {
                // ... declare other members here...
            }
        ]
    }
}

当类型已经声明对 MyProtocol 的符合性时,protocols 参数将不包含 MyProtocol

在Xcode中的示例输出:

图片描述

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章