Java annotation processors come to mind as a similar model to what we're discussing here (but with the possible addition of annotation-less macros in narrower situations that you described).
That makes me think of another strong potential use case: automatic dependency injection. A set of macros could synthesize the initializers for fields marked as being injected, simplify other infrastructure around components/modules, and so forth.
This would require some degree of breadth and depth in the APIs that the macro implementation gets to access the type-checked AST, since it would need to navigate and filter type members, may want to check their conformances, things like that.
On a different note: One of the inputs to the macro is the syntax node to which it is being applied, and in SwiftSyntax those nodes also contain the trivia (whitespace and comments) that are applied to those syntactic elements.
Would that trivia be carried along through to the macro implementation? If it was, then that would in effect give us semantically important whitespace and comments, because the macro implementation could operate differently based on those elements. Let's be gross for a moment:
public struct SumOfNumberAndLeadingSpaces: ExpressionMacro {
public static func apply(
arguments: (String?, ExprSyntax)..., in context: MacroEvaluationContext
) -> (ExprSyntax, [Diagnostic]) {
guard let integerExpr = arguments[0].1 as? IntegerLiteralExprSyntax else {
return (arguments[0].1, [ /* some diagnostic */ ])
}
let numSpaces = node.leadingTrivia.reduce(Int(integerExpr.digits.text)) { result, trivia in
switch trivia {
case .spaces(let count): return result + count
default: return result
}
}
return ("\(numSpaces)", [])
}
}
let x = #sumOfNumberAndLeadingSpaces( 5) // x = 12
I assume this is something we'd never want anyone to do. At the same time, I don't think we can completely strip the trivia from the nodes, because the Stringify example in the vision document relies on the whitespace being the same as what was passed into the macro.
I wonder if there's a way we can thread this needle to prevent egregious use cases, or if we just need to assume that users won't do horrible things (as we do for many other language features).