AutoCodableMacro.swift 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // The Swift Programming Language
  2. // https://docs.swift.org/swift-book
  3. import SwiftSyntax
  4. import SwiftSyntaxBuilder
  5. import SwiftSyntaxMacros
  6. import SwiftDiagnostics
  7. import Foundation
  8. // 宏实现:自动生成 Codable 协议的实现
  9. public struct AutoCodableMacro: MemberMacro {
  10. public static func expansion(
  11. of node: AttributeSyntax,
  12. providingMembersOf declaration: some DeclGroupSyntax,
  13. in context: some MacroExpansionContext
  14. ) throws -> [DeclSyntax] {
  15. // 仅支持类和结构体
  16. guard let typeDecl: DeclGroupSyntax = declaration.as(ClassDeclSyntax.self) ?? declaration.as(StructDeclSyntax.self) else {
  17. context.diagnose(
  18. Diagnostic(
  19. node: node,
  20. message: AutoCodableMacroError.onlyClassesAndStructs
  21. )
  22. )
  23. return []
  24. }
  25. // 解析类型遵循的协议列表
  26. let inheritedProtocols = typeDecl.inheritanceClause?.inheritedTypes
  27. .compactMap { $0.type.as(IdentifierTypeSyntax.self)?.name.text } ?? []
  28. // 判断是否遵循 Codable/Decodable/Encodable
  29. let conformsToCodable = inheritedProtocols.contains("Codable")
  30. let conformsToDecodable = inheritedProtocols.contains("Decodable") || conformsToCodable
  31. let conformsToEncodable = inheritedProtocols.contains("Encodable") || conformsToCodable
  32. // 校验:至少遵循一个协议,否则报错
  33. guard conformsToDecodable || conformsToEncodable else {
  34. context.diagnose(Diagnostic(
  35. node: node,
  36. message: AutoCodableMacroError.protocolError
  37. ))
  38. return []
  39. }
  40. // 提取所有存储属性
  41. let properties: [(name: String, type: String)] = typeDecl.memberBlock.members.compactMap { member -> (name: String, type: String)? in
  42. // 1. 检查是否是变量声明
  43. guard let variableDecl = member.decl.as(VariableDeclSyntax.self) else {
  44. return nil
  45. }
  46. // 2. 确保只处理单个属性的声明
  47. guard variableDecl.bindings.count == 1,
  48. let binding = variableDecl.bindings.first else {
  49. return nil
  50. }
  51. // 过滤计算属性
  52. if let accessorBlock = binding.accessorBlock {
  53. // 判断 accessor 类型
  54. switch accessorBlock.accessors {
  55. case .accessors(let accessors):
  56. let hasGet = accessors.contains { $0.accessorSpecifier.tokenKind == .keyword(.get) }
  57. if hasGet {
  58. return nil
  59. }
  60. case .getter:
  61. return nil
  62. }
  63. }
  64. // 3. 提取属性名称
  65. guard let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) else {
  66. return nil
  67. }
  68. let name = identifierPattern.identifier.text
  69. // 4. 提取属性类型
  70. guard let typeAnnotation = binding.typeAnnotation else {
  71. return nil
  72. }
  73. let type = typeAnnotation.type.description.trimmingCharacters(in: .whitespacesAndNewlines)
  74. return (name: name, type: type)
  75. }
  76. if properties.isEmpty {
  77. context.diagnose(
  78. Diagnostic(
  79. node: node,
  80. message: AutoCodableMacroError.noStoredProperties
  81. )
  82. )
  83. }
  84. var syntax: [DeclSyntax] = []
  85. // 生成 CodingKeys 枚举
  86. let codingKeys = try EnumDeclSyntax("enum CodingKeys: String, CodingKey") {
  87. for property in properties {
  88. DeclSyntax("case \(raw: property.name)")
  89. }
  90. }
  91. syntax.append(DeclSyntax(codingKeys))
  92. if conformsToDecodable {
  93. // 生成 init(from:) 方法
  94. let bodyBuilder: SyntaxNodeString = declaration is ClassDeclSyntax ? "required init(from decoder: Decoder) throws" : "init(from decoder: Decoder) throws"
  95. let initFrom = try InitializerDeclSyntax(bodyBuilder) {
  96. CodeBlockItemListSyntax {
  97. "let container = try decoder.container(keyedBy: CodingKeys.self)"
  98. for property in properties {
  99. """
  100. if let value = try? container.decode(\(raw: property.type).self, forKey: .\(raw: property.name)) {
  101. self.\(raw: property.name) = value
  102. }
  103. """
  104. }
  105. }
  106. }
  107. syntax.append(DeclSyntax(initFrom))
  108. }
  109. if conformsToEncodable {
  110. // 生成 encode(to:) 方法
  111. let encodeTo = try FunctionDeclSyntax("func encode(to encoder: Encoder) throws") {
  112. CodeBlockItemListSyntax {
  113. "var container = encoder.container(keyedBy: CodingKeys.self)"
  114. for property in properties {
  115. "try container.encode(\(raw: property.name), forKey: .\(raw: property.name))"
  116. }
  117. }
  118. }
  119. syntax.append(DeclSyntax(encodeTo))
  120. }
  121. return syntax
  122. }
  123. }
  124. // 错误提示定义
  125. enum AutoCodableMacroError: DiagnosticMessage {
  126. case onlyClassesAndStructs
  127. case noStoredProperties
  128. case protocolError
  129. case customError(String)
  130. var message: String {
  131. switch self {
  132. case .onlyClassesAndStructs:
  133. "AutoCodable can only be applied to classes and structs"
  134. case .noStoredProperties:
  135. "Type has no stored properties; Codable implementation will be empty"
  136. case .protocolError:
  137. "类型必须遵循 Codable、Decodable 或 Encodable"
  138. case .customError(let err):
  139. err
  140. }
  141. }
  142. var diagnosticID: MessageID { .init(domain: "Codable", id: "\(self)") }
  143. var severity: DiagnosticSeverity { .error }
  144. }