24 releases
0.11.4 | Aug 25, 2024 |
---|---|
0.11.3 | Jul 30, 2024 |
0.11.1 | Mar 16, 2024 |
0.10.4 | Nov 24, 2023 |
0.5.1 | Jul 21, 2023 |
#31 in Procedural macros
79,451 downloads per month
Used in 299 crates
(22 directly)
77KB
1K
SLoC
manyhow
anyhow for proc macros
Proc macro anyhow, a combination of ideas from
anyhow
and
proc-macro-error
to improve proc macro
development, especially focused on the error handling.
Motivation
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 implementation in
a separate function to be able to use the ergonomic rust error handling with
e.g., ?
.
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
match actual_implementation(input.into()) {
Ok(output) => output,
Err(error) => error.into_compile_error(),
}
.into()
}
fn actual_implementation(input: TokenStream2) -> syn::Result<TokenStream2> {
// ..
}
Using the #[manyhow]
macro
To activate the error handling, just add #[manyhow]
above any
proc macro implementation, reducing the above example to:
use manyhow::manyhow;
use proc_macro2::TokenStream as TokenStream2;
#[manyhow]
#[proc_macro]
fn my_macro(input: TokenStream2) -> syn::Result<TokenStream2> {
// ..
}
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
implements ToTokensError
. As additional parameters 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.
Without macros
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:
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
manyhow::function(
input,
false,
|input: TokenStream2| -> syn::Result<TokenStream2> {
// ..
},
)
}
Emitter
and dummy
TokenStream
can also be used. function
and
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 immediately.
Emitter::into_result
can be used to return if an Emitter
contains
any values.
use manyhow::{manyhow, Emitter, ErrorMessage};
use proc_macro2::TokenStream as TokenStream2;
#[manyhow]
#[proc_macro]
fn my_macro(input: TokenStream2, emitter: &mut Emitter) -> manyhow::Result<TokenStream2> {
// ..
emitter.emit(ErrorMessage::call_site("A fun error!"));
emitter.into_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}
.
Dependencies
~95–600KB
~13K SLoC