From c816203ae3ad27f549465f56874c5558e9c43fa9 Mon Sep 17 00:00:00 2001 From: Maxence Mottard Date: Mon, 12 May 2025 11:23:22 +0200 Subject: [PATCH 1/3] Asd unit tests --- .../Macro/UT_SpyableMacro.swift | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift index 60f87de..246d7c1 100644 --- a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift +++ b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift @@ -555,4 +555,38 @@ final class UT_SpyableMacro: XCTestCase { macros: sut ) } + + func testMacroWithParameterToMakeGeneratedSpyConformToAnotherProtocol() { + let baseProtocolDeclaration = """ + protocol BaseProtocol { + func baseMethod() + } + + class BaseClass: BaseProtocol { + func baseMethod() {} + } + + """ + + let protocolDeclaration = "protocol MyProtocol: BaseProtocol {}" + + assertMacroExpansion( + """ + \(baseProtocolDeclaration) + @Spyable(inheritedTypes: "BaseClass") + \(protocolDeclaration) + """, + expandedSource: """ + \(baseProtocolDeclaration) + + \(protocolDeclaration) + + class MyProtocolSpy: MyProtocol, @unchecked Sendable, BaseClass { + init() { + } + } + """, + macros: sut + ) + } } From 1b4e33e898ea1e8ba3c2bb8297064606b2078e52 Mon Sep 17 00:00:00 2001 From: Maxence Mottard Date: Mon, 12 May 2025 11:24:00 +0200 Subject: [PATCH 2/3] Add "inheritedTypes" parameter to the macro --- Sources/Spyable/Spyable.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/Spyable/Spyable.swift b/Sources/Spyable/Spyable.swift index c5feb99..d0648d8 100644 --- a/Sources/Spyable/Spyable.swift +++ b/Sources/Spyable/Spyable.swift @@ -134,7 +134,11 @@ /// - The generated spy class name is suffixed with `Spy` (e.g., `ServiceProtocolSpy`). /// @attached(peer, names: suffixed(Spy)) -public macro Spyable(behindPreprocessorFlag: String? = nil, accessLevel: SpyAccessLevel? = nil) = +public macro Spyable( + behindPreprocessorFlag: String? = nil, + accessLevel: SpyAccessLevel? = nil, + inheritedTypes: String? = nil +) = #externalMacro( module: "SpyableMacro", type: "SpyableMacro" From 7efaafdb63fd4a3e7b26a674687518e1bb31be1a Mon Sep 17 00:00:00 2001 From: Maxence Mottard Date: Mon, 12 May 2025 11:24:26 +0200 Subject: [PATCH 3/3] Create the method to extract the "inheritedTypes" parameter --- .../SpyableMacro/Extractors/Extractor.swift | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Sources/SpyableMacro/Extractors/Extractor.swift b/Sources/SpyableMacro/Extractors/Extractor.swift index 48aeefb..441b045 100644 --- a/Sources/SpyableMacro/Extractors/Extractor.swift +++ b/Sources/SpyableMacro/Extractors/Extractor.swift @@ -145,6 +145,46 @@ struct Extractor { func extractAccessLevel(from protocolDeclSyntax: ProtocolDeclSyntax) -> DeclModifierSyntax? { protocolDeclSyntax.modifiers.first(where: \.name.isAccessLevelSupportedInProtocol) } + + func extractInheritedTypes( + from attribute: AttributeSyntax, + in context: some MacroExpansionContext + ) -> String? { + guard case let .argumentList(argumentList) = attribute.arguments else { + // No arguments are present in the attribute. + return nil + } + + let inheritedTypesArgument = argumentList.first { argument in + argument.label?.text == "inheritedTypes" + } + + guard let inheritedTypesArgument else { + // The `inheritedTypes` argument is missing. + return nil + } + + let segments = inheritedTypesArgument.expression + .as(StringLiteralExprSyntax.self)? + .segments + + guard let segments, + segments.count == 1, + case let .stringSegment(literalSegment)? = segments.first + else { + // The `inheritedTypes` argument's value is not a static string literal. + context.diagnose( + Diagnostic( + node: attribute, + message: SpyableDiagnostic.behindPreprocessorFlagArgumentRequiresStaticStringLiteral, + highlights: [Syntax(inheritedTypesArgument.expression)] + ) + ) + return nil + } + + return literalSegment.content.text + } } extension TokenSyntax {