Proc macro anyhow, a combination of ideas from
anyhow
and
proc-macro-error
to improve proc macro
development, especially focused on the error handling.
Error handling in proc-macros is unideal, as the top level functions of proc
macros can only return TokenStreams
both in success and failure case. This
means that I often write code like this, moving the actual impelemtation in
a seperate function to be able to use the ergonomic rust error handling with
e.g. ?
.
```rust
use proc_macro2::TokenStream as TokenStream2;
pub fn mymacro(input: TokenStream) -> TokenStream { match actualimplementation(input.into()) { Ok(output) => output, Err(error) => error.intocompileerror(), } .into() }
fn actual_implementation(input: TokenStream2) -> syn::Result
#[manyhow]
macroTo activate the error hadling, just add #[manyhow]
above any
proc macro implementation, reducing the above example to:
```rust use manyhow::manyhow; use proc_macro2::TokenStream as TokenStream2;
fn my_macro(input: TokenStream2) -> syn::Result
See Without macros to see what this expands to under the hood.
A proc macro function marked as #[manyhow]
can take and return any
TokenStream
and can also return Result<TokenStream,
E>
where E
implments ToTokensError
. As additional paramters a
dummy and/or emitter can
be specified.
The manyhow
attribute takes one optional flag when used for proc_macro
and proc_macro_attribute
. #[manyhow(input_as_dummy)]
will take the input
of a function like proc_macro
to initialize the dummy &mut
TokenStream
while #[manyhow(item_as_dummy)]
on
proc_macro_attribute
will initialize the dummy with the annotated item.
manyhow
can be used without proc macros, and they can be disabled by
adding manyhow
with default-features=false
.
The usage is more or less the same, though with some added boilerplate from
needing to invoke one of function
, attribute
or derive
directly.
While the examples use closures, functions can be passed in as well. The above example would then change to: ```rust use proc_macro2::TokenStream as TokenStream2;
pub fn my_macro(input: TokenStream) -> TokenStream {
manyhow::function(
input,
false,
|input: TokenStream2| -> syn::Result
[
Emitter](#emitter-mut-emitter) and [dummy
TokenStream](#dummy-mut-tokenstream) can also be used.
functionand
attribute` take an additional boolean parameter controlling whether the
input/item will be used as initial dummy.
emitter: &mut Emitter
MacroHandler
s (the trait defining what closures/functions can be used
with manyhow
) can take a mutable reference to an Emitter
. This
allows to collect errors, but not fail imidiatly.
Emitter::fail_if_dirty
can be used to return if an Emitter
contains
any values.
```rust use manyhow::{manyhow, Emitter, ErrorMessage}; use proc_macro2::TokenStream as TokenStream2;
fn mymacro(input: TokenStream2, emitter: &mut Emitter) -> manyhow::Result
dummy: &mut TokenStream
MacroHandler
s also take a mutable reference to a TokenStream
, to
enable emitting some dummy code to be used in case the macro errors.
This allows either appending tokens e.g. with ToTokens::to_tokens
or
directly setting the dummy code e.g. *dummy = quote!{some tokens}
.