Skip to content

Commit c1097bb

Browse files
committed
feat: added externally tagged enum support
1 parent 4bfeac3 commit c1097bb

59 files changed

Lines changed: 2770 additions & 309 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Sources/CodableMacroPlugin/Attributes/Codable/Codable.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@ struct Codable: Attribute {
4040
/// attached declaration.
4141
///
4242
/// Builds diagnoser that validates attached declaration
43-
/// is `struct` or `class` declaration and macro
43+
/// is `struct`/`class`/`enum` declaration and macro
4444
/// usage is not duplicated for the same declaration.
4545
///
4646
/// - Returns: The built diagnoser instance.
4747
func diagnoser() -> DiagnosticProducer {
4848
return AggregatedDiagnosticProducer {
49-
expect(syntaxes: StructDeclSyntax.self, ClassDeclSyntax.self)
49+
expect(
50+
syntaxes: StructDeclSyntax.self, ClassDeclSyntax.self,
51+
EnumDeclSyntax.self
52+
)
5053
cantDuplicate()
5154
}
5255
}

Sources/CodableMacroPlugin/Attributes/Codable/CodingKeys/CodingKeys.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,23 @@ struct CodingKeys: PeerAttribute {
4343
/// has `Codable` macro attached and macro usage
4444
/// is not duplicated for the same declaration.
4545
///
46+
/// For enum case declarations this attribute can be attached
47+
/// without `Codable` macro.
48+
///
4649
/// - Returns: The built diagnoser instance.
4750
func diagnoser() -> DiagnosticProducer {
4851
return AggregatedDiagnosticProducer {
49-
mustBeCombined(with: Codable.self)
5052
cantDuplicate()
53+
`if`(
54+
isStruct || isClass || isEnum,
55+
mustBeCombined(with: Codable.self),
56+
else: expect(syntaxes: EnumCaseDeclSyntax.self)
57+
)
5158
}
5259
}
5360
}
5461

55-
extension Registration {
62+
extension Registration where Key == [String] {
5663
/// Update current registration `CodingKey` path data.
5764
///
5865
/// New registration is updated with the transformed `CodingKey` path
@@ -65,6 +72,6 @@ extension Registration {
6572
) -> Self where D: AttributableDeclSyntax {
6673
guard let attr = CodingKeys(from: decl) else { return self }
6774
let strategy = attr.strategy
68-
return self.updating(with: strategy.transform(keyPath: self.keyPath))
75+
return self.updating(with: strategy.transform(keyPath: self.key))
6976
}
7077
}

Sources/CodableMacroPlugin/Attributes/Codable/IgnoreCodingInitialized.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,23 @@ struct IgnoreCodingInitialized: PeerAttribute {
3636
/// has `Codable` macro attached and macro usage
3737
/// is not duplicated for the same declaration.
3838
///
39+
/// For enum case declarations this attribute can be attached
40+
/// without `Codable` macro.
41+
///
3942
/// - Returns: The built diagnoser instance.
4043
func diagnoser() -> DiagnosticProducer {
4144
return AggregatedDiagnosticProducer {
42-
mustBeCombined(with: Codable.self)
4345
shouldNotDuplicate()
46+
`if`(
47+
isStruct || isClass || isEnum,
48+
mustBeCombined(with: Codable.self),
49+
else: expect(syntaxes: EnumCaseDeclSyntax.self)
50+
)
4451
}
4552
}
4653
}
4754

48-
extension Registration where Var: NamedVariable {
55+
extension Registration where Var: ValuedVariable {
4956
/// Update registration whether decoding/encoding to be ignored.
5057
///
5158
/// New registration is updated with decoding and encoding condition
@@ -57,7 +64,7 @@ extension Registration where Var: NamedVariable {
5764
/// data.
5865
func checkInitializedCodingIgnored<D: AttributableDeclSyntax>(
5966
attachedAt decl: D
60-
) -> Registration<Decl, ConditionalCodingVariable<Var>> {
67+
) -> Registration<Decl, Key, ConditionalCodingVariable<Var>> {
6168
typealias Output = ConditionalCodingVariable<Var>
6269
let attr = IgnoreCodingInitialized(from: decl)
6370
let code = attr != nil ? self.variable.value == nil : nil
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
@_implementationOnly import SwiftSyntax
2+
3+
/// Attribute type for `CodedAs` macro-attribute.
4+
///
5+
/// This type can validate`CodedAs` macro-attribute
6+
/// usage and extract data for `Codable` macro to
7+
/// generate implementation.
8+
struct CodedAs: PropertyAttribute {
9+
/// The node syntax provided
10+
/// during initialization.
11+
let node: AttributeSyntax
12+
13+
/// The alternate value expression provided.
14+
var expr: ExprSyntax {
15+
return node.arguments!
16+
.as(LabeledExprListSyntax.self)!.first!.expression
17+
}
18+
19+
/// Creates a new instance with the provided node.
20+
///
21+
/// The initializer fails to create new instance if the name
22+
/// of the provided node is different than this attribute.
23+
///
24+
/// - Parameter node: The attribute syntax to create with.
25+
/// - Returns: Newly created attribute instance.
26+
init?(from node: AttributeSyntax) {
27+
guard
28+
node.attributeName.as(IdentifierTypeSyntax.self)!
29+
.description == Self.name
30+
else { return nil }
31+
self.node = node
32+
}
33+
34+
/// Builds diagnoser that can validate this macro
35+
/// attached declaration.
36+
///
37+
/// The following conditions are checked by the
38+
/// built diagnoser:
39+
/// * Attached declaration is an enum-case declaration.
40+
/// * Macro usage is not duplicated for the same declaration.
41+
/// * This attribute isn't used combined with `IgnoreCoding`
42+
/// attribute.
43+
///
44+
/// - Returns: The built diagnoser instance.
45+
func diagnoser() -> DiagnosticProducer {
46+
return AggregatedDiagnosticProducer {
47+
expect(syntaxes: EnumCaseDeclSyntax.self)
48+
cantDuplicate()
49+
cantBeCombined(with: IgnoreCoding.self)
50+
}
51+
}
52+
}
53+
54+
extension Registration where Key == ExprSyntax?, Decl: AttributableDeclSyntax {
55+
/// Update registration with alternate value expression data.
56+
///
57+
/// New registration is updated with value expression data that will be
58+
/// used for decoding/encoding, if provided and registration doesn't
59+
/// already have a value.
60+
///
61+
/// - Returns: Newly built registration with value expression data.
62+
func checkForAlternateValue() -> Self {
63+
guard
64+
self.key == nil,
65+
let attr = CodedAs(from: self.decl)
66+
else { return self }
67+
return self.updating(with: attr.expr)
68+
}
69+
}

Sources/CodableMacroPlugin/Attributes/CodedBy.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ where
7070
/// used for decoding/encoding, if provided.
7171
///
7272
/// - Returns: Newly built registration with helper expression data.
73-
func useHelperCoderIfExists() -> Registration<Decl, CodedByOutput> {
74-
guard let attr = CodedBy(from: self.declaration)
73+
func useHelperCoderIfExists() -> Registration<Decl, Key, CodedByOutput> {
74+
guard let attr = CodedBy(from: self.decl)
7575
else { return self.updating(with: self.variable.any) }
7676
let newVar = self.variable.with(helper: attr.expr)
7777
return self.updating(with: newVar.any)

Sources/CodableMacroPlugin/Attributes/Default.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ where
6969
/// used for decoding failure and memberwise initializer(s), if provided.
7070
///
7171
/// - Returns: Newly built registration with default expression data.
72-
func addDefaultValueIfExists() -> Registration<Decl, DefOutput> {
73-
guard let attr = Default(from: self.declaration)
72+
func addDefaultValueIfExists() -> Registration<Decl, Key, DefOutput> {
73+
guard let attr = Default(from: self.decl)
7474
else { return self.updating(with: self.variable.any) }
7575
let newVar = self.variable.with(default: attr.expr)
7676
return self.updating(with: newVar.any)

Sources/CodableMacroPlugin/Attributes/IgnoreCoding/IgnoreCoding.swift

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,25 @@ struct IgnoreCoding: PropertyAttribute {
3030
/// Builds diagnoser that can validate this macro
3131
/// attached declaration.
3232
///
33-
/// The following conditions are checked by the
34-
/// built diagnoser:
35-
/// * Attached declaration is a variable declaration.
36-
/// * Attached variable declaration has default
37-
/// initialization or variable is a computed property.
38-
/// * This attribute isn't used combined with `CodedIn`
39-
/// and `CodedAt` attribute.
40-
/// * Additionally, warning generated if macro usage
41-
/// is duplicated for the same declaration.
33+
/// The following conditions are checked by the built diagnoser:
34+
/// * Attached declaration is a variable or enum case declaration.
35+
/// * Attached variable declaration has default initialization or
36+
/// variable is a computed property.
37+
/// * This attribute isn't used combined with `CodedIn` and
38+
/// `CodedAt` attribute.
39+
/// * Additionally, warning generated if macro usage is duplicated
40+
/// for the same declaration.
4241
///
4342
/// - Returns: The built diagnoser instance.
4443
func diagnoser() -> DiagnosticProducer {
4544
return AggregatedDiagnosticProducer {
46-
attachedToInitializedVariable()
4745
cantBeCombined(with: CodedIn.self)
4846
cantBeCombined(with: CodedAt.self)
4947
shouldNotDuplicate()
48+
`if`(
49+
isVariable, attachedToInitializedVariable(),
50+
else: expect(syntaxes: EnumCaseDeclSyntax.self)
51+
)
5052
}
5153
}
5254
}
@@ -69,12 +71,11 @@ extension Registration where Decl: AttributableDeclSyntax {
6971
///
7072
/// - Returns: Newly built registration with conditional decoding/encoding
7173
/// data.
72-
func checkCodingIgnored() -> Registration<Decl, ConditionalOutput> {
74+
func checkCodingIgnored() -> Registration<Decl, Key, ConditionalOutput> {
7375
typealias Output = ConditionalOutput
74-
let declaration = self.declaration
75-
let ignoreCoding = IgnoreCoding(from: declaration) != nil
76-
let ignoreDecoding = IgnoreDecoding(from: declaration) != nil
77-
let ignoreEncoding = IgnoreEncoding(from: declaration) != nil
76+
let ignoreCoding = IgnoreCoding(from: self.decl) != nil
77+
let ignoreDecoding = IgnoreDecoding(from: self.decl) != nil
78+
let ignoreEncoding = IgnoreEncoding(from: self.decl) != nil
7879
let decode = !ignoreCoding && !ignoreDecoding
7980
let encode = !ignoreCoding && !ignoreEncoding
8081
let options = Output.Options(decode: decode, encode: encode)

Sources/CodableMacroPlugin/Attributes/IgnoreCoding/IgnoreDecoding.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,26 @@ struct IgnoreDecoding: PropertyAttribute {
2727
self.node = node
2828
}
2929

30-
/// Builds diagnoser that can validate this macro
31-
/// attached declaration.
30+
/// Builds diagnoser that can validate this macro attached declaration.
3231
///
33-
/// The following conditions are checked by the
34-
/// built diagnoser:
35-
/// * Attached declaration is a variable declaration.
36-
/// * Attached variable declaration has default
37-
/// initialization or variable is a computed property.
38-
/// * Additionally, warning generated if macro usage
39-
/// is duplicated for the same declaration.
40-
/// * Additionally, warning also generated if this
41-
/// attribute is used combined with `IgnoreCoding`
42-
/// attribute.
32+
/// The following conditions are checked by the built diagnoser:
33+
/// * Attached declaration is a variable or enum case declaration.
34+
/// * Attached variable declaration has default initialization or
35+
/// variable is a computed property.
36+
/// * Additionally, warning generated if macro usage is duplicated
37+
/// for the same declaration.
38+
/// * Additionally, warning also generated if this attribute is used
39+
/// combined with `IgnoreCoding` attribute.
4340
///
4441
/// - Returns: The built diagnoser instance.
4542
func diagnoser() -> DiagnosticProducer {
4643
return AggregatedDiagnosticProducer {
47-
attachedToInitializedVariable()
4844
shouldNotDuplicate()
4945
shouldNotBeCombined(with: IgnoreCoding.self)
46+
`if`(
47+
isVariable, attachedToInitializedVariable(),
48+
else: expect(syntaxes: EnumCaseDeclSyntax.self)
49+
)
5050
}
5151
}
5252
}

Sources/CodableMacroPlugin/Attributes/IgnoreCoding/IgnoreEncoding.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,19 @@ struct IgnoreEncoding: PropertyAttribute {
2727
self.node = node
2828
}
2929

30-
/// Builds diagnoser that can validate this macro
31-
/// attached declaration.
30+
/// Builds diagnoser that can validate this macro attached declaration.
3231
///
33-
/// The following conditions are checked by the
34-
/// built diagnoser:
35-
/// * Attached declaration is a variable declaration.
36-
/// * Additionally, warning generated if macro usage
37-
/// is duplicated for the same declaration.
38-
/// * Additionally, warning also generated if this
39-
/// attribute is used combined with `IgnoreCoding`
40-
/// attribute.
32+
/// The following conditions are checked by the built diagnoser:
33+
/// * Attached declaration is a variable or enum case declaration.
34+
/// * Additionally, warning generated if macro usage is duplicated
35+
/// for the same declaration.
36+
/// * Additionally, warning also generated if this attribute is used
37+
/// combined with `IgnoreCoding` attribute.
4138
///
4239
/// - Returns: The built diagnoser instance.
4340
func diagnoser() -> DiagnosticProducer {
4441
return AggregatedDiagnosticProducer {
45-
expect(syntaxes: VariableDeclSyntax.self)
42+
expect(syntaxes: VariableDeclSyntax.self, EnumCaseDeclSyntax.self)
4643
shouldNotDuplicate()
4744
shouldNotBeCombined(with: IgnoreCoding.self)
4845
}

Sources/CodableMacroPlugin/Attributes/KeyPath/KeyPathProvider.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ extension CodedIn: KeyPathProvider {
8686
}
8787
}
8888

89-
extension Registration where Decl: AttributableDeclSyntax {
89+
extension Registration where Key == [String] {
9090
/// Update registration with `CodingKey` path data.
9191
///
9292
/// New registration is updated with the provided `CodingKey` path from provider,
@@ -96,13 +96,13 @@ extension Registration where Decl: AttributableDeclSyntax {
9696
/// - Returns: Newly built registration with additional `CodingKey` path data.
9797
func registerKeyPath(
9898
provider: KeyPathProvider
99-
) -> Registration<Decl, KeyedVariable<Var>> {
99+
) -> Registration<Decl, Key, KeyedVariable<Var>> {
100100
typealias Output = KeyedVariable<Var>
101101
let options = Output.Options(code: provider.provided)
102102
let newVar = Output(base: self.variable, options: options)
103103
let output = self.updating(with: newVar)
104104
guard provider.provided else { return output }
105-
let updatedPath = provider.keyPath(withExisting: self.keyPath)
105+
let updatedPath = provider.keyPath(withExisting: self.key)
106106
return output.updating(with: updatedPath)
107107
}
108108
}

0 commit comments

Comments
 (0)