proc-macro-error

travis ci docs.rs unsafe forbidden

This crate aims to make error reporting in proc-macros simple and easy to use. Migrate from panic!-based errors for as little effort as possible!

Also, there's ability to append a dummy token stream to your errors.

toml [dependencies] proc-macro-error = "0.4"

Supports rustc 1.31 and up

Documentation and guide

What emitted errors look like

``` error: multiple error part: multi2

= note: help message test = help: Option help test = note: I see what you did here...

--> $DIR/multi-error.rs:4:18 | 4 | make_fn!(multi1, multi2, _, multi3); | ^^^^^^ ```

Examples

Panic-like usage

```rust use procmacroerror::*; use procmacro::TokenStream; use syn::{DeriveInput, parsemacro_input}; use quote::quote;

// This is your main entry point

[proc_macro]

// this attribute MUST be placed on top of the #[proc_macro] function

[procmacroerror]

pub fn makeanswer(input: TokenStream) -> TokenStream { let input = parsemacro_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
    //
    // You can use `proc_macro::Span`, `proc_macro2::Span`, and
    // anything that implements `quote::ToTokens` (almost every type from
    // `syn` and `proc_macro2`)
    abort!(err, "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()

} ```

proc_macro::Diagnostic-like usage

```rust use procmacroerror::*; use procmacro::TokenStream; use syn::{spanned::Spanned, DeriveInput, ItemStruct, Fields, Attribute , parsemacro_input}; use quote::quote;

fn processattrs(attrs: &[Attribute]) -> Vec { attrs .iter() .filtermap(|attr| match processattr(attr) { Ok(res) => Some(res), Err(msg) => { emiterror!(attr, "Invalid attribute: {}", msg); None } }) .collect() }

fn processfields(attrs: &Fields) -> Vec { // processing fields in pretty much the same way as attributes unimplemented!() }

[proc_macro]

[procmacroerror]

pub fn makeanswer(input: TokenStream) -> TokenStream { let input = parsemacroinput!(input as ItemStruct); let attrs = processattrs(&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_error] will handle them for you
//
// just return a TokenStream as you normally would
quote!(/* stuff */).into()

} ```

Limitations

MSRV policy

proc_macro_error will always be compatible with proc-macro Holy Trinity: proc_macro2, syn, quote crates. In other words, if the Trinity is available to you - proc_macro_error is available too.

Important!

If you want to use #[proc_macro_error] with synstructure, you're going to have to put the attribute inside the decl_derive! invocation. Unfortunately, due to some bug in pre-1.34 rustc, putting proc-macro attributes inside macro invocations doesn't work, so your MSRV is effectively 1.34 in cases like that.

Motivation

Error handling in proc-macros sucks. There's not much of a choice today: you either "bubble up" the error up to the top-level of the macro and convert it to a compile_error! invocation or just use a good old panic. Both these ways suck:

That said, we need a solution, but this solution must meet these conditions:

This crate aims to provide such a mechanism. All you have to do is annotate your top-level #[proc_macro] function with #[proc_macro_errors] attribute and change panics to [abort!]/[abort_call_site!] where appropriate, see the Guide.

Disclaimer

Please note that this crate is not intended to be used in any way other than proc-macro error reporting, use Result and ? for anything else.


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.