expander
Expands a proc-macro into a file, and uses a include!
directive in place.
Advantages
- Only expands a particular proc-macro, not all of them. I.e.
tracing
is notorious for expanding into a significant amount of boilerplate with i.e.cargo expand
- Get good errors when your generated code is not perfect yet
Usage
In your proc-macro
, use it like:
// or any other macro type
will expand into
include!;
where the file content will be
Examplary output
An error in your proc-macro, i.e. an excess ;
, is shown as
becomes
which tells you exactly where in the generated code of your proc-macro you generated that superfluous statement.
Now this was a simple example, doing this with macros that would expand to multiple tens of thousand lines of
code with cargo-expand
, but only in a few thousand that your particular one generates, it's a
life saver to know what caused the issue rather than having to use eprintln!
to print a unformated
string to the terminal.
Hint: You can quickly toggle this by using
.dry(true || false)
Special handling: syn
By default expander
is built with feature syndicate
which adds fn maybe_write_*
to struct Expander
, which aids handling of Result<TokenStream, syn::Error>
for the
commonly used rust parsing library syn
.
Reasoning
syn::Error::new(Span::call_site(),"yikes!").into_token_stream(self)
becomes compile_error!("yikes!")
which provides better info to the user (that's you!) than when serializing it to file, since the provided
span
for the syn::Error
is printed differently - being pointed to the compile_error!
invocation
in the generated file is not helpful, and rustc can point to the span
instead.