From a1cdf83d5d88434f74492982eb53780ca5b02ee7 Mon Sep 17 00:00:00 2001 From: Soumya Ranjan Mahunt Date: Mon, 2 Feb 2026 09:13:08 +0530 Subject: [PATCH 1/2] fix: removed unnecessary container variable warning for enum decoding expansion --- .../Switcher/AdjacentlyTaggableSwitcher.swift | 5 +- .../CodedAt/CodedAtEnumTests.swift | 129 ++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/Sources/PluginCore/Variables/Enum/Switcher/AdjacentlyTaggableSwitcher.swift b/Sources/PluginCore/Variables/Enum/Switcher/AdjacentlyTaggableSwitcher.swift index bc6e73478..1ad68da0b 100644 --- a/Sources/PluginCore/Variables/Enum/Switcher/AdjacentlyTaggableSwitcher.swift +++ b/Sources/PluginCore/Variables/Enum/Switcher/AdjacentlyTaggableSwitcher.swift @@ -244,6 +244,9 @@ extension InternallyTaggedEnumSwitcher: AdjacentlyTaggableSwitcher { ).combined() if containerType.isOptionalTypeSyntax { + let needsContainer = location.cases.contains { variable, _ in + variable.variables.contains { $0.label != nil } + } let topContainerOptional = decodingNode.children .flatMap(\.value.linkedVariables) .allSatisfy { variable in @@ -256,7 +259,7 @@ extension InternallyTaggedEnumSwitcher: AdjacentlyTaggableSwitcher { } let header: SyntaxNodeString = - topContainerOptional && !rawRepresentable + needsContainer && topContainerOptional && !rawRepresentable ? "if let \(container) = \(container), let \(location.container) = \(location.container)" : "if let \(container) = \(container)" try! IfExprSyntax(header) { diff --git a/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift b/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift index bb7eddd2f..e6c730b1a 100644 --- a/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift +++ b/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift @@ -1,3 +1,4 @@ +import Foundation import HelperCoders import MetaCodable import Testing @@ -673,4 +674,132 @@ struct CodedAtEnumTests { ) } } + + struct WithOnlyAssociatedVariablesAtTopLevel { + @Codable + @CodedAt("type") + enum TypeObject { + case type1(Type1) + + @Codable + struct Type1 { + let int: Int + } + } + + @Test + func expansion() throws { + assertMacroExpansion( + """ + @Codable + @CodedAt("type") + enum TypeObject { + case type1(Int) + } + """, + expandedSource: + """ + enum TypeObject { + case type1(Int) + } + + extension TypeObject: Decodable { + init(from decoder: any Decoder) throws { + var typeContainer: KeyedDecodingContainer? + let container = try? decoder.container(keyedBy: CodingKeys.self) + if let container = container { + typeContainer = container + } else { + typeContainer = nil + } + if let typeContainer = typeContainer { + let typeString: String? + do { + typeString = try typeContainer.decodeIfPresent(String.self, forKey: CodingKeys.type) ?? nil + } catch { + typeString = nil + } + if let typeString = typeString { + switch typeString { + case "type1": + let _0: Int + _0 = try Int(from: decoder) + self = .type1(_0) + return + default: + break + } + } + } + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Couldn't match any cases." + ) + throw DecodingError.typeMismatch(Self.self, context) + } + } + + extension TypeObject: Encodable { + func encode(to encoder: any Encoder) throws { + let container = encoder.container(keyedBy: CodingKeys.self) + var typeContainer = container + switch self { + case .type1(let _0): + try typeContainer.encode("type1", forKey: CodingKeys.type) + try _0.encode(to: encoder) + } + } + } + + extension TypeObject { + enum CodingKeys: String, CodingKey { + case type = "type" + } + } + """ + ) + } + + @Test + func decodingAndEncoding() throws { + let original = TypeObject.type1(TypeObject.Type1(int: 42)) + let encoded = try JSONEncoder().encode(original) + let decoded = try JSONDecoder().decode( + TypeObject.self, from: encoded) + if case .type1(let data) = decoded { + #expect(data.int == 42) + } else { + #expect(Bool(false), "Expected type1 case") + } + } + + @Test + func decodingFromJSON() throws { + let jsonStr = """ + { + "type": "type1", + "int": 42 + } + """ + let jsonData = try #require(jsonStr.data(using: .utf8)) + let decoded = try JSONDecoder().decode( + TypeObject.self, from: jsonData) + if case .type1(let data) = decoded { + #expect(data.int == 42) + } else { + #expect(Bool(false), "Expected type1 case") + } + } + + @Test + func encodingToJSON() throws { + let original = TypeObject.type1(TypeObject.Type1(int: 42)) + let encoded = try JSONEncoder().encode(original) + let json = + try JSONSerialization.jsonObject(with: encoded) + as! [String: Any] + #expect(json["type"] as? String == "type1") + #expect(json["int"] as? Int == 42) + } + } } From 39c2d8ad17f4a6a5b4d9a02214b8eb4216c2431e Mon Sep 17 00:00:00 2001 From: Soumya Ranjan Mahunt Date: Mon, 2 Feb 2026 19:38:02 +0530 Subject: [PATCH 2/2] test: fixed tests in older Swift versions --- Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift b/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift index e6c730b1a..6e9a8b19b 100644 --- a/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift +++ b/Tests/MetaCodableTests/CodedAt/CodedAtEnumTests.swift @@ -769,7 +769,7 @@ struct CodedAtEnumTests { if case .type1(let data) = decoded { #expect(data.int == 42) } else { - #expect(Bool(false), "Expected type1 case") + Issue.record("Expected type1 case") } } @@ -787,7 +787,7 @@ struct CodedAtEnumTests { if case .type1(let data) = decoded { #expect(data.int == 42) } else { - #expect(Bool(false), "Expected type1 case") + Issue.record("Expected type1 case") } }