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
21 changes: 15 additions & 6 deletions Sources/NewCodable/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,29 @@
/// 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))
@attached(extension, conformances: JSONEncodable, names: named(CodingFields), 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))
@attached(extension, conformances: JSONDecodable, names: named(CodingFields), 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))
@attached(extension, conformances: JSONEncodable, JSONDecodable, names: named(CodingFields), named(encode), named(decode))
public macro JSONCodable() = #externalMacro(module: "NewCodableMacros", type: "JSONCodableMacro")

/// Experimental macro that synthesizes `CommonEncodable` conformance.
@attached(extension, conformances: CommonEncodable, names: named(CodingFields), named(encode))
public macro CommonEncodable() = #externalMacro(module: "NewCodableMacros", type: "CommonEncodableMacro")

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

/// Experimental macro that synthesizes both `CommonEncodable` and `CommonDecodable`.
@attached(extension, conformances: CommonEncodable, CommonDecodable, names: named(CodingFields), named(encode), named(decode))
public macro CommonCodable() = #externalMacro(module: "NewCodableMacros", type: "CommonCodableMacro")

/// Experimental per-property marker macro for overriding the serialized key.
@attached(peer)
public macro CodingKey(_ name: String) = #externalMacro(module: "NewCodableMacros", type: "CodingKeyMacro")
Expand Down
2 changes: 1 addition & 1 deletion Sources/NewCodable/SharedTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ public protocol StaticStringEncodingField: EncodingField, ~Escapable {

public protocol StaticStringCodingField: StaticStringDecodingField, StaticStringEncodingField, ~Escapable { }

public extension CodingField where Self: StaticStringCodingField & ~Escapable {
public extension StaticStringCodingField where Self: ~Escapable {
@_alwaysEmitIntoClient
@inline(__always)
// var utf8Span: UTF8Span { @_lifetime(borrow self) get }
Expand Down
61 changes: 61 additions & 0 deletions Sources/NewCodableMacros/CommonCodableMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//===----------------------------------------------------------------------===//
//
// 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 CommonCodableMacro { }

extension CommonCodableMacro: 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 validate(declaration: declaration, for: node, in: context) else {
return []
}

guard let (typeName, properties) = extractTypeNameAndStoredProperties(
attachedTo: declaration,
for: node,
providingExtensionsOf: type,
in: context) else {
return []
}

let codingFields = makeCodingFieldsExtension(for: typeName, from: properties, kind: CommonCodingFieldExpansionKind.both)
let encodingImpl = makeEncodableExtension(for: typeName, with: properties, kind: CommonEncodableExpansionKind())
let decodingImpl = makeDecodableExtension(for: typeName, with: properties, kind: CommonDecodableExpansionKind())
return [codingFields, encodingImpl, decodingImpl].compactMap { $0 }
}
}

enum CommonCodingFieldExpansionKind: CodingFieldExpansionKind {
case encodingOnly
case decodingOnly
case both

var protocolName: String {
switch self {
case .encodingOnly:
return "StaticStringEncodingField"
case .decodingOnly:
return "StaticStringDecodingField"
case .both:
return "StaticStringCodingField"
}
}
}
49 changes: 49 additions & 0 deletions Sources/NewCodableMacros/CommonDecodableMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
//
// 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 SwiftSyntaxBuilder
import SwiftDiagnostics

public struct CommonDecodableMacro { }

extension CommonDecodableMacro: 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 validate(declaration: declaration, for: node, in: context) else {
return []
}

guard let (typeName, properties) = extractTypeNameAndStoredProperties(
attachedTo: declaration,
for: node,
providingExtensionsOf: type,
in: context) else {
return []
}

let codingFields = makeCodingFieldsExtension(for: typeName, from: properties, kind: CommonCodingFieldExpansionKind.decodingOnly)
let impl = makeDecodableExtension(for: typeName, with: properties, kind: CommonDecodableExpansionKind())
return [codingFields, impl].compactMap { $0 }
}
}

struct CommonDecodableExpansionKind: DecodableExpansionKind {
var protocolName: String { "CommonDecodable" }
var decoderType: String { "inout some CommonDecoder & ~Escapable" }
}
49 changes: 49 additions & 0 deletions Sources/NewCodableMacros/CommonEncodableMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
//
// 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 SwiftSyntaxBuilder
import SwiftDiagnostics

public struct CommonEncodableMacro { }

extension CommonEncodableMacro: 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 validate(declaration: declaration, for: node, in: context) else {
return []
}

guard let (typeName, properties) = extractTypeNameAndStoredProperties(
attachedTo: declaration,
for: node,
providingExtensionsOf: type,
in: context) else {
return []
}

let codingFields = makeCodingFieldsExtension(for: typeName, from: properties, kind: CommonCodingFieldExpansionKind.encodingOnly)
let impl = makeEncodableExtension(for: typeName, with: properties, kind: CommonEncodableExpansionKind())
return [codingFields, impl].compactMap { $0 }
}
}

struct CommonEncodableExpansionKind: EncodableExpansionKind {
var protocolName: String { "CommonEncodable" }
var encoderType: String { "inout some CommonEncoder & ~Copyable & ~Escapable" }
}
66 changes: 30 additions & 36 deletions Sources/NewCodableMacros/JSONCodableMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,46 @@ import SwiftDiagnostics

public struct JSONCodableMacro { }

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

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

guard let (typeName, properties) = extractTypeNameAndStoredProperties(
attachedTo: declaration,
for: node,
providingExtensionsOf: type,
in: context) else {
return []
}

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

let codingFields = makeCodingFieldsExtension(for: typeName, from: properties, kind: JSONCodingFieldKind.both)
let encodingImpl = makeEncodableExtension(for: typeName, with: properties, kind: JSONEncodableExpansionKind())
let decodingImpl = makeDecodableExtension(for: typeName, with: properties, kind: JSONDecodableExpansionKind())
return [codingFields, encodingImpl, decodingImpl].compactMap { $0 }
}
}

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
enum JSONCodingFieldKind: CodingFieldExpansionKind {
case encodingOnly
case decodingOnly
case both

var protocolName: String {
switch self {
case .encodingOnly:
return "JSONOptimizedEncodingField"
case .decodingOnly:
return "JSONOptimizedDecodingField"
case .both:
return "JSONOptimizedCodingField"
}
}
}
Loading
Loading