Skip to content

Give MemberwiseInit support for @inlinable and @usableFromInline #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions Sources/MemberwiseInit/MemberwiseInit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ public enum AccessLevelConfig {
case `open`
}

public enum InlinabilityConfig {
case usableFromInline
case inlinable
}

// MARK: @MemberwiseInit macro

@attached(member, names: named(init))
public macro MemberwiseInit(
_ accessLevel: AccessLevelConfig,
_ inlinability: InlinabilityConfig? = nil,
_deunderscoreParameters: Bool? = nil,
_optionalsDefaultNil: Bool? = nil
) =
Expand All @@ -22,6 +28,7 @@ public macro MemberwiseInit(

@attached(member, names: named(init))
public macro MemberwiseInit(
_ inlinability: InlinabilityConfig? = nil,
_deunderscoreParameters: Bool? = nil,
_optionalsDefaultNil: Bool? = nil
) =
Expand All @@ -32,6 +39,7 @@ public macro MemberwiseInit(

@attached(member, names: named(init))
public macro _UncheckedMemberwiseInit(
_ inlinability: InlinabilityConfig? = nil,
_deunderscoreParameters: Bool? = nil,
_optionalsDefaultNil: Bool? = nil
) =
Expand All @@ -43,6 +51,7 @@ public macro _UncheckedMemberwiseInit(
@attached(member, names: named(init))
public macro _UncheckedMemberwiseInit(
_ accessLevel: AccessLevelConfig,
_ inlinability: InlinabilityConfig? = nil,
_deunderscoreParameters: Bool? = nil,
_optionalsDefaultNil: Bool? = nil
) =
Expand Down
37 changes: 37 additions & 0 deletions Sources/MemberwiseInitClient/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,43 @@ public struct InferType<T: CaseIterable> {
var dictionaryAs = ["foo": 1, 3: "bar"] as [AnyHashable: Any]
}

@MemberwiseInit(.inlinable)
public struct InlinableInit_Default {
public let foo: String

@Init(.ignore)
private var ignored: Int = 7
}
let _ = InlinableInit_Default(foo: "bar")

@MemberwiseInit(.internal, .inlinable)
public struct InlinableInit_Internal {
public let foo: String

@Init(.ignore)
private var ignored: Int = 7
}
let _ = InlinableInit_Internal(foo: "bar")

//@MemberwiseInit(.fileprivate, .inlinable)
//internal struct InlinableInit_FilePrivate {
// fileprivate let foo: String
//
// @Init(.ignore)
// private var ignored: Int = 7
//}
////let _ = InlinableInit_FilePrivate(foo: "bar")

@MemberwiseInit(.package, .usableFromInline)
public struct InlinableInit_Package {
public let foo: String

@Init(.ignore)
private var ignored: Int = 7
}
let _ = InlinableInit_Package(foo: "bar")


// - MARK: Usage tour

public typealias SimpleClosure = () -> Void
Expand Down
69 changes: 53 additions & 16 deletions Sources/MemberwiseInitMacros/Macros/MemberwiseInitMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import SwiftCompilerPlugin
import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacroExpansion
import SwiftSyntaxMacros



public struct InitMacro: PeerMacro {
public static func expansion(
of node: SwiftSyntax.AttributeSyntax,
Expand All @@ -30,11 +31,13 @@ public struct MemberwiseInitMacro: MemberMacro {
"""
)
}

deprecationDiagnostics(node: node, declaration: decl)
.forEach(context.diagnose)

let configuredAccessLevel: AccessLevelModifier? = extractConfiguredAccessLevel(from: node)
let inlinability: InlinabilityAttribute? = extractInlinabilityAttribute(from: node)

let optionalsDefaultNil: Bool? =
extractLabeledBoolArgument("_optionalsDefaultNil", from: node)
let deunderscoreParameters: Bool =
Expand All @@ -46,37 +49,71 @@ public struct MemberwiseInitMacro: MemberMacro {
targetAccessLevel: accessLevel
)
diagnostics.forEach { context.diagnose($0) }
if let incompatibilityDiagnostic = incompatibilityDiagnosticBetween(
accessLevel: accessLevel,
inlinability: inlinability,
in: node,
typeAccessLevel: decl.declAccessLevel
) {
context.diagnose(incompatibilityDiagnostic)
}

return [
DeclSyntax(
MemberwiseInitFormatter.formatInitializer(
properties: properties,
accessLevel: accessLevel,
inlinability: inlinability,
deunderscoreParameters: deunderscoreParameters,
optionalsDefaultNil: optionalsDefaultNil
)
)
]
}

static func extractConfiguredAccessLevel(
from node: AttributeSyntax
) -> AccessLevelModifier? {
guard let arguments = node.arguments?.as(LabeledExprListSyntax.self)
else { return nil }

// NB: Search for the first argument whose name matches an access level name
for labeledExprSyntax in arguments {
if let identifier = labeledExprSyntax.expression.as(MemberAccessExprSyntax.self)?.declName,
let accessLevel = AccessLevelModifier(rawValue: identifier.baseName.trimmedDescription)
{
return accessLevel
}
node.firstArgumentValue(interpretableAs: AccessLevelModifier.self)
}

static func extractInlinabilityAttribute(
from node: AttributeSyntax
) -> InlinabilityAttribute? {
node.firstArgumentValue(interpretableAs: InlinabilityAttribute.self)
}

static func incompatibilityDiagnosticBetween(
accessLevel: AccessLevelModifier,
inlinability: InlinabilityAttribute?,
in node: AttributeSyntax,
typeAccessLevel: AccessLevelModifier
) -> Diagnostic? {
guard let inlinability else { return nil }
switch (accessLevel, inlinability) {
case
(.public, .inlinable),
(.package, .inlinable),
(.package, .usableFromInline),
(.internal, .inlinable),
(.internal, .usableFromInline):
return nil
default:
// NOTE:
// the real issue with (.open, .inlinable) is specific
// to init: `init` can't be `open`.
return Diagnostic(
node: node,
message: MacroExpansionErrorMessage(
"""
Inlinability '.\(inlinability.rawValue)' is incompatible-with access-level '.\(accessLevel)'!
"""
),
fixIts: node.allInlinabilityFixIts(typeAccessLevel: typeAccessLevel)
)
}

return nil
}

static func extractLabeledBoolArgument(
_ label: String,
from node: AttributeSyntax
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxMacroExpansion
import SwiftSyntaxMacros

func deprecationDiagnostics(
node: AttributeSyntax,
Expand Down
Loading
Loading