Skip to content
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

assertMacroExpansion passes 0 protocols to ExtensionMacro. #2723

Closed
drekka opened this issue Jul 10, 2024 · 4 comments
Closed

assertMacroExpansion passes 0 protocols to ExtensionMacro. #2723

drekka opened this issue Jul 10, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@drekka
Copy link

drekka commented Jul 10, 2024

Description

I've been writing at attached extension macro to apply some protocols to certain types which looks something like this:

public protocol PX {}

/// THis defines the macro as it will be seen by the source code.
@attached(extension, conformances: PX, Hashable, Identifiable, names: named(hash(into:)))
public macro pathElement() = #externalMacro(module: "SwiftUIExtensionsMacros", type: "PathElementMacro")

With an implementation of:

public enum PathElementMacro: ExtensionMacro {

    public static func expansion(
      of node: AttributeSyntax,
      attachedTo declaration: some DeclGroupSyntax,
      providingExtensionsOf type: some TypeSyntaxProtocol,
      conformingTo protocols: [TypeSyntax],
      in context: some MacroExpansionContext
    ) throws -> [ExtensionDeclSyntax] {
        let count:Int = protocols.count
        let equatableExtension = try ExtensionDeclSyntax("extension \(type.trimmed): Hashable { let x = \(raw: count) }")
            return [equatableExtension]
    }
}

Obviously this is not a finished macro as I''m still working out what it will do. However it does show the number of protocols passed which I eventually intend to use in generating extensions.

The bug is that when I debug this macro using this unit test code:

assertMacroExpansion(
                """
                @pathElement
                struct X: Hashable {}
                """,
                expandedSource: """
                struct X: Hashable {}

                extension X: Identifiable {
                   let id = UUID()
                }
                """,
                macros: [
                    "pathElement": PathElementMacro.self,
                ]
            )

and breakpoint inside the macro, the protocols array argument is always zero length. However when I run this macro on a live source code file, I see let x = 3 in the generated extension indicating that the 3 protocols were passed as expected.

Any I correct? Is this a missed bug?

Or have I missed something in my test code?

Steps to Reproduce

No response

@drekka drekka added the bug Something isn't working label Jul 10, 2024
@ahoppen
Copy link
Contributor

ahoppen commented Jul 10, 2024

Synced to Apple’s issue tracker as rdar://131441049

@Matejkob
Copy link
Contributor

Hey there!

Thanks for bringing this up.

There's a bit of a mismatch between what your test is doing and how the macro actually behaves in real code–since the unit tests expansion is based on a totally different expansion system than source code (compiler) expansion.

If you want to cover conformances in your unit tests, you have to provide a list of conformances to the assertMacroExpansion function. The function has an argument called macroSpecs that you need to fill up if you want to receive conformances in your macro implementation from unit tests.

Here's the documentation for this argument:

/// - macroSpecs: The macros that should be expanded, provided as a dictionary
/// mapping macro names (e.g., `"CodableMacro"`) to specification with macro type
/// (e.g., `CodableMacro.self`) and a list of conformances macro provides
/// (e.g., `["Decodable", "Encodable"]`).

Basically, there's this MacroSpec struct with a conformances property that you need to fill in for your tests. It's a new thing that came with swift-syntax version 6.0, which is part of the upcoming Swift 6.0 release. If you're really keen, you can check it out on the main branch right now.

@drekka
Copy link
Author

drekka commented Jul 10, 2024

Thanks @Matejkob However I'm on a client's project where we have to support iOS16 and Swift 5. Is there any way to address this without Swift 6?

@drekka
Copy link
Author

drekka commented Jul 11, 2024

Ok, I updated to use the main branch and now I can interrogate the protocols.

@drekka drekka closed this as completed Jul 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants