Description
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 passedMacroExpansionContext
contains atypeOfLexicalContext
value corresponding to theDummy
structure. - when the
@Test
macro is expanded, the passedMacroExpansionContext
DOES NOT contain atypeOfLexicalContext
. 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, theMacroExpansionContext
is built from the syntax node of the structure. - when the
@Test
macro is expanded, theMacroExpansionContext
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:
- 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
- the code is expanded with only the
- 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
- the code is expanded with only the
- A structure annotated with
@GenerateTest
- the code is expanded with the
GenerateTestMacro
AND theTestDeclarationMacro
- the generated is not correct and does not compile.
- the code is expanded with the