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

Nested macro does not receive a proper MacroExpansionContext #78611

Open
letiemble opened this issue Jan 12, 2025 · 2 comments
Open

Nested macro does not receive a proper MacroExpansionContext #78611

letiemble opened this issue Jan 12, 2025 · 2 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. swift macro Feature → declarations: Swift `macro` declarations

Comments

@letiemble
Copy link

Description

Context

Consider the following use-case: I have a @GenerateTest macro is a MemberMacro that generates a function which is decorated with a @Test attribute.

When the @GenerateTest macro is expanded, the member is properly generated with the @Test attribute.
When the @Test macro is expanded, the generated code is incorrect and does not compile.

Details

Here is the initial code:

@GenerateTest
struct Dummy {
}

Then the @GenerateTest macro is expanded resulting in:

struct Dummy {
    @Test("Random number generation")
    func rng() {
    }
}

Then the @Test macro is expanded resulting in:

struct Dummy {
    func rng() {
    }

    @available(*, deprecated, message: "This function is an implementation detail of the testing library. Do not use it directly.")
    @Sendable private  func __macro_local_9Z72b5e1c4fMu_() async throws -> Void {  // <= (1) The thunk is not static
        @Sendable func __macro_local_7__localfMu_(_:isolated (any Actor)?=Testing.__defaultSynchronousIsolationContext) async throws {
            _ = try await Testing.__requiringTry(Testing.__requiringAwait(rng()))
        }
        try await __macro_local_7__localfMu_()
    }

    @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
    enum __macro_local_38__🟠$test_container__function__72b5e1c4fMu_: Testing.__TestContainer {
        static var __tests: [Testing.Test] {
            get async {
                return [.__function(
                    named: "rng()",
                    in: nil,
                    xcTestCompatibleSelector: nil,
                    displayName: "Random number generation",traits: [],sourceLocation: Testing.SourceLocation.__here(),
                    parameters: [],
                    testFunction: __macro_local_9Z72b5e1c4fMu_  // <= (2) A reference to an instance function
                )]
            }
        }
    }
}

The generated thunk is not correct (1), and the container reference an instance function (2) which does not compile.

Analysis

I have step into the code to see if I could figure out what was going wrong.

Here are my observations:

  • when the @GenerateTest macro is expanded, the passed MacroExpansionContext contains a typeOfLexicalContext value corresponding to the Dummy structure.
  • when the @Test macro is expanded, the passed MacroExpansionContext DOES NOT contain a typeOfLexicalContext. This affects the generated code by making it non compilable.

I think the issue lies in the way the MacroExpansionContext is built before being passed to the macro:

  • when the @GenerateTest macro is expanded, the MacroExpansionContext is built from the syntax node of the structure.
  • when the @Test macro is expanded, the MacroExpansionContext is built from the newly generated member syntax node which is not attached to the syntax node of the structure.

Steps to Reproduce

Here is a GIST containing a test case to reproduce (the code is a bit rough). The file can be dropped in the TestingMacroTests source folder as-is.

The test case contains 3 tests:

  1. A structure with a function @Test annotation:
    • the code is expanded with only the TestDeclarationMacro
    • the generated is correct and compiles.
    • this is the code expected if everything goes fine
  2. A structure annotated with @GenerateTest
    • the code is expanded with only the GenerateTestMacro
    • the generated is correct and compiles.
    • this is a test to check that the custom macro works fine
  3. A structure annotated with @GenerateTest
    • the code is expanded with the GenerateTestMacro AND the TestDeclarationMacro
    • the generated is not correct and does not compile.
@letiemble letiemble added the bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. label Jan 12, 2025
@ahoppen
Copy link
Member

ahoppen commented Jan 12, 2025

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

@ahoppen
Copy link
Member

ahoppen commented Jan 13, 2025

The behavior during macro expansion matches the behavior that we’re seeing during compilation. Moving to the compiler repo.

Attached is a reduced package that reproduces the behavior: Test.zip

@ahoppen ahoppen transferred this issue from swiftlang/swift-syntax Jan 13, 2025
@ahoppen ahoppen added the swift macro Feature → declarations: Swift `macro` declarations label Jan 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. swift macro Feature → declarations: Swift `macro` declarations
Projects
None yet
Development

No branches or pull requests

2 participants