Swift 宏:解析 DeclSyntax 节点时出现意外错误

问题描述 投票:0回答:1

我正在尝试熟悉 Swift 宏。任务是从

获取
@RecordInterfacable
@Observable
class Model {
  let id: UUID
  var title: String

  init(
    id: UUID = UUID(),
    title: String
  ) {
    self.id = id
    self.title = title
  }
}

@Observable
class Model {
  let id: UUID
  var title: String

  init(
    id: UUID = UUID(),
    title: String
  ) {
    self.id = id
    self.title = title
  }

  convenience init(from record: ModelRecord) {
    self.init(id: record.id, title: record.title)
  }

  var record: ModelRecord {
    ModelRecord(id: self.id, title: self.title)
  }

  struct ModelRecord {
    let id: UUID
    var title: String
  }
}

删除所需的结果编译得很好,但是当运行我的宏测试时,我遇到了

Parsing a `DeclSyntax` node from string interpolation produced the following parsing errors.
Set a breakpoint in `SyntaxParseable.logStringInterpolationParsingError()` to debug the failure.
2 │   self.init(id: record.id, title: record.title)
3 │ }
4 │ struct ModelRecord: Codable {
  │ ╰─ error: unexpected code in initializer
5 │   let id: UUID
6 │ var title: String

我知道该宏尚未完全实现,但

convenience init()
已经给我带来了麻烦……我很惊讶事实如此,因为我在
static expansion(of:providingMembersOf:conformingTo:in:)
中返回的内容正在尝试实现完全相同的代码我在上面已经删掉了:

static expansion(of:providingMembersOf:conformingTo:in:) {
  // …
  return [
      DeclSyntax(
              extendedGraphemeClusterLiteral: """
      convenience init(from record: \(symbolName)Record) {
        //this needs to be implemented:
        self.init(id: record.id, title: record.title)
      }
      // var record is still missing
      struct \(symbolName)Record: Codable {
        \(memberStrings)
      }
      """
      )
    ]
}

...根据测试控制台输出产生:

Actual expanded source:
@Observable
class Model {
  let id: UUID
  var title: String
  init(
    id: UUID = UUID(),
    title: String
  ) {
    self.id = id
    self.title = title
  }

    convenience init(from record: ModelRecord) {
      self.init(id: record.id, title: record.title)
    }
    struct ModelRecord: Codable {
      let id: UUID
    var title: String
    }
}

完整的宏实现如下所示:

public struct RecordInterfacableMacro: MemberMacro {
  public static func expansion(
    of node: AttributeSyntax,
    providingMembersOf declaration: some DeclGroupSyntax,
    conformingTo protocols: [TypeSyntax],
    in context: some MacroExpansionContext
  ) throws -> [DeclSyntax] {
    guard let symbolName = declaration
      .as(ClassDeclSyntax.self)?
      .name
      .text
    else {
      fatalError("Could not extract symbol name.")
    }

    /// Extracts all the elements of the body of the given class.
    /// This includes all properties and functions.
    let membersDeclSyntax = declaration
      .as(ClassDeclSyntax.self)?
      .memberBlock
      .members
      .compactMap {
        $0
          .as(MemberBlockItemSyntax.self)?
          .decl
          .as(DeclSyntax.self)
      }

    /// Further extracts all variables
    let membersVariableDeclSyntax = membersDeclSyntax?
      .compactMap {
        $0
          .as(VariableDeclSyntax.self)
      }

    /// Create a string with the declaration of all members
    let memberStrings = membersVariableDeclSyntax?
      .map { member in
        guard let memberBindingSpecifier = member
          .bindingSpecifier
          .text
          .split(separator: ".")
          .last
        else { fatalError() }

        let identifierText = member
          .bindings
          .compactMap {
            $0
              .as(PatternBindingSyntax.self)?
              .pattern
              .as(IdentifierPatternSyntax.self)?
              .identifier
              .text
          }
          .first
        guard let identifierText else { fatalError() }

        let memberType = member
          .bindings
          .compactMap {
            $0
              .as(PatternBindingSyntax.self)?
              .typeAnnotation?
              .type
              .as(IdentifierTypeSyntax.self)?
              .name
              .text
          }
          .first
        guard let memberType else { fatalError() }

        return "\(memberBindingSpecifier) \(identifierText): \(memberType)"
      }
      .joined(separator: "\n")
    guard let memberStrings else{ fatalError() }

    return [
      DeclSyntax(
              extendedGraphemeClusterLiteral: """
      convenience init(from record: \(symbolName)Record) {
        //this needs to be implemented:
        self.init(id: record.id, title: record.title)
      }
      // var record is still missing
      struct \(symbolName)Record: Codable {
        \(memberStrings)
      }
      """
      )
    ]
  }
}
swift macros
1个回答
0
投票

事实证明我持有错误——每个

DeclSyntax
需要单独声明:

    return [
      DeclSyntax(
        extendedGraphemeClusterLiteral: """
      convenience init(from record: \(symbolName)) {
        self.init(id: record.id, title: record.title)
      }
      """
      ),
      DeclSyntax(
        stringLiteral: """
      struct \(symbolName)Record: Codable {
        \(memberStrings)
      }
      """
      )
    ]

现在我可以继续完成我的宏了😀

© www.soinside.com 2019 - 2024. All rights reserved.