Skip to content
Closed
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 @@ -20,12 +20,33 @@ public struct MemberwiseInitializerMacro: MemberMacro {
.flatMap(\.bindings)
.filter { $0.accessorBlock == nil }

let parameters = bindings.map { "\($0.pattern): \($0.typeAnnotation!.type)" }.joined(separator: ", ")
let header = SyntaxNodeString(stringLiteral: "public init(\(parameters))")
var parameters = [String]()
var initializations = [String]()

bindings.forEach {
guard let typeAnnotation = $0.typeAnnotation else { return }
var typeAnnotationText = "\(typeAnnotation.type)"

// Detect if the property is a closure
if typeAnnotationText.contains("() ->") && !typeAnnotationText.contains("@escaping") {
typeAnnotationText = "@escaping " + typeAnnotationText
}

// Prepare parameter string, auto-nil for optional values
let defaultValue = (typeAnnotationText.contains("?") || typeAnnotationText.contains("= nil")) ? " = nil" : ""
Copy link
Collaborator

Choose a reason for hiding this comment

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

@Lutzifer I woudl also like to have your opionion on that.

I understand that this solves a problem for one of our projects. With the change regarding closures i am pretty fine, but regarding the "auto-nil"-defaultvalue i am not sure if that always makes sense or creates some kind of trap for us as an uninitialized value is not complained anymore as soon as it is optional

Copy link
Member

Choose a reason for hiding this comment

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

I second that.

@im4xpro

  • restore the original test, we should not have to change this to ensure backwards-compatibilty
  • add a new test that documents what changes we need
  • implement the macro so that both cases are successfull

let parameter = "\($0.pattern): \(typeAnnotationText)\(defaultValue)"
parameters.append(parameter)

// Prepare initialization string
let initialization = "self.\($0.pattern) = \($0.pattern)"
initializations.append(initialization)
}

let header = SyntaxNodeString(stringLiteral: "public init(\(parameters.joined(separator: ", ")))")

let initializer = try! InitializerDeclSyntax(header) {
CodeBlockItemListSyntax(
bindings.map { "self.\($0.pattern) = \($0.pattern)" }.map { CodeBlockItemListSyntax.Element(stringLiteral: $0) }
initializations.map { CodeBlockItemListSyntax.Element(stringLiteral: $0) }
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@MemberwiseInitializer
struct A {
let a: String
let b: String
let b: String?
let c: () -> Void
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
struct A {
let a: String
let b: String
let b: String?
let c: () -> Void

public init(a: String, b: String) {
public init(a: String, b: String? = nil, c: @escaping () -> Void) {
self.a = a
self.b = b
self.c = c
}
}