diff --git a/Examples/Sources/ViewModel.swift b/Examples/Sources/ViewModel.swift index 53b04bb..5d091c5 100644 --- a/Examples/Sources/ViewModel.swift +++ b/Examples/Sources/ViewModel.swift @@ -17,7 +17,7 @@ protocol ServiceProtocol { func append(name: (any Codable) -> (any Codable)?) func get() async throws -> any Codable func read() -> String! - func wrapDataInArray(_ data: T) -> Array + func wrapDataInArray(_ data: T) -> [T] } final class ViewModel { @@ -44,8 +44,8 @@ final class ViewModel { _ = try await service.fetchConfig(arg: 2) config.removeAll() } - - func wrapData(_ data: T) -> Array { + + func wrapData(_ data: T) -> [T] { service.wrapDataInArray(data) } } diff --git a/Examples/Tests/ViewModelTests.swift b/Examples/Tests/ViewModelTests.swift index b63a385..b01b468 100644 --- a/Examples/Tests/ViewModelTests.swift +++ b/Examples/Tests/ViewModelTests.swift @@ -61,7 +61,7 @@ final class ViewModelTests: XCTestCase { serviceSpy.wrapDataInArrayReturnValue = [123] XCTAssertEqual(sut.wrapData(1), [123]) XCTAssertEqual(serviceSpy.wrapDataInArrayReceivedData as? Int, 1) - + // ⚠️ The following would cause a fatal error, because an Array will be returned by wrapData(), but we provided an Array to wrapDataInArrayReturnValue. ⚠️ // XCTAssertEqual(sut.wrapData("hi"), ["hello"]) } diff --git a/Sources/SpyableMacro/Extensions/FunctionDeclSyntax+Extensions.swift b/Sources/SpyableMacro/Extensions/FunctionDeclSyntax+Extensions.swift index c6c968b..30d6d3e 100644 --- a/Sources/SpyableMacro/Extensions/FunctionDeclSyntax+Extensions.swift +++ b/Sources/SpyableMacro/Extensions/FunctionDeclSyntax+Extensions.swift @@ -12,8 +12,9 @@ extension FunctionDeclSyntax { /// Ex: `func foo() -> T` will create `var fooReturnValue: Any!`, which will be used in the spy method implementation as `fooReturnValue as! T` var forceCastType: TypeSyntax? { guard !genericTypes.isEmpty, - let returnType = signature.returnClause?.type, - returnType.containsGenericType(from: genericTypes) == true else { + let returnType = signature.returnClause?.type, + returnType.containsGenericType(from: genericTypes) == true + else { return nil } return returnType.trimmed diff --git a/Sources/SpyableMacro/Extensions/TypeSyntax+Extensions.swift b/Sources/SpyableMacro/Extensions/TypeSyntax+Extensions.swift index 637a209..48c8510 100644 --- a/Sources/SpyableMacro/Extensions/TypeSyntax+Extensions.swift +++ b/Sources/SpyableMacro/Extensions/TypeSyntax+Extensions.swift @@ -28,7 +28,8 @@ extension TypeSyntax { guard !genericTypes.isEmpty else { return self } // TODO: An improvement upon this could be to throw an error here, instead of falling back to `self`. This could be ultimately used to emit a diagnostic about the unsupported TypeSyntax for a better user experience. - return TypeSyntax(fromProtocol: asTypeSyntaxSupportingGenerics?.erasingGenericTypes(genericTypes)) ?? self + return TypeSyntax( + fromProtocol: asTypeSyntaxSupportingGenerics?.erasingGenericTypes(genericTypes)) ?? self } /// Recurses through type syntaxes to find all `IdentifierTypeSyntax` leaves, and checks each of them to see if its name exists in `genericTypes`. @@ -39,8 +40,10 @@ extension TypeSyntax { func containsGenericType(from genericTypes: Set) -> Bool { guard !genericTypes.isEmpty else { return false } - return if let type = self.as(IdentifierTypeSyntax.self), - genericTypes.contains(type.name.text) { + return + if let type = self.as(IdentifierTypeSyntax.self), + genericTypes.contains(type.name.text) + { true } else { nestedTypeSyntaxes.contains { $0.containsGenericType(from: genericTypes) } @@ -64,7 +67,7 @@ private protocol TypeSyntaxSupportingGenerics: TypeSyntaxProtocol { } private let typeSyntaxesSupportingGenerics: [TypeSyntaxSupportingGenerics.Type] = [ - IdentifierTypeSyntax.self, // Start with IdentifierTypeSyntax for the sake of efficiency when looping through this array, as it's the most common TypeSyntax. + IdentifierTypeSyntax.self, // Start with IdentifierTypeSyntax for the sake of efficiency when looping through this array, as it's the most common TypeSyntax. ArrayTypeSyntax.self, GenericArgumentClauseSyntax.self, TupleTypeSyntax.self, @@ -82,7 +85,7 @@ extension IdentifierTypeSyntax: TypeSyntaxSupportingGenerics { if let genericArgumentClause { copy = copy.with( \.genericArgumentClause, - genericArgumentClause.erasingGenericTypes(genericTypes) + genericArgumentClause.erasingGenericTypes(genericTypes) ) } return copy @@ -105,14 +108,14 @@ extension GenericArgumentClauseSyntax: TypeSyntaxSupportingGenerics { fileprivate func erasingGenericTypes(_ genericTypes: Set) -> Self { with( \.arguments, - GenericArgumentListSyntax { - for argumentElement in arguments { - argumentElement.with( + GenericArgumentListSyntax { + for argumentElement in arguments { + argumentElement.with( \.argument, - argumentElement.argument.erasingGenericTypes(genericTypes) - ) - } - } + argumentElement.argument.erasingGenericTypes(genericTypes) + ) + } + } ) } } @@ -124,13 +127,13 @@ extension TupleTypeSyntax: TypeSyntaxSupportingGenerics { fileprivate func erasingGenericTypes(_ genericTypes: Set) -> Self { with( \.elements, - TupleTypeElementListSyntax { - for element in elements { - element.with( + TupleTypeElementListSyntax { + for element in elements { + element.with( \.type, - element.type.erasingGenericTypes(genericTypes)) - } - } + element.type.erasingGenericTypes(genericTypes)) + } + } ) } } diff --git a/Sources/SpyableMacro/Factories/ClosureFactory.swift b/Sources/SpyableMacro/Factories/ClosureFactory.swift index 4fdfb09..027ab4e 100644 --- a/Sources/SpyableMacro/Factories/ClosureFactory.swift +++ b/Sources/SpyableMacro/Factories/ClosureFactory.swift @@ -72,20 +72,24 @@ struct ClosureFactory { /* func f() -> String! */ - if let implicitlyUnwrappedType = functionReturnClause.type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { + if let implicitlyUnwrappedType = functionReturnClause.type.as( + ImplicitlyUnwrappedOptionalTypeSyntax.self) + { var functionReturnClause = functionReturnClause /* `() -> String!` is not a valid code so we have to convert it to `() -> String? */ - functionReturnClause.type = TypeSyntax(OptionalTypeSyntax(wrappedType: implicitlyUnwrappedType.wrappedType)) + functionReturnClause.type = TypeSyntax( + OptionalTypeSyntax(wrappedType: implicitlyUnwrappedType.wrappedType)) return functionReturnClause /* func f() -> Any func f() -> Any? */ } else { - return functionReturnClause.with(\.type, functionReturnClause.type.erasingGenericTypes(genericTypes)) + return functionReturnClause.with( + \.type, functionReturnClause.type.erasingGenericTypes(genericTypes)) } /* func f() diff --git a/Sources/SpyableMacro/Factories/SpyFactory.swift b/Sources/SpyableMacro/Factories/SpyFactory.swift index a1bbb4b..aa1518f 100644 --- a/Sources/SpyableMacro/Factories/SpyFactory.swift +++ b/Sources/SpyableMacro/Factories/SpyFactory.swift @@ -125,7 +125,8 @@ struct SpyFactory { for functionDeclaration in functionDeclarations { let variablePrefix = variablePrefixFactory.text(for: functionDeclaration) let genericTypes = functionDeclaration.genericTypes - let parameterList = parameterList(protocolFunctionDeclaration: functionDeclaration, genericTypes: genericTypes) + let parameterList = parameterList( + protocolFunctionDeclaration: functionDeclaration, genericTypes: genericTypes) try callsCountFactory.variableDeclaration(variablePrefix: variablePrefix) try calledFactory.variableDeclaration(variablePrefix: variablePrefix) diff --git a/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ContainsGenericType.swift b/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ContainsGenericType.swift index 909406c..88f5622 100644 --- a/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ContainsGenericType.swift +++ b/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ContainsGenericType.swift @@ -77,11 +77,13 @@ final class UT_TypeSyntax_ContainsGenericType: XCTestCase { containsGenericType genericTypes: Set ) -> Bool { TypeSyntax( - TupleTypeSyntax(elements: TupleTypeElementListSyntax { - TupleTypeElementSyntax(type: IdentifierTypeSyntax( - name: .identifier(identifier) - )) - }) + TupleTypeSyntax( + elements: TupleTypeElementListSyntax { + TupleTypeElementSyntax( + type: IdentifierTypeSyntax( + name: .identifier(identifier) + )) + }) ) .containsGenericType(from: genericTypes) } diff --git a/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ErasingGenericType.swift b/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ErasingGenericType.swift index 5890882..1f89879 100644 --- a/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ErasingGenericType.swift +++ b/Tests/SpyableMacroTests/Extensions/UT_TypeSyntax+ErasingGenericType.swift @@ -75,13 +75,15 @@ final class UT_TypeSyntax_ErasingGenericTypes: XCTestCase { TupleTypeSyntax( leadingTrivia: .space, elements: TupleTypeElementListSyntax { - TupleTypeElementSyntax(type: IdentifierTypeSyntax( - name: .identifier(identifier) - )) - TupleTypeElementSyntax(type: IdentifierTypeSyntax( - leadingTrivia: .space, - name: .identifier("Unerased") - )) + TupleTypeElementSyntax( + type: IdentifierTypeSyntax( + name: .identifier(identifier) + )) + TupleTypeElementSyntax( + type: IdentifierTypeSyntax( + leadingTrivia: .space, + name: .identifier("Unerased") + )) }, trailingTrivia: .space )