Skip to content
Merged
22 changes: 22 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,25 @@ let package = Package(
swiftSettings: availabilityMacros + featureSettings + testOnlySwiftSettings
),

// NewCodableMacros
.macro(
name: "NewCodableMacros",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
],
swiftSettings: featureSettings
),

// NewCodable
.target(
name: "NewCodable",
dependencies: [
.target(name: "FoundationEssentials"),
"NewCodableMacros",
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "BasicContainers", package: "swift-collections"),
],
Expand Down Expand Up @@ -277,6 +291,14 @@ let package = Package(
.enableExperimentalFeature("Lifetimes"),
.enableUpcomingFeature("MemberImportVisibility"),
]
),
.testTarget(
name: "NewCodableMacrosTests",
dependencies: [
"NewCodableMacros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
],
swiftSettings: featureSettings
)
]
)
1 change: 1 addition & 0 deletions Sources/NewCodable/JSON/JSONParserDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,7 @@ extension JSONParserDecoder.ArrayDecoder: CommonArrayDecoder {
}

extension JSONParserDecoder.StructDecoder: CommonStructDecoder {
@_lifetime(self: copy self)
public mutating func decodeExpectedOrderField(required: Bool, matchingClosure: (UTF8Span) -> Bool, andValue valueDecoderClosure: (inout JSONParserDecoder) throws(CodingError.Decoding) -> Void) throws(CodingError.Decoding) -> Bool {
try self.decodeExpectedOrderField(required: required, matchingClosure: matchingClosure, optimizedSafeStringKey: nil, andValue: valueDecoderClosure)
}
Expand Down
1 change: 1 addition & 0 deletions Sources/NewCodable/JSON/NewJSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ extension JSONDirectEncoder {
try self.encode(arbitraryPrecisionNumber: decimal.description.utf8Span)
}

@_lifetime(self: copy self)
internal mutating func encodeGenericNonCopyable<T: CommonEncodable & ~Copyable>(_ value: borrowing T) throws(CodingError.Encoding) {
try value.encode(to: &self)
}
Expand Down
42 changes: 42 additions & 0 deletions Sources/NewCodable/Macros.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// Experimental NewCodable macro API.
///
/// The macro spellings in this file are provisional and may evolve with the
/// macro-based Codable design. The per-property marker macros below are
/// especially likely to change as the feature set is refined.
@attached(member, names: named(CodingFields))
@attached(extension, conformances: JSONEncodable, names: named(encode))
public macro JSONEncodable() = #externalMacro(module: "NewCodableMacros", type: "JSONEncodableMacro")

/// Experimental macro that synthesizes `JSONDecodable` conformance.
@attached(member, names: named(CodingFields))
@attached(extension, conformances: JSONDecodable, names: named(decode))
public macro JSONDecodable() = #externalMacro(module: "NewCodableMacros", type: "JSONDecodableMacro")

/// Experimental macro that synthesizes both `JSONEncodable` and `JSONDecodable`.
@attached(member, names: named(CodingFields))
@attached(extension, conformances: JSONEncodable, JSONDecodable, names: named(encode), named(decode))
public macro JSONCodable() = #externalMacro(module: "NewCodableMacros", type: "JSONCodableMacro")

/// Experimental per-property marker macro for overriding the serialized key.
@attached(peer)
public macro CodingKey(_ name: String) = #externalMacro(module: "NewCodableMacros", type: "CodingKeyMacro")

/// Experimental per-property marker macro for supplying a default decoding value.
@attached(peer)
public macro CodableDefault<T>(_ value: T) = #externalMacro(module: "NewCodableMacros", type: "CodableDefaultMacro")

/// Experimental per-property marker macro for accepting alternate decoding keys.
@attached(peer)
public macro CodableAlias(_ names: String...) = #externalMacro(module: "NewCodableMacros", type: "CodableAliasMacro")
Comment thread
tevelee marked this conversation as resolved.
Outdated
24 changes: 24 additions & 0 deletions Sources/NewCodableMacros/CodableAliasMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxMacros

public struct CodableAliasMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
[]
}
}
24 changes: 24 additions & 0 deletions Sources/NewCodableMacros/CodableDefaultMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxMacros

public struct CodableDefaultMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
[]
}
}
24 changes: 24 additions & 0 deletions Sources/NewCodableMacros/CodingKeyMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxMacros

public struct CodingKeyMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
[]
}
}
67 changes: 67 additions & 0 deletions Sources/NewCodableMacros/JSONCodableMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxMacros
import SwiftDiagnostics

public struct JSONCodableMacro { }

extension JSONCodableMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard declaration.is(StructDeclSyntax.self) else {
context.diagnose(.init(
node: node,
message: JSONEncodableDiagnostic.notAStruct
))
return []
}

let properties = extractStoredProperties(from: declaration.memberBlock, in: context)
guard !properties.isEmpty else {
return []
}

return [makeCodingFieldsDecl(from: properties, kind: .coding)]
}
}

extension JSONCodableMacro: 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] {
let encodableExtensions = try JSONEncodableMacro.expansion(
of: node,
attachedTo: declaration,
providingExtensionsOf: type,
conformingTo: protocols,
in: context
)
let decodableExtensions = try JSONDecodableMacro.expansion(
of: node,
attachedTo: declaration,
providingExtensionsOf: type,
conformingTo: protocols,
in: context
)
return encodableExtensions + decodableExtensions
}
}
Loading
Loading