Compile time source generation
That is, instead of using simple string concatenation (via a crude macro system) or trying to make the macro system Turing complete with a lot of extra syntax, you could use the very source language to do this.
Something like C# source generators.
This is a extreme form of the lexer hack, where the compiler runs some previous compiled source generator at some specifics steps on parsing and/or compiling stages of normal code, so this source generators inject pure new code, or directly inspect and create/modify AST nodes in the intermediary compiled state.
#[sgen(Display)] // Register a source generator for later execution.
struct Foo<T>(T); // The "visible" source code, parsed from file.
// When the compiler finds the end of struct in visible source code,
// execute registered source generators, to inject new code or to
// modify the parsed AST. (after the only `;` in this example)
If the compiler is already bootstrapped, it's easier to implement source generators, as any internal compiler infrastructure can be exposed without code duplication.