| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- // The Swift Programming Language
- // https://docs.swift.org/swift-book
- import SwiftSyntax
- import SwiftSyntaxBuilder
- import SwiftSyntaxMacros
- import SwiftDiagnostics
- import Foundation
- // 宏实现:自动生成 Codable 协议的实现
- public struct AutoCodableMacro: MemberMacro {
- public static func expansion(
- of node: AttributeSyntax,
- providingMembersOf declaration: some DeclGroupSyntax,
- in context: some MacroExpansionContext
- ) throws -> [DeclSyntax] {
- // 仅支持类和结构体
- guard let typeDecl: DeclGroupSyntax = declaration.as(ClassDeclSyntax.self) ?? declaration.as(StructDeclSyntax.self) else {
- context.diagnose(
- Diagnostic(
- node: node,
- message: AutoCodableMacroError.onlyClassesAndStructs
- )
- )
- return []
- }
-
- // 解析类型遵循的协议列表
- let inheritedProtocols = typeDecl.inheritanceClause?.inheritedTypes
- .compactMap { $0.type.as(IdentifierTypeSyntax.self)?.name.text } ?? []
-
- // 判断是否遵循 Codable/Decodable/Encodable
- let conformsToCodable = inheritedProtocols.contains("Codable")
- let conformsToDecodable = inheritedProtocols.contains("Decodable") || conformsToCodable
- let conformsToEncodable = inheritedProtocols.contains("Encodable") || conformsToCodable
-
- // 校验:至少遵循一个协议,否则报错
- guard conformsToDecodable || conformsToEncodable else {
- context.diagnose(Diagnostic(
- node: node,
- message: AutoCodableMacroError.protocolError
- ))
- return []
- }
-
- // 提取所有存储属性
- let properties: [(name: String, type: String)] = typeDecl.memberBlock.members.compactMap { member -> (name: String, type: String)? in
- // 1. 检查是否是变量声明
- guard let variableDecl = member.decl.as(VariableDeclSyntax.self) else {
- return nil
- }
-
- // 2. 确保只处理单个属性的声明
- guard variableDecl.bindings.count == 1,
- let binding = variableDecl.bindings.first else {
- return nil
- }
-
- // 过滤计算属性
- guard binding.accessorBlock == nil else {
- return nil
- }
-
- // 3. 提取属性名称
- guard let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) else {
- return nil
- }
- let name = identifierPattern.identifier.text
-
- // 4. 提取属性类型
- guard let typeAnnotation = binding.typeAnnotation else {
- return nil
- }
- let type = typeAnnotation.type.description.trimmingCharacters(in: .whitespacesAndNewlines)
-
- return (name: name, type: type)
- }
-
- if properties.isEmpty {
- context.diagnose(
- Diagnostic(
- node: node,
- message: AutoCodableMacroError.noStoredProperties
- )
- )
- }
-
- var syntax: [DeclSyntax] = []
- // 生成 CodingKeys 枚举
- let codingKeys = try EnumDeclSyntax("enum CodingKeys: String, CodingKey") {
- for property in properties {
- DeclSyntax("case \(raw: property.name)")
- }
- }
- syntax.append(DeclSyntax(codingKeys))
-
- if conformsToDecodable {
- // 生成 init(from:) 方法
- let bodyBuilder: SyntaxNodeString = declaration is ClassDeclSyntax ? "required init(from decoder: Decoder) throws" : "init(from decoder: Decoder) throws"
- let initFrom = try InitializerDeclSyntax(bodyBuilder) {
- CodeBlockItemListSyntax {
- "let container = try decoder.container(keyedBy: CodingKeys.self)"
- for property in properties {
- """
- if let value = try? container.decode(\(raw: property.type).self, forKey: .\(raw: property.name)) {
- self.\(raw: property.name) = value
- }
- """
- }
- }
- }
- syntax.append(DeclSyntax(initFrom))
- }
-
- if conformsToEncodable {
- // 生成 encode(to:) 方法
- let encodeTo = try FunctionDeclSyntax("func encode(to encoder: Encoder) throws") {
- CodeBlockItemListSyntax {
- "var container = encoder.container(keyedBy: CodingKeys.self)"
- for property in properties {
- "try container.encode(\(raw: property.name), forKey: .\(raw: property.name))"
- }
- }
- }
- syntax.append(DeclSyntax(encodeTo))
- }
-
- return syntax
- }
- }
- // 错误提示定义
- enum AutoCodableMacroError: DiagnosticMessage {
- case onlyClassesAndStructs
- case noStoredProperties
- case protocolError
-
- var message: String {
- switch self {
- case .onlyClassesAndStructs:
- "AutoCodable can only be applied to classes and structs"
- case .noStoredProperties:
- "Type has no stored properties; Codable implementation will be empty"
- case .protocolError:
- "类型必须遵循 Codable、Decodable 或 Encodable"
- }
- }
-
- var diagnosticID: MessageID { .init(domain: "Codable", id: "\(self)") }
- var severity: DiagnosticSeverity { .error }
- }
|