1- @_implementationOnly import Foundation
21@_implementationOnly import SwiftSyntax
32@_implementationOnly import SwiftSyntaxMacros
43
5- extension Codable : ExtensionMacro {
4+ extension Codable : MemberMacro , ExtensionMacro {
65 /// Expand to produce extensions with `Codable` implementation
7- /// members for attached struct.
6+ /// members for attached `class`.
7+ ///
8+ /// Conformance for both `Decodable` and `Encodable` is generated regardless
9+ /// of whether class already conforms to any. Class or its super class
10+ /// shouldn't conform to `Decodable` or `Encodable`
11+ ///
12+ /// The `AttributeExpander` instance provides declarations based on
13+ /// whether declaration is supported.
14+ ///
15+ /// - Parameters:
16+ /// - node: The custom attribute describing this attached macro.
17+ /// - declaration: The declaration this macro attribute is attached to.
18+ /// - context: The context in which to perform the macro expansion.
19+ ///
20+ /// - Returns: Declarations of `CodingKeys` type, `Decodable`
21+ /// conformance with `init(from:)` implementation and `Encodable`
22+ /// conformance with `encode(to:)` implementation depending on already
23+ /// declared conformances of type.
24+ ///
25+ /// - Note: For types other than `class` types no declarations generated.
26+ static func expansion(
27+ of node: AttributeSyntax ,
28+ providingMembersOf declaration: some DeclGroupSyntax ,
29+ in context: some MacroExpansionContext
30+ ) throws -> [ DeclSyntax ] {
31+ let defaultProtocols : [ TypeSyntax ] = [
32+ . init( stringLiteral: TypeCodingLocation . Method. decode. protocol) ,
33+ . init( stringLiteral: TypeCodingLocation . Method. encode. protocol) ,
34+ ]
35+ return try Self . expansion (
36+ of: node, providingMembersOf: declaration,
37+ conformingTo: defaultProtocols, in: context
38+ )
39+ }
40+
41+ /// Expand to produce extensions with `Codable` implementation
42+ /// members for attached `class`.
43+ ///
44+ /// Depending on whether attached type already conforms to `Decodable`
45+ /// or `Encodable`, `Decodable` or `Encodable` conformance
46+ /// implementation is skipped. Entire macro expansion is skipped if attached
47+ /// type already conforms to both `Decodable` and`Encodable`.
48+ ///
49+ /// The `AttributeExpander` instance provides declarations based on
50+ /// whether declaration is supported.
51+ ///
52+ /// - Parameters:
53+ /// - node: The custom attribute describing this attached macro.
54+ /// - declaration: The declaration this macro attribute is attached to.
55+ /// - protocols: The list of protocols to add conformances to. These will
56+ /// always be protocols that `type` does not already state a conformance
57+ /// to.
58+ /// - context: The context in which to perform the macro expansion.
59+ ///
60+ /// - Returns: Declarations of `CodingKeys` type, `Decodable`
61+ /// conformance with `init(from:)` implementation and `Encodable`
62+ /// conformance with `encode(to:)` implementation depending on already
63+ /// declared conformances of type.
64+ ///
65+ /// - Note: For types other than `class` types no declarations generated.
66+ static func expansion(
67+ of node: AttributeSyntax ,
68+ providingMembersOf declaration: some DeclGroupSyntax ,
69+ conformingTo protocols: [ TypeSyntax ] ,
70+ in context: some MacroExpansionContext
71+ ) throws -> [ DeclSyntax ] {
72+ guard
73+ let exp = AttributeExpander ( for: declaration, in: context) ,
74+ let decl = declaration. as ( ClassDeclSyntax . self) ,
75+ case let type = IdentifierTypeSyntax ( name: decl. name)
76+ else { return [ ] }
77+ let exts = exp. codableExpansion ( for: type, to: protocols, in: context)
78+ return exts. flatMap { `extension` in
79+ `extension`. memberBlock. members. map { DeclSyntax ( $0. decl) }
80+ }
81+ }
82+
83+ /// Expand to produce extensions with `Codable` implementation
84+ /// members for attached `struct` or `class`.
885 ///
986 /// Depending on whether attached type already conforms to `Decodable`
1087 /// or `Encodable` extension for `Decodable` or `Encodable` conformance
11- /// implementation is skipped.Entire macro expansion is skipped if attached type
12- /// already conforms to both `Decodable` and`Encodable`.
88+ /// implementation is skipped. Entire macro expansion is skipped if attached
89+ /// type already conforms to both `Decodable` and`Encodable`.
1390 ///
14- /// For all the variable declarations in the attached type registration is
15- /// done via `Registrar` instance with optional `PeerAttribute`
16- /// metadata. The `Registrar` instance provides declarations based on
17- /// all the registrations.
91+ /// The `AttributeExpander` instance provides declarations based on
92+ /// whether declaration is supported.
1893 ///
1994 /// - Parameters:
2095 /// - node: The custom attribute describing this attached macro.
@@ -25,44 +100,33 @@ extension Codable: ExtensionMacro {
25100 /// to.
26101 /// - context: The context in which to perform the macro expansion.
27102 ///
28- /// - Returns: Extensions with `CodingKeys` type, `Decodable`
103+ /// - Returns: Extensions with `CodingKeys` type, `Decodable`
29104 /// conformance with `init(from:)` implementation and `Encodable`
30105 /// conformance with `encode(to:)` implementation depending on already
31106 /// declared conformances of type.
107+ ///
108+ /// - Note: For `class` types only conformance is generated,
109+ /// member expansion generates the actual implementation.
32110 static func expansion(
33111 of node: AttributeSyntax ,
34112 attachedTo declaration: some DeclGroupSyntax ,
35113 providingExtensionsOf type: some TypeSyntaxProtocol ,
36114 conformingTo protocols: [ TypeSyntax ] ,
37115 in context: some MacroExpansionContext
38116 ) throws -> [ ExtensionDeclSyntax ] {
39- let registrar = registrar ( for: declaration, node: node, in: context)
40- guard let registrar else { return [ ] }
41- return registrar. codableExpansion ( for: type, to: protocols, in: context)
117+ guard
118+ let self = Self ( from: node) ,
119+ !self . diagnoser ( ) . produce ( for: declaration, in: context) ,
120+ let exp = AttributeExpander ( for: declaration, in: context)
121+ else { return [ ] }
122+ var exts = exp. codableExpansion ( for: type, to: protocols, in: context)
123+ if declaration. is ( ClassDeclSyntax . self) {
124+ for (index, var `extension`) in exts. enumerated ( ) {
125+ `extension`. memberBlock = . init( members: [ ] )
126+ exts [ index] = `extension`
127+ }
128+ exts. removeAll { $0. inheritanceClause == nil }
129+ }
130+ return exts
42131 }
43132}
44-
45- /// An extension that converts field token syntax
46- /// to equivalent key token.
47- extension TokenSyntax {
48- /// Convert field token syntax
49- /// to equivalent key token
50- /// string by trimming \`s`.
51- var asKey : String {
52- self . text. trimmingCharacters ( in: . swiftVariableExtra)
53- }
54-
55- /// Convert field token syntax
56- /// to equivalent key token
57- /// by trimming \`s`.
58- var raw : TokenSyntax { . identifier( self . asKey) }
59- }
60-
61- /// An extension that manages
62- /// custom character sets
63- /// for macro expansion.
64- extension CharacterSet {
65- /// Character set that contains extra characters in swift variable names
66- /// not applicable for key construction.
67- static let swiftVariableExtra : Self = . init( arrayLiteral: " ` " )
68- }
0 commit comments