Skip to content

Commit ee54b7d

Browse files
authored
fix: removed unnecessary container variable warning for enum decoding expansion (#156)
* fix: removed unnecessary container variable warning for enum decoding expansion * test: fixed tests in older Swift versions
1 parent c2a6cc9 commit ee54b7d

File tree

2 files changed

+133
-1
lines changed

2 files changed

+133
-1
lines changed

Sources/PluginCore/Variables/Enum/Switcher/AdjacentlyTaggableSwitcher.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ extension InternallyTaggedEnumSwitcher: AdjacentlyTaggableSwitcher {
244244
).combined()
245245

246246
if containerType.isOptionalTypeSyntax {
247+
let needsContainer = location.cases.contains { variable, _ in
248+
variable.variables.contains { $0.label != nil }
249+
}
247250
let topContainerOptional = decodingNode.children
248251
.flatMap(\.value.linkedVariables)
249252
.allSatisfy { variable in
@@ -256,7 +259,7 @@ extension InternallyTaggedEnumSwitcher: AdjacentlyTaggableSwitcher {
256259
}
257260

258261
let header: SyntaxNodeString =
259-
topContainerOptional && !rawRepresentable
262+
needsContainer && topContainerOptional && !rawRepresentable
260263
? "if let \(container) = \(container), let \(location.container) = \(location.container)"
261264
: "if let \(container) = \(container)"
262265
try! IfExprSyntax(header) {

Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Foundation
12
import HelperCoders
23
import MetaCodable
34
import Testing
@@ -673,4 +674,132 @@ struct CodedAtEnumTests {
673674
)
674675
}
675676
}
677+
678+
struct WithOnlyAssociatedVariablesAtTopLevel {
679+
@Codable
680+
@CodedAt("type")
681+
enum TypeObject {
682+
case type1(Type1)
683+
684+
@Codable
685+
struct Type1 {
686+
let int: Int
687+
}
688+
}
689+
690+
@Test
691+
func expansion() throws {
692+
assertMacroExpansion(
693+
"""
694+
@Codable
695+
@CodedAt("type")
696+
enum TypeObject {
697+
case type1(Int)
698+
}
699+
""",
700+
expandedSource:
701+
"""
702+
enum TypeObject {
703+
case type1(Int)
704+
}
705+
706+
extension TypeObject: Decodable {
707+
init(from decoder: any Decoder) throws {
708+
var typeContainer: KeyedDecodingContainer<CodingKeys>?
709+
let container = try? decoder.container(keyedBy: CodingKeys.self)
710+
if let container = container {
711+
typeContainer = container
712+
} else {
713+
typeContainer = nil
714+
}
715+
if let typeContainer = typeContainer {
716+
let typeString: String?
717+
do {
718+
typeString = try typeContainer.decodeIfPresent(String.self, forKey: CodingKeys.type) ?? nil
719+
} catch {
720+
typeString = nil
721+
}
722+
if let typeString = typeString {
723+
switch typeString {
724+
case "type1":
725+
let _0: Int
726+
_0 = try Int(from: decoder)
727+
self = .type1(_0)
728+
return
729+
default:
730+
break
731+
}
732+
}
733+
}
734+
let context = DecodingError.Context(
735+
codingPath: decoder.codingPath,
736+
debugDescription: "Couldn't match any cases."
737+
)
738+
throw DecodingError.typeMismatch(Self.self, context)
739+
}
740+
}
741+
742+
extension TypeObject: Encodable {
743+
func encode(to encoder: any Encoder) throws {
744+
let container = encoder.container(keyedBy: CodingKeys.self)
745+
var typeContainer = container
746+
switch self {
747+
case .type1(let _0):
748+
try typeContainer.encode("type1", forKey: CodingKeys.type)
749+
try _0.encode(to: encoder)
750+
}
751+
}
752+
}
753+
754+
extension TypeObject {
755+
enum CodingKeys: String, CodingKey {
756+
case type = "type"
757+
}
758+
}
759+
"""
760+
)
761+
}
762+
763+
@Test
764+
func decodingAndEncoding() throws {
765+
let original = TypeObject.type1(TypeObject.Type1(int: 42))
766+
let encoded = try JSONEncoder().encode(original)
767+
let decoded = try JSONDecoder().decode(
768+
TypeObject.self, from: encoded)
769+
if case .type1(let data) = decoded {
770+
#expect(data.int == 42)
771+
} else {
772+
Issue.record("Expected type1 case")
773+
}
774+
}
775+
776+
@Test
777+
func decodingFromJSON() throws {
778+
let jsonStr = """
779+
{
780+
"type": "type1",
781+
"int": 42
782+
}
783+
"""
784+
let jsonData = try #require(jsonStr.data(using: .utf8))
785+
let decoded = try JSONDecoder().decode(
786+
TypeObject.self, from: jsonData)
787+
if case .type1(let data) = decoded {
788+
#expect(data.int == 42)
789+
} else {
790+
Issue.record("Expected type1 case")
791+
}
792+
}
793+
794+
@Test
795+
func encodingToJSON() throws {
796+
let original = TypeObject.type1(TypeObject.Type1(int: 42))
797+
let encoded = try JSONEncoder().encode(original)
798+
let json =
799+
try JSONSerialization.jsonObject(with: encoded)
800+
as! [String: Any]
801+
#expect(json["type"] as? String == "type1")
802+
#expect(json["int"] as? Int == 42)
803+
}
804+
}
676805
}

0 commit comments

Comments
 (0)