Skip to content

Commit d378204

Browse files
authored
fix: fixed error with private access modifier on type declaration (#46)
1 parent 12784b2 commit d378204

5 files changed

Lines changed: 212 additions & 35 deletions

File tree

Sources/CodableMacroPlugin/Registration/ConstraintGenerator.swift renamed to Sources/CodableMacroPlugin/Registration/Options/ConstraintGenerator.swift

File renamed without changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@_implementationOnly import SwiftSyntax
2+
3+
extension Registrar.Options {
4+
/// A declaration modifiers generator for `Codable`
5+
/// conformance implementations.
6+
///
7+
/// This generator keeps track of original declaration
8+
/// and modifiers, then generates modifiers for
9+
/// `Decodable` or `Encodable` implementations.
10+
struct DeclModifiersGenerator {
11+
/// The declaration for which modifiers generated.
12+
let decl: DeclGroupSyntax
13+
14+
/// The generated list of modifiers.
15+
///
16+
/// If declaration has `public` or `package` modifier
17+
/// then same is generated, otherwise no extra modifiers
18+
/// generated.
19+
var generated: DeclModifierListSyntax {
20+
let `public` = DeclModifierSyntax(name: "public")
21+
let package = DeclModifierSyntax(name: "package")
22+
var modifiers = DeclModifierListSyntax()
23+
let accessModifier = [`public`, package].first { accessModifier in
24+
decl.modifiers.contains { modifier in
25+
modifier.name.text == accessModifier.name.text
26+
}
27+
}
28+
if let accessModifier {
29+
modifiers.append(accessModifier)
30+
}
31+
return modifiers
32+
}
33+
}
34+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
@_implementationOnly import SwiftSyntax
2+
3+
extension Registrar {
4+
/// A type indicating various configurations available
5+
/// for `Registrar`.
6+
///
7+
/// These options are used as global level customization
8+
/// performed on the final generated implementation
9+
/// of `Codable` conformance.
10+
struct Options {
11+
/// The list of modifiers generator for
12+
/// conformance implementation declarations.
13+
let modifiersGenerator: DeclModifiersGenerator
14+
/// The where clause generator for generic type arguments.
15+
let constraintGenerator: ConstraintGenerator
16+
17+
/// Memberwise initialization generator with provided options.
18+
///
19+
/// Creates memberwise initialization generator by passing
20+
/// the provided access modifiers.
21+
var initGenerator: MemberwiseInitGenerator {
22+
let modifiers = modifiersGenerator.generated
23+
return .init(options: .init(modifiers: modifiers))
24+
}
25+
26+
/// Creates a new options instance with provided declaration group.
27+
///
28+
/// - Parameters:
29+
/// - decl: The declaration group options will be applied to.
30+
///
31+
/// - Returns: The newly created options.
32+
init(decl: DeclGroupSyntax) {
33+
self.modifiersGenerator = .init(decl: decl)
34+
self.constraintGenerator = .init(decl: decl)
35+
}
36+
}
37+
}

Sources/CodableMacroPlugin/Registration/Registrar.swift

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,6 @@
88
/// use `decoding`, `encoding` and `codingKeys` methods
99
/// to get final generated implementation of `Codable` conformance.
1010
struct Registrar {
11-
/// A type indicating various configurations available
12-
/// for `Registrar`.
13-
///
14-
/// These options are used as global level customization
15-
/// performed on the final generated implementation
16-
/// of `Codable` conformance.
17-
struct Options {
18-
/// The default list of modifiers to be applied to generated
19-
/// conformance implementation declarations.
20-
fileprivate let modifiers: DeclModifierListSyntax
21-
/// The where clause generator for generic type arguments.
22-
fileprivate let constraintGenerator: ConstraintGenerator
23-
24-
/// Memberwise initialization generator with provided options.
25-
///
26-
/// Creates memberwise initialization generator by passing
27-
/// the provided access modifiers.
28-
var initGenerator: MemberwiseInitGenerator {
29-
return .init(options: .init(modifiers: modifiers))
30-
}
31-
32-
/// Creates a new options instance with provided declaration group.
33-
///
34-
/// - Parameters:
35-
/// - decl: The declaration group options will be applied to.
36-
///
37-
/// - Returns: The newly created options.
38-
init(decl: DeclGroupSyntax) {
39-
self.modifiers = decl.modifiers
40-
self.constraintGenerator = .init(decl: decl)
41-
}
42-
}
43-
4411
/// The root node containing all the keys
4512
/// and associated field metadata maps.
4613
private var root: Node
@@ -190,7 +157,7 @@ struct Registrar {
190157
)
191158
) {
192159
InitializerDeclSyntax.decode(
193-
modifiers: options.modifiers
160+
modifiers: options.modifiersGenerator.generated
194161
) { decoder in
195162
let type = caseMap.type
196163
root.decoding(in: context, from: .coder(decoder, keyType: type))
@@ -223,7 +190,9 @@ struct Registrar {
223190
conformingTo: `protocol`
224191
)
225192
) {
226-
FunctionDeclSyntax.encode(modifiers: options.modifiers) { encoder in
193+
FunctionDeclSyntax.encode(
194+
modifiers: options.modifiersGenerator.generated
195+
) { encoder in
227196
let type = caseMap.type
228197
root.encoding(in: context, from: .coder(encoder, keyType: type))
229198
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#if SWIFT_SYNTAX_EXTENSION_MACRO_FIXED
2+
import XCTest
3+
4+
@testable import CodableMacroPlugin
5+
6+
final class AccessModifierTests: XCTestCase {
7+
8+
func testPublic() throws {
9+
assertMacroExpansion(
10+
"""
11+
@Codable
12+
@MemberInit
13+
public struct SomeCodable {
14+
let value: String
15+
}
16+
""",
17+
expandedSource:
18+
"""
19+
public struct SomeCodable {
20+
let value: String
21+
22+
public init(value: String) {
23+
self.value = value
24+
}
25+
}
26+
27+
extension SomeCodable: Decodable {
28+
public init(from decoder: any Decoder) throws {
29+
let container = try decoder.container(keyedBy: CodingKeys.self)
30+
self.value = try container.decode(String.self, forKey: CodingKeys.value)
31+
}
32+
}
33+
34+
extension SomeCodable: Encodable {
35+
public func encode(to encoder: any Encoder) throws {
36+
var container = encoder.container(keyedBy: CodingKeys.self)
37+
try container.encode(self.value, forKey: CodingKeys.value)
38+
}
39+
}
40+
41+
extension SomeCodable {
42+
enum CodingKeys: String, CodingKey {
43+
case value = "value"
44+
}
45+
}
46+
"""
47+
)
48+
}
49+
50+
func testPackage() throws {
51+
assertMacroExpansion(
52+
"""
53+
@Codable
54+
@MemberInit
55+
package struct SomeCodable {
56+
let value: String
57+
}
58+
""",
59+
expandedSource:
60+
"""
61+
package struct SomeCodable {
62+
let value: String
63+
64+
package init(value: String) {
65+
self.value = value
66+
}
67+
}
68+
69+
extension SomeCodable: Decodable {
70+
package init(from decoder: any Decoder) throws {
71+
let container = try decoder.container(keyedBy: CodingKeys.self)
72+
self.value = try container.decode(String.self, forKey: CodingKeys.value)
73+
}
74+
}
75+
76+
extension SomeCodable: Encodable {
77+
package func encode(to encoder: any Encoder) throws {
78+
var container = encoder.container(keyedBy: CodingKeys.self)
79+
try container.encode(self.value, forKey: CodingKeys.value)
80+
}
81+
}
82+
83+
extension SomeCodable {
84+
enum CodingKeys: String, CodingKey {
85+
case value = "value"
86+
}
87+
}
88+
"""
89+
)
90+
}
91+
92+
func testOthers() throws {
93+
for modifier in ["internal", "fileprivate", "private", ""] {
94+
let prefix = modifier.isEmpty ? "" : "\(modifier) "
95+
assertMacroExpansion(
96+
"""
97+
@Codable
98+
@MemberInit
99+
\(prefix)struct SomeCodable {
100+
let value: String
101+
}
102+
""",
103+
expandedSource:
104+
"""
105+
\(prefix)struct SomeCodable {
106+
let value: String
107+
108+
init(value: String) {
109+
self.value = value
110+
}
111+
}
112+
113+
extension SomeCodable: Decodable {
114+
init(from decoder: any Decoder) throws {
115+
let container = try decoder.container(keyedBy: CodingKeys.self)
116+
self.value = try container.decode(String.self, forKey: CodingKeys.value)
117+
}
118+
}
119+
120+
extension SomeCodable: Encodable {
121+
func encode(to encoder: any Encoder) throws {
122+
var container = encoder.container(keyedBy: CodingKeys.self)
123+
try container.encode(self.value, forKey: CodingKeys.value)
124+
}
125+
}
126+
127+
extension SomeCodable {
128+
enum CodingKeys: String, CodingKey {
129+
case value = "value"
130+
}
131+
}
132+
"""
133+
)
134+
}
135+
}
136+
}
137+
#endif

0 commit comments

Comments
 (0)