Skip to content

Add inherited types macro parameter #136

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 5 additions & 1 deletion Sources/Spyable/Spyable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
40 changes: 40 additions & 0 deletions Sources/SpyableMacro/Extractors/Extractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
34 changes: 34 additions & 0 deletions Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
}
Loading