I'm experimenting with a macro that is both a MemberMacro and a ExtensionMacro. The macro should only be applied to a struct.
Both expansions look kind of similar with an early guard:
guard
let declaration = declaration.as(StructDeclSyntax.self)
else {
context.diagnose(
Diagnostic(
node: node,
message: "not a struct"
)
)
return []
}
...
What seems to be happening is that if I try and write a unit test to confirm my diagnostics are applied correctly… my compiler is generating two diagnostics: one for MemberMacro and one for ExtensionMacro.
This sort of makes sense… but I could also be missing something here. Is there any kind of shortcut where these two macro expansions could produce "the same" diagnostic but the compiler "folds" them down to one on my behalf?
I'm not able to see this specific pattern used in production libraries from Swift… but I did find a sample macro that sort of tries to work around this. The OptionSetMacro example from swift-syntax is both a MemberMacro and a ExtensionMacro and must only be applied to a struct. The MemberMacro expansion tests for struct and applies diagnostics:
But the ExtensionMacro expansions tests for struct without diagnostics:
Is this sort of the common practice at this point? Is it normal for MemberMacro to control diagnostics and ExtensionMacro generally "fails silently" when we assume the generated diagnostic would be identical?