1
- @_implementationOnly import Foundation
2
1
@_implementationOnly import SwiftSyntax
3
2
@_implementationOnly import SwiftSyntaxMacros
4
3
5
- extension Codable : ExtensionMacro {
4
+ extension Codable : MemberMacro , ExtensionMacro {
6
5
/// 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`.
8
85
///
9
86
/// Depending on whether attached type already conforms to `Decodable`
10
87
/// 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`.
13
90
///
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.
18
93
///
19
94
/// - Parameters:
20
95
/// - node: The custom attribute describing this attached macro.
@@ -25,44 +100,33 @@ extension Codable: ExtensionMacro {
25
100
/// to.
26
101
/// - context: The context in which to perform the macro expansion.
27
102
///
28
- /// - Returns: Extensions with `CodingKeys` type, `Decodable`
103
+ /// - Returns: Extensions with `CodingKeys` type, `Decodable`
29
104
/// conformance with `init(from:)` implementation and `Encodable`
30
105
/// conformance with `encode(to:)` implementation depending on already
31
106
/// declared conformances of type.
107
+ ///
108
+ /// - Note: For `class` types only conformance is generated,
109
+ /// member expansion generates the actual implementation.
32
110
static func expansion(
33
111
of node: AttributeSyntax ,
34
112
attachedTo declaration: some DeclGroupSyntax ,
35
113
providingExtensionsOf type: some TypeSyntaxProtocol ,
36
114
conformingTo protocols: [ TypeSyntax ] ,
37
115
in context: some MacroExpansionContext
38
116
) 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
42
131
}
43
132
}
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