AutoCodableMacro.swift 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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 properties: [(name: String, type: String)] = typeDecl.memberBlock.members.compactMap { member -> (name: String, type: String)? in
  27. // 1. 检查是否是变量声明
  28. guard let variableDecl = member.decl.as(VariableDeclSyntax.self) else {
  29. return nil
  30. }
  31. // 2. 确保只处理单个属性的声明
  32. guard variableDecl.bindings.count == 1,
  33. let binding = variableDecl.bindings.first else {
  34. return nil
  35. }
  36. // 3. 提取属性名称
  37. guard let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) else {
  38. return nil
  39. }
  40. let name = identifierPattern.identifier.text
  41. // 4. 提取属性类型
  42. guard let typeAnnotation = binding.typeAnnotation else {
  43. return nil
  44. }
  45. let type = typeAnnotation.type.description.trimmingCharacters(in: .whitespacesAndNewlines)
  46. return (name: name, type: type)
  47. }
  48. if properties.isEmpty {
  49. context.diagnose(
  50. Diagnostic(
  51. node: node,
  52. message: AutoCodableMacroError.noStoredProperties
  53. )
  54. )
  55. }
  56. // 生成 CodingKeys 枚举
  57. let codingKeys = try EnumDeclSyntax("enum CodingKeys: String, CodingKey") {
  58. for property in properties {
  59. DeclSyntax("case \(raw: property.name)")
  60. }
  61. }
  62. // 生成 init(from:) 方法
  63. let bodyBuilder: SyntaxNodeString = declaration is ClassDeclSyntax ? "required init(from decoder: Decoder) throws" : "init(from decoder: Decoder) throws"
  64. let initFrom = try InitializerDeclSyntax(bodyBuilder) {
  65. CodeBlockItemListSyntax {
  66. "let container = try decoder.container(keyedBy: CodingKeys.self)"
  67. for property in properties {
  68. """
  69. if let value = try? container.decode(\(raw: property.type).self, forKey: .\(raw: property.name)) {
  70. self.\(raw: property.name) = value
  71. }
  72. """
  73. }
  74. }
  75. }
  76. // 生成 encode(to:) 方法
  77. let encodeTo = try FunctionDeclSyntax("func encode(to encoder: Encoder) throws") {
  78. CodeBlockItemListSyntax {
  79. "var container = encoder.container(keyedBy: CodingKeys.self)"
  80. for property in properties {
  81. "try container.encode(\(raw: property.name), forKey: .\(raw: property.name))"
  82. }
  83. }
  84. }
  85. return [
  86. DeclSyntax(codingKeys),
  87. DeclSyntax(initFrom),
  88. DeclSyntax(encodeTo)
  89. ]
  90. }
  91. }
  92. // 错误提示定义
  93. enum AutoCodableMacroError: DiagnosticMessage {
  94. case onlyClassesAndStructs
  95. case noStoredProperties
  96. var message: String {
  97. switch self {
  98. case .onlyClassesAndStructs:
  99. "AutoCodable can only be applied to classes and structs"
  100. case .noStoredProperties:
  101. "Type has no stored properties; Codable implementation will be empty"
  102. }
  103. }
  104. var diagnosticID: MessageID { .init(domain: "Codable", id: "\(self)") }
  105. var severity: DiagnosticSeverity { .error }
  106. }