Announcement proc-macro-error
crate.
Summary
Move from panic-based errors for as little effort as possible! Built in hope that people would stop using panics in proc-macros (and I finally get to see nice descriptive errors pointing to the origin location).
Supports single shot panic-like errors, errors that not abort right away (emitting instead of aborting), dummy implementations.
Limitations
- No support for warnings.
- Very limited support for "help" suggestions.
- If a panic occurs somewhere in your macro no errors will be displayed.
Example
Panic-like usage
use proc_macro_error::*;
use proc_macro::TokenStream;
use syn::{DeriveInput, parse_macro_input};
use quote::quote;
// This is your main entry point
#[proc_macro]
// this attribute *MUST* be placed on top of the #[proc_macro] function
#[proc_macro_error]
pub fn make_answer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
if let Err(err) = some_logic(&input) {
// we've got a span to blame, let's use it
// This immediately aborts the proc-macro and shows the error
abort!(err.span, "You made an error, go fix it: {}", err.msg);
}
// `Result` has some handy shortcuts if your error type implements
// `Into<MacroError>`. `Option` has one unconditionally.
more_logic(&input).expect_or_abort("What a careless user, behave!");
if !more_logic_for_logic_god(&input) {
// We don't have an exact location this time,
// so just highlight the proc-macro invocation itself
abort_call_site!(
"Bad, bad user! Now go stand in the corner and think about what you did!");
}
// Now all the processing is done, return `proc_macro::TokenStream`
quote!(/* stuff */).into()
}
Multiple errors
use proc_macro_error::*;
use proc_macro::TokenStream;
use syn::{spanned::Spanned, DeriveInput, ItemStruct, Fields, Attribute , parse_macro_input};
use quote::quote;
fn process_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
attrs
.iter()
.filter_map(|attr| match process_attr(attr) {
Ok(res) => Some(res),
Err(msg) => {
emit_error!(attr.span(), "Invalid attribute: {}", msg);
None
}
})
.collect()
}
fn process_fields(_attrs: &Fields) -> Vec<TokenStream> {
// processing fields in pretty much the same way as attributes
unimplemented!()
}
#[proc_macro]
#[proc_macro_error]
pub fn make_answer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
let attrs = process_attrs(&input.attrs);
// abort right now if some errors were encountered
// at the attributes processing stage
abort_if_dirty();
let fields = process_fields(&input.fields);
// no need to think about emitted errors
// #[proc_macro_errors] will handle them for you
//
// just return a TokenStream as you normally would
quote!(/* stuff */).into()
}