Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import Foundation
when no matching type is found. The default value for this property is `nil`.
*/
@attached(peer, names: suffixed(CodableStrategy))
@attached(extension, names: arbitrary)
@attached(member, names: arbitrary)
public macro PolymorphicCodableStrategyProviding(
identifierCodingKey: String = "type",
matchingTypes: [PolymorphicDecodableType.Type],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,45 @@ import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct PolymorphicCodableStrategyProvidingMacro: PeerMacro {
public struct PolymorphicCodableStrategyProvidingMacro {}

// MARK: - MemberMacro

extension PolymorphicCodableStrategyProvidingMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else {
throw CodableKitError.message("Macro must be attached to a protocol.")
}

let identifier = protocolDecl.name.text
let strategyStructName = "\(identifier)CodableStrategy"

return [
DeclSyntax("typealias Polymorphic = PolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias OptionalPolymorphic = OptionalPolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias PolymorphicArray = PolymorphicArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<\(raw: strategyStructName)>"),
]
}
Comment on lines +17 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make generated typealiases respect access level and guard on invalid args.

[HIGH] Currently the seven typealiases are emitted without access modifiers, which makes them internal even when the host protocol is public—this is a source-breaking visibility regression. Also, the MemberMacro should short-circuit on invalid arguments to avoid emitting aliases when the PeerMacro fails.

Apply this diff in MemberMacro expansion:

 extension PolymorphicCodableStrategyProvidingMacro: MemberMacro {
   public static func expansion(
     of node: AttributeSyntax,
     providingMembersOf declaration: some DeclGroupSyntax,
     in context: some MacroExpansionContext
   ) throws -> [DeclSyntax] {
     guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else {
       throw CodableKitError.message("Macro must be attached to a protocol.")
     }

-    let identifier = protocolDecl.name.text
-    let strategyStructName = "\(identifier)CodableStrategy"
+    // Validate arguments here as well to avoid cascading errors when PeerMacro fails.
+    if let args = node.arguments?.as(LabeledExprListSyntax.self) {
+      if SyntaxHelper.findArgument(named: "identifierCodingKey", in: args)
+        .flatMap({ SyntaxHelper.extractString(from: $0) }) == "" {
+        throw CodableKitError.message("Invalid identifierCodingKey: expected a non-empty string.")
+      }
+      guard SyntaxHelper.findArgument(named: "matchingTypes", in: args) != nil else {
+        throw CodableKitError.message("Missing required arguments")
+      }
+    }
+
+    let identifier = protocolDecl.name.text
+    let strategyStructName = "\(identifier)CodableStrategy"
+    // Mirror host protocol's access level.
+    let accessModifier = (protocolDecl.accessLevel != .internal) ? protocolDecl.accessLevel.rawValue + " " : ""

-    return [
-      DeclSyntax("typealias Polymorphic = PolymorphicValue<\(raw: strategyStructName)>"),
-      DeclSyntax("typealias OptionalPolymorphic = OptionalPolymorphicValue<\(raw: strategyStructName)>"),
-      DeclSyntax("typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<\(raw: strategyStructName)>"),
-      DeclSyntax("typealias PolymorphicArray = PolymorphicArrayValue<\(raw: strategyStructName)>"),
-      DeclSyntax("typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<\(raw: strategyStructName)>"),
-      DeclSyntax("typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<\(raw: strategyStructName)>"),
-      DeclSyntax("typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<\(raw: strategyStructName)>"),
-    ]
+    return [
+      DeclSyntax("\(raw: accessModifier)typealias Polymorphic = PolymorphicValue<\(raw: strategyStructName)>"),
+      DeclSyntax("\(raw: accessModifier)typealias OptionalPolymorphic = OptionalPolymorphicValue<\(raw: strategyStructName)>"),
+      DeclSyntax("\(raw: accessModifier)typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<\(raw: strategyStructName)>"),
+      DeclSyntax("\(raw: accessModifier)typealias PolymorphicArray = PolymorphicArrayValue<\(raw: strategyStructName)>"),
+      DeclSyntax("\(raw: accessModifier)typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<\(raw: strategyStructName)>"),
+      DeclSyntax("\(raw: accessModifier)typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<\(raw: strategyStructName)>"),
+      DeclSyntax("\(raw: accessModifier)typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<\(raw: strategyStructName)>"),
+    ]
   }
 }

Additionally, make the shared helper visible across extensions (change shown below in a separate comment).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
extension PolymorphicCodableStrategyProvidingMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else {
throw CodableKitError.message("Macro must be attached to a protocol.")
}
let identifier = protocolDecl.name.text
let strategyStructName = "\(identifier)CodableStrategy"
return [
DeclSyntax("typealias Polymorphic = PolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias OptionalPolymorphic = OptionalPolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias PolymorphicArray = PolymorphicArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<\(raw: strategyStructName)>"),
]
}
extension PolymorphicCodableStrategyProvidingMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else {
throw CodableKitError.message("Macro must be attached to a protocol.")
}
// Validate arguments here as well to avoid cascading errors when PeerMacro fails.
if let args = node.arguments?.as(LabeledExprListSyntax.self) {
if SyntaxHelper.findArgument(named: "identifierCodingKey", in: args)
.flatMap({ SyntaxHelper.extractString(from: $0) }) == "" {
throw CodableKitError.message("Invalid identifierCodingKey: expected a non-empty string.")
}
guard SyntaxHelper.findArgument(named: "matchingTypes", in: args) != nil else {
throw CodableKitError.message("Missing required arguments")
}
}
let identifier = protocolDecl.name.text
let strategyStructName = "\(identifier)CodableStrategy"
// Mirror host protocol's access level.
let accessModifier = (protocolDecl.accessLevel != .internal) ? protocolDecl.accessLevel.rawValue + " " : ""
return [
DeclSyntax("\(raw: accessModifier)typealias Polymorphic = PolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("\(raw: accessModifier)typealias OptionalPolymorphic = OptionalPolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("\(raw: accessModifier)typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<\(raw: strategyStructName)>"),
DeclSyntax("\(raw: accessModifier)typealias PolymorphicArray = PolymorphicArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("\(raw: accessModifier)typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("\(raw: accessModifier)typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<\(raw: strategyStructName)>"),
DeclSyntax("\(raw: accessModifier)typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<\(raw: strategyStructName)>"),
]
}
}
🤖 Prompt for AI Agents
In
Sources/KarrotCodableKitMacros/PolymorphicCodableMacros/PolymorphicCodableStrategyMacro.swift
around lines 17–39, the generated typealias lines must preserve the host
protocol's access level and the macro must bail out when its attribute/peer
arguments are invalid: derive an accessPrefix string from protocolDecl.modifiers
(e.g., "public ", "internal ", etc.) and prepend it to each emitted "typealias
..." declaration (so the aliases have the same visibility as the protocol), and
before returning the aliases validate the attribute/peer arguments (inspect
node.arguments or the parsed peer info) and short-circuit by returning an empty
array if parsing/validation fails instead of emitting aliases; also ensure the
shared helper referenced elsewhere is made internal/public so it’s visible
across extensions as requested.

}

