From f684135c511ce506b17bb2f006761f754d0eff61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Pi=C3=B1era?= Date: Wed, 29 Oct 2025 20:05:44 +0100 Subject: [PATCH 1/2] feat: add support for strictMemorySafety SwiftSetting and improve error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds two important improvements to PackageInfo decoding: 1. **Add strictMemorySafety support**: Adds the strictMemorySafety setting introduced in Swift Package Manager 6.2 (SE-0458). This setting enables strict memory safety checking and was causing decoding failures when packages used this new setting. 2. **Improve error handling**: Wraps legacy format decoding in a try-catch block with a custom SettingDecodingError that provides actionable diagnostics including: - Exact location in the JSON structure (coding path) - Which tool the setting was for - Available keys in the malformed setting - Helpful guidance about likely causes Changes: - Add strictMemorySafety case to SettingName enum - Add strictMemorySafety(String) case to Kind enum - Add SettingDecodingError with descriptive error messages - Handle strictMemorySafety in decoder switch statement - Handle strictMemorySafety in encoder switch statement This fixes decoding errors like: ``` keyNotFound(CodingKeys(stringValue: "name", intValue: nil), ...) ``` And replaces them with: ``` Failed to decode target build setting at 'targets.Index 3.settings.Index 0'. Expected either 'kind' (Xcode 14+ format) or 'name' (legacy format) key, but neither was found. Tool: swift Available keys: tool, condition, kind ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Sources/XcodeGraph/PackageInfo.swift | 41 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/Sources/XcodeGraph/PackageInfo.swift b/Sources/XcodeGraph/PackageInfo.swift index 781c00e0..4bf4909a 100644 --- a/Sources/XcodeGraph/PackageInfo.swift +++ b/Sources/XcodeGraph/PackageInfo.swift @@ -455,6 +455,7 @@ extension PackageInfo.Target { case enableExperimentalFeature case interoperabilityMode case defaultIsolation + case strictMemorySafety } /// An individual build setting. @@ -506,6 +507,26 @@ extension PackageInfo.Target { case enableExperimentalFeature(String) case interoperabilityMode(String) case defaultIsolation(String) + case strictMemorySafety(String) + } + + enum SettingDecodingError: Error, CustomStringConvertible { + case missingRequiredKeys(tool: Tool, availableKeys: [String], codingPath: [CodingKey]) + + var description: String { + switch self { + case let .missingRequiredKeys(tool, availableKeys, codingPath): + let path = codingPath.map(\.stringValue).joined(separator: ".") + return """ + Failed to decode target build setting at '\(path)'. + Expected either 'kind' (Xcode 14+ format) or 'name' (legacy format) key, but neither was found. + Tool: \(tool) + Available keys: \(availableKeys.joined(separator: ", ")) + + This usually indicates a malformed Package.swift manifest or an unsupported Swift Package Manager version. + """ + } + } } public init(from decoder: Decoder) throws { @@ -545,10 +566,24 @@ extension PackageInfo.Target { case let .defaultIsolation(value): name = .defaultIsolation self.value = [value] + case let .strictMemorySafety(value): + name = .strictMemorySafety + self.value = [value] } } else { - name = try container.decode(SettingName.self, forKey: .name) - value = try container.decode([String].self, forKey: .value) + // Legacy format - try to decode name + do { + name = try container.decode(SettingName.self, forKey: .name) + value = try container.decode([String].self, forKey: .value) + } catch { + // Neither 'kind' nor 'name' was found - provide helpful error + let availableKeys = container.allKeys.map(\.stringValue) + throw SettingDecodingError.missingRequiredKeys( + tool: tool, + availableKeys: availableKeys, + codingPath: decoder.codingPath + ) + } } } @@ -578,6 +613,8 @@ extension PackageInfo.Target { try container.encode(Kind.swiftLanguageMode(value.first!), forKey: .kind) case .defaultIsolation: try container.encode(Kind.defaultIsolation(value.first!), forKey: .kind) + case .strictMemorySafety: + try container.encode(Kind.strictMemorySafety(value.first!), forKey: .kind) } } } From 7dd9e4f7c0ae3547a7d2b4cc7f415d610814a27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Pi=C3=B1era?= Date: Wed, 29 Oct 2025 20:11:15 +0100 Subject: [PATCH 2/2] Fix error type --- Sources/XcodeGraph/PackageInfo.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/XcodeGraph/PackageInfo.swift b/Sources/XcodeGraph/PackageInfo.swift index 4bf4909a..fe1f752e 100644 --- a/Sources/XcodeGraph/PackageInfo.swift +++ b/Sources/XcodeGraph/PackageInfo.swift @@ -436,7 +436,7 @@ extension PackageInfo.Target { /// A namespace for target-specific build settings. public enum TargetBuildSettingDescription { /// The tool for which a build setting is declared. - public enum Tool: String, Codable, Hashable, CaseIterable { + public enum Tool: String, Codable, Hashable, CaseIterable, Sendable { case c case cxx case swift @@ -510,10 +510,10 @@ extension PackageInfo.Target { case strictMemorySafety(String) } - enum SettingDecodingError: Error, CustomStringConvertible { + enum SettingDecodingError: LocalizedError { case missingRequiredKeys(tool: Tool, availableKeys: [String], codingPath: [CodingKey]) - var description: String { + var errorDescription: String? { switch self { case let .missingRequiredKeys(tool, availableKeys, codingPath): let path = codingPath.map(\.stringValue).joined(separator: ".")