From d169f5a5352103706ca71a3dc66b2f3b2600aa95 Mon Sep 17 00:00:00 2001 From: winddpan Date: Tue, 4 Jun 2024 18:26:46 +0800 Subject: [PATCH] Compatible with SwiftData support optional wiseInit: @Codable(wiseInit: false) support variable FunctionCallExpr type: `var subModel = SubModel(subName: "", subAge: 0)` fix @Model @Codable mixed usage may cause crashes --- .../CodableWrapper/CodableWrapperMacros.swift | 2 +- Sources/CodableWrapper/Decoder.swift | 13 ++++++----- Sources/CodableWrapperMacros/Codable.swift | 22 +++++++++++++------ .../ModelMemberPropertyContainer.swift | 2 +- .../VariableDeclSyntaxExtension.swift | 5 ++++- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Sources/CodableWrapper/CodableWrapperMacros.swift b/Sources/CodableWrapper/CodableWrapperMacros.swift index 375f03b..a7b6296 100644 --- a/Sources/CodableWrapper/CodableWrapperMacros.swift +++ b/Sources/CodableWrapper/CodableWrapperMacros.swift @@ -1,6 +1,6 @@ @attached(member, names: named(init(from:)), named(encode(to:)), arbitrary) @attached(extension, conformances: Codable) -public macro Codable() = #externalMacro(module: "CodableWrapperMacros", type: "Codable") +public macro Codable(wiseInit: Bool = true) = #externalMacro(module: "CodableWrapperMacros", type: "Codable") @attached(member, names: named(init(from:)), named(encode(to:)), arbitrary) public macro CodableSubclass() = #externalMacro(module: "CodableWrapperMacros", type: "CodableSubclass") diff --git a/Sources/CodableWrapper/Decoder.swift b/Sources/CodableWrapper/Decoder.swift index 09826dc..2e50696 100644 --- a/Sources/CodableWrapper/Decoder.swift +++ b/Sources/CodableWrapper/Decoder.swift @@ -24,11 +24,14 @@ public extension KeyedDecodingContainer where K == AnyCodingKey { } private extension KeyedDecodingContainer where K == AnyCodingKey { - func tryNormalKeyDecode(type: Value.Type, key: String) -> Value? { + func tryNormalKeyDecode(type: Value.Type, key: String) -> Value? { func _decode(key: String) -> Value? { guard let key = Key(stringValue: key) else { return nil } + if let value = try? decodeIfPresent(type, forKey: key) { + return value + } let value = try? decodeIfPresent(AnyDecodable.self, forKey: key)?.value if let value = value { if let converted = value as? Value { @@ -37,10 +40,8 @@ private extension KeyedDecodingContainer where K == AnyCodingKey { if let _bridged = (Value.self as? _BuiltInBridgeType.Type)?._transform(from: value), let __bridged = _bridged as? Value { return __bridged } - if let valueType = Value.self as? Decodable.Type { - if let value = try? valueType.decode(from: self, forKey: key) as? Value { - return value - } + if let value = try? Value.decode(from: self, forKey: key) { + return value } } return nil @@ -54,7 +55,7 @@ private extension KeyedDecodingContainer where K == AnyCodingKey { return nil } - private func tryNestedKeyDecode(type: Value.Type, key: String) -> Value? { + private func tryNestedKeyDecode(type: Value.Type, key: String) -> Value? { var keyComps = key.components(separatedBy: ".") guard let rootKey = AnyCodingKey(stringValue: keyComps.removeFirst()) else { return nil diff --git a/Sources/CodableWrapperMacros/Codable.swift b/Sources/CodableWrapperMacros/Codable.swift index 8a5ffb2..74d2117 100644 --- a/Sources/CodableWrapperMacros/Codable.swift +++ b/Sources/CodableWrapperMacros/Codable.swift @@ -4,7 +4,7 @@ import SwiftSyntaxMacros public struct Codable: ExtensionMacro, MemberMacro { public static func expansion(of node: AttributeSyntax, attachedTo declaration: some DeclGroupSyntax, - providingExtensionsOf type: some TypeSyntaxProtocol, + providingExtensionsOf type: some TypeSyntaxProtocol, conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext) throws -> [ExtensionDeclSyntax] { var inheritedTypes: InheritedTypeListSyntax? if let declaration = declaration.as(StructDeclSyntax.self) { @@ -15,8 +15,7 @@ public struct Codable: ExtensionMacro, MemberMacro { throw ASTError("use @Codable in `struct` or `class`") } if let inheritedTypes = inheritedTypes, - inheritedTypes.contains(where: { inherited in inherited.type.trimmedDescription == "Codable" }) - { + inheritedTypes.contains(where: { inherited in inherited.type.trimmedDescription == "Codable" }) { return [] } @@ -30,14 +29,23 @@ public struct Codable: ExtensionMacro, MemberMacro { public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingMembersOf declaration: some SwiftSyntax.DeclGroupSyntax, - in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] - { + in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] { // TODO: diagnostic do not implement `init(from:)` or `encode(to:))` let propertyContainer = try ModelMemberPropertyContainer(decl: declaration, context: context) let decoder = try propertyContainer.genDecoderInitializer(config: .init(isOverride: false)) let encoder = try propertyContainer.genEncodeFunction(config: .init(isOverride: false)) - let memberwiseInit = try propertyContainer.genMemberwiseInit(config: .init(isOverride: false)) - return [decoder, encoder, memberwiseInit] + + var hasWiseInit = true + if case let .argumentList(list) = node.arguments, list.first?.expression.description == "false" { + hasWiseInit = false + } + + if !hasWiseInit { + return [decoder, encoder] + } else { + let memberwiseInit = try propertyContainer.genMemberwiseInit(config: .init(isOverride: false)) + return [decoder, encoder, memberwiseInit] + } } } diff --git a/Sources/CodableWrapperMacros/ModelMemberPropertyContainer.swift b/Sources/CodableWrapperMacros/ModelMemberPropertyContainer.swift index 7fa30cd..b4229bc 100644 --- a/Sources/CodableWrapperMacros/ModelMemberPropertyContainer.swift +++ b/Sources/CodableWrapperMacros/ModelMemberPropertyContainer.swift @@ -90,7 +90,7 @@ struct ModelMemberPropertyContainer { return text } else { - let body = "container.decode(type: Swift.type(of: self.\(member.name)), keys: [\(member.codingKeys.joined(separator: ", "))], nestedKeys: [\(member.nestedKeys.joined(separator: ", "))])" + let body = "container.decode(type: \(member.type).self, keys: [\(member.codingKeys.joined(separator: ", "))], nestedKeys: [\(member.nestedKeys.joined(separator: ", "))])" if let initializerExpr = member.initializerExpr { return "self.\(member.name) = (try? \(body)) ?? (\(initializerExpr))" diff --git a/Sources/CodableWrapperMacros/VariableDeclSyntaxExtension.swift b/Sources/CodableWrapperMacros/VariableDeclSyntaxExtension.swift index 72b717a..5cd2b39 100644 --- a/Sources/CodableWrapperMacros/VariableDeclSyntaxExtension.swift +++ b/Sources/CodableWrapperMacros/VariableDeclSyntaxExtension.swift @@ -34,7 +34,7 @@ extension VariableDeclSyntax { } var inferType: String? { - var type = bindings.compactMap(\.typeAnnotation).first?.type.description + var type: String? = bindings.compactMap(\.typeAnnotation).first?.type.trimmedDescription // try infer type if type == nil, let initExpr = bindings.compactMap(\.initializer).first?.value { if initExpr.is(StringLiteralExprSyntax.self) { @@ -45,6 +45,9 @@ extension VariableDeclSyntax { type = "Double" } else if initExpr.is(BooleanLiteralExprSyntax.self) { type = "Bool" + } else if let funcDecl = initExpr.as(FunctionCallExprSyntax.self), + let declRef = funcDecl.calledExpression.as(DeclReferenceExprSyntax.self) { + type = declRef.trimmedDescription } } return type