// MARK: - PeerMacro

extension PolymorphicCodableStrategyProvidingMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else { return [] }

guard
let arguments = node.arguments?.as(LabeledExprListSyntax.self),
let matchingTypes = SyntaxHelper.findArgument(named: "matchingTypes", in: arguments)
Expand Down Expand Up @@ -80,39 +109,3 @@ public struct PolymorphicCodableStrategyProvidingMacro: PeerMacro {
return protocolDecl.accessLevel.rawValue + " "
}
}

extension PolymorphicCodableStrategyProvidingMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else { return [] }

let accessModifier = accessModifier(from: protocolDecl)
let identifier = protocolDecl.name.text
let strategyStructName = "\(identifier)CodableStrategy"

return [
try ExtensionDeclSyntax(
"""
extension \(raw: identifier) {
\(raw: accessModifier)typealias Polymorphic = PolymorphicValue<\(raw: strategyStructName)>
\(raw: accessModifier)typealias OptionalPolymorphic = OptionalPolymorphicValue<\(raw: strategyStructName)>
\(raw: accessModifier)typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<\(
raw: strategyStructName
)>
\(raw: accessModifier)typealias PolymorphicArray = PolymorphicArrayValue<\(raw: strategyStructName)>
\(raw: accessModifier)typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<\(raw: strategyStructName)>
\(raw: accessModifier)typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<\(raw: strategyStructName)>
\(raw: accessModifier)typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<\(
raw: strategyStructName
)>
}
"""
),
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,24 @@ final class PolymorphicCodableStrategyProvidingMacroTests: XCTestCase {
""",
// when
expandedSource: """

public protocol Notice: Codable {
var type: String { get }
var title: String? { get }
var description: String { get }

typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>

typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>

typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>

typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>

typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>

typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>

typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
Comment on lines +52 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Assert public access for generated typealiases.

[HIGH] With members injected into a public protocol, the default access of nested declarations is internal. Tests should assert public typealias to lock API and prevent accidental visibility regressions.

Apply this diff to the expected expansion:

-          typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>
+          public typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>

-          typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>
+          public typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>

-          typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>
+          public typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>

-          typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>
+          public typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>

-          typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>
+          public typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>

-          typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>
+          public typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>

-          typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
+          public typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>
typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>
typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>
typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>
typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>
typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>
typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
public typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>
public typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>
public typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>
public typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>
public typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>
public typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>
public typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
🤖 Prompt for AI Agents
In
Tests/KarrotCodableMacrosTests/PolymorphicCodableMacrosTests/PolymorphicCodableStrategyProvidingMacroTests.swift
around lines 52 to 65, the generated nested typealiases are missing explicit
public visibility; update each declaration to be public typealias (e.g., public
typealias Polymorphic = ...) for all listed aliases (Polymorphic,
OptionalPolymorphic, LossyOptionalPolymorphic, PolymorphicArray,
OptionalPolymorphicArray, PolymorphicLossyArray, DefaultEmptyPolymorphicArray)
so the expected expansion asserts public access and prevents accidental
visibility regressions.

}

public struct NoticeCodableStrategy: PolymorphicCodableStrategy {
Expand All @@ -72,16 +85,6 @@ final class PolymorphicCodableStrategyProvidingMacroTests: XCTestCase {
)
}
}

extension Notice {
public typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>
public typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>
public typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>
public typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>
public typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>
public typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>
public typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
}
""",
macros: testMacros,
indentationWidth: .spaces(2)
Expand Down Expand Up @@ -114,6 +117,20 @@ final class PolymorphicCodableStrategyProvidingMacroTests: XCTestCase {
var type: String { get }
var title: String? { get }
var description: String { get }

typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>

typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>

typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>

typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>

typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>

typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>

typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
Comment on lines +121 to +133
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Mirror the public access assertion in the default-parameters case.

[HIGH] Same reasoning as above—ensure all seven aliases are public in the expected expansion.

Apply the same public typealias changes here.

🤖 Prompt for AI Agents
In
Tests/KarrotCodableMacrosTests/PolymorphicCodableMacrosTests/PolymorphicCodableStrategyProvidingMacroTests.swift
around lines 121 to 133 the seven typealias declarations are missing the public
access modifier; update each declaration to be a public typealias (make
Polymorphic, OptionalPolymorphic, LossyOptionalPolymorphic, PolymorphicArray,
OptionalPolymorphicArray, PolymorphicLossyArray, and
DefaultEmptyPolymorphicArray all declared as public typealias) so the expected
expansion matches the public default-parameters case.

}

public struct NoticeCodableStrategy: PolymorphicCodableStrategy {
Expand All @@ -136,16 +153,6 @@ final class PolymorphicCodableStrategyProvidingMacroTests: XCTestCase {
)
}
}

extension Notice {
public typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>
public typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>
public typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>
public typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>
public typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>
public typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>
public typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
}
""",
macros: testMacros,
indentationWidth: .spaces(2)
Expand Down Expand Up @@ -216,15 +223,19 @@ final class PolymorphicCodableStrategyProvidingMacroTests: XCTestCase {
var type: String
var title: String?
var description: String
}

extension Notice {
typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>

typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>

typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>

typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>

typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>

typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>

typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
Comment on lines 227 to 239
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Avoid emitting members when arguments are invalid; update test accordingly.

[HIGH] When identifierCodingKey is invalid (empty), the PeerMacro errors but the MemberMacro still injects typealiases referencing a non-generated NoticeCodableStrategy, causing cascading compile errors. Prefer no member emission on validation failure and assert that in tests.

Update expected expansion to exclude the seven typealiases:

-        protocol Notice: Codable {
-          var type: String
-          var title: String?
-          var description: String
-
-          typealias Polymorphic = PolymorphicValue<NoticeCodableStrategy>
-          typealias OptionalPolymorphic = OptionalPolymorphicValue<NoticeCodableStrategy>
-          typealias LossyOptionalPolymorphic = LossyOptionalPolymorphicValue<NoticeCodableStrategy>
-          typealias PolymorphicArray = PolymorphicArrayValue<NoticeCodableStrategy>
-          typealias OptionalPolymorphicArray = OptionalPolymorphicArrayValue<NoticeCodableStrategy>
-          typealias PolymorphicLossyArray = PolymorphicLossyArrayValue<NoticeCodableStrategy>
-          typealias DefaultEmptyPolymorphicArray = DefaultEmptyPolymorphicArrayValue<NoticeCodableStrategy>
-        }
+        protocol Notice: Codable {
+          var type: String
+          var title: String?
+          var description: String
+        }

Also consider adding tests for:

  • A nested protocol inside a containing type to prove the fix.
  • A discriminator like "type-id" to verify key sanitization (see macro comments).
  • Empty matchingTypes array should produce a diagnostic.

🏁 Script executed:

#!/bin/bash
set -eux
file=$(fd --full-path PolymorphicCodableStrategyProvidingMacroTests.swift)
echo "Found file: $file"
sed -n '200,260p' "$file"

Length of output: 2355


🏁 Script executed:

#!/bin/bash
set -eux
# Search for identifierCodingKey usage to locate validation logic
rg -n 'identifierCodingKey' -g '*.swift'

Length of output: 8890


Exclude typealiases from expansion on invalid identifierCodingKey. Update testPolymorphicCodableStrategyProvidingMacroIdentifierCodingKeyValueError in Tests/KarrotCodableMacrosTests/PolymorphicCodableMacrosTests/PolymorphicCodableStrategyProvidingMacroTests.swift to remove the seven typealias …<NoticeCodableStrategy> lines from the expected expandedSource. Also add tests for a nested protocol, for discriminator key sanitization (e.g. "type-id"), and for empty matchingTypes producing a diagnostic.

🤖 Prompt for AI Agents
In
Tests/KarrotCodableMacrosTests/PolymorphicCodableMacrosTests/PolymorphicCodableStrategyProvidingMacroTests.swift
around lines 227–239, the test
testPolymorphicCodableStrategyProvidingMacroIdentifierCodingKeyValueError
expects expandedSource to include seven typealias lines using
<NoticeCodableStrategy>, but per the change these typealiases must be excluded
when identifierCodingKey is invalid; remove those seven typealias lines from the
expected expandedSource in that test. Additionally add three new
assertions/tests: (1) a case for a nested protocol to ensure nested protocol
types are handled, (2) a case that verifies discriminator key sanitization (e.g.
input key "type-id" becomes a valid identifier) and the macro output uses the
sanitized key, and (3) a case where matchingTypes is empty that asserts the
macro emits the appropriate diagnostic; implement these tests by constructing
the input source and expected diagnostics/expandedSource consistent with
existing test patterns.

}
""",
Expand Down
Loading