I'm experimenting with macros for viability with code injection in variable bodies. Unfortunately, I'm getting different results from assertMacroExpansion in swift-syntax's main branch and Xcode's swift-frontend.
The following example doubles statements in computed properties:
extension InstrumentedMacro: AccessorMacro {
public static func expansion<Context: MacroExpansionContext, Declaration: DeclSyntaxProtocol>(
of node: AttributeSyntax,
providingAccessorsOf declaration: Declaration,
in context: Context
) throws -> [AccessorDeclSyntax] {
guard
let varDecl = declaration.as(VariableDeclSyntax.self),
let binding = varDecl.bindings.first,
let statements = binding.accessor?.as(CodeBlockSyntax.self)?.statements
else {
return []
}
return [
AccessorDeclSyntax(
accessorKind: "get",
body: CodeBlockSyntax(statements: CodeBlockItemListSyntax(
Array(statements) + Array(statements)
)))
]
}
}
When running assertMacroExpansion, it successfully transforms the following:
struct MyView: View {
@Instrumented
var body: some View {
Text("Hello, ")
Text("World!")
}
}
Into:
struct MyView: View {
var body: some View {
get {
Text("Hello, ")
Text("World!")
Text("Hello, ")
Text("World!")
}
}
}
When I try running it in a in Xcode 15.0 (22181.22), I get the error message, /var/folders/5n/7jx33tyj6lj7jzz48r267wpw0000gn/T/swift-generated-sources/@__swiftmacro_6MyDemo0A4ViewV4body12InstrumentedfMa_.swift:1:1 Multiple definitions of symbol '$s6MyDemo0A4ViewV4bodyQrvg'
It appears that instead of rewriting the source like swift-syntax is doing, swift-frontend is generating the code, compiling the macro result and defining trying to add it to object code that already has the symbol defined.
Who has the correct long-term behavior here? Is it assertMacroExpansion, which replaces the existing accessor or swift-frontend, which is purely additive?