From 5b645895608af052f3ce3434f4d608937c70253c Mon Sep 17 00:00:00 2001 From: Dan Federman Date: Tue, 28 Nov 2023 19:37:49 -0800 Subject: [PATCH] Initializer fixit fixes --- Sources/SafeDICore/Models/Initializer.swift | 2 +- .../Macros/ConstructableMacro.swift | 2 +- Tests/SafeDIMacrosTests/MacroTests.swift | 96 ++++++++++++++++++- 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/Sources/SafeDICore/Models/Initializer.swift b/Sources/SafeDICore/Models/Initializer.swift index 539f2cb3..f0b68cae 100644 --- a/Sources/SafeDICore/Models/Initializer.swift +++ b/Sources/SafeDICore/Models/Initializer.swift @@ -186,7 +186,7 @@ public struct Initializer: Codable, Equatable { leadingTrivia: .space, trailingTrivia: .space), rightOperand: DeclReferenceExprSyntax(baseName: TokenSyntax.identifier(dependency.property.label)), - trailingTrivia: .newline + trailingTrivia: dependency == dependencies.last ? .newline : nil ))) ) } diff --git a/Sources/SafeDIMacros/Macros/ConstructableMacro.swift b/Sources/SafeDIMacros/Macros/ConstructableMacro.swift index 9dda7de5..3a768a15 100644 --- a/Sources/SafeDIMacros/Macros/ConstructableMacro.swift +++ b/Sources/SafeDIMacros/Macros/ConstructableMacro.swift @@ -59,7 +59,7 @@ public struct ConstructableMacro: MemberMacro { .compactMap({ try? $0.result.get() }) .first else { - if initializerAndResultPairs.isEmpty { + if !visitor.dependencies.isEmpty || initializerAndResultPairs.isEmpty { var membersWithInitializer = declaration.memberBlock.members membersWithInitializer.insert( MemberBlockItemSyntax( diff --git a/Tests/SafeDIMacrosTests/MacroTests.swift b/Tests/SafeDIMacrosTests/MacroTests.swift index 521dbced..15f02c9b 100644 --- a/Tests/SafeDIMacrosTests/MacroTests.swift +++ b/Tests/SafeDIMacrosTests/MacroTests.swift @@ -488,7 +488,7 @@ final class MacroTests: XCTestCase { @provided } - """ // fixes is quite wrong here. In Xcode this removes all but the first macro. + """ // fixes are super wrong here. We delete @provided not the rest. } } @@ -679,14 +679,14 @@ final class MacroTests: XCTestCase { init() {} init() {} - """ // fixes are wrong! It's duplicating the correction. not sure why. + """ // this is seriously incorrect – it works in Xcode. } expansion: { """ public struct ExampleService { init() {} init() {} - """ // expansion is wrong! It's duplicating the correction. not sure why. + """ } } @@ -738,5 +738,95 @@ final class MacroTests: XCTestCase { } } + func test_constructableMacro_addsFixitMissingRequiredInitializerWhenDependencyMissingFromInit() { + assertMacro { + """ + @constructable + public struct ExampleService { + init(variantA: VariantA, variantB: VariantB) { + self.variantA = variantA + self.variantB = variantB + invariantA = InvariantA() + } + + @propagated + let variantA: VariantA + @propagated + let variantB: VariantB + @provided + let invariantA: InvariantA + } + """ + } diagnostics: { + """ + @constructable + public struct ExampleService { + ╰─ πŸ›‘ @constructable-decorated type must have initializer for all injected parameters + ✏️ Add required initializer + init(variantA: VariantA, variantB: VariantB) { + self.variantA = variantA + self.variantB = variantB + invariantA = InvariantA() + } + + @propagated + let variantA: VariantA + @propagated + let variantB: VariantB + @provided + let invariantA: InvariantA + } + """ + } fixes: { + """ + @constructable + public struct ExampleService { + init(variantA: VariantA, variantB: VariantB, invariantA: InvariantA) { + self.variantA = variantA + self.variantB = variantB + self.invariantA = invariantA + } + + init(variantA: VariantA, variantB: VariantB) { + self.variantA = variantA + self.variantB = variantB + invariantA = InvariantA() + } + + @propagated + let variantA: VariantA + @propagated + let variantB: VariantB + @provided + let invariantA: InvariantA + } + """ + } expansion: { + """ + public struct ExampleService { + init(variantA: VariantA, variantB: VariantB, invariantA: InvariantA) { + self.variantA = variantA + self.variantB = variantB + self.invariantA = invariantA + } + + init(variantA: VariantA, variantB: VariantB) { + self.variantA = variantA + self.variantB = variantB + invariantA = InvariantA() + } + let variantA: VariantA + let variantB: VariantB + let invariantA: InvariantA + + public init(buildSafeDIDependencies: (VariantA, VariantB) -> (variantA: VariantA, variantB: VariantB, invariantA: InvariantA), variantA: VariantA, variantB: VariantB) { + let dependencies = buildSafeDIDependencies(variantA, variantB) + self.init(variantA: dependencies.variantA, variantB: dependencies.variantB, invariantA: dependencies.invariantA) + } + } + """ + } + } + } #endif