I was getting bothered that I needed to give context as a parameter to every function that I implemented. But I got the idea of storing it in a global variable. But is it safe to do this?
// Macros are not isolated to anything.
nonisolated(unsafe) var currentContext: (any MacroExpansionContext)!
extension SomeMacro: MemberMacro {
static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
currentContext = context
// Do stuff
}
}
func something(_ syntax: Syntax) -> Syntax { /* do someting that accesses 'currentContext' */ }
For example, if I have 2 macros implemented in the same module, can/will they run in parallel and cause a race condition, or does each macro runs in its own isolated environment?
Have you considered creating a helper struct/class and using its stored properties to manage state? (And then implementing your logic as functions/methods on the helper type)
That's a good idea, actually. I'm going to try/use this.
Oh yeah, expansion functions are static, also a great idea. Although it may not work for multi-purpose macros, depends on the implementation.
One issue I found with this approach is the expand function. When I call it, if the macro uses the same technique (in the same module), the context gets replaced. But I can work around it by storing context in a temp variable and reassigning it after it's called.
It seems like the main question is if each macro runs in its own isolated environment, or not (excluding expand function). Maybe I should've asked that question from the start instead of this.