1ea88969fSopenharmony_ci# Makes error reporting in procedural macros nice and easy 2ea88969fSopenharmony_ci 3ea88969fSopenharmony_ci[](https://travis-ci.org/CreepySkeleton/proc-macro-error) 4ea88969fSopenharmony_ci[](https://docs.rs/proc-macro-error) 5ea88969fSopenharmony_ci[](https://github.com/rust-secure-code/safety-dance/) 6ea88969fSopenharmony_ci 7ea88969fSopenharmony_ciThis crate aims to make error reporting in proc-macros simple and easy to use. 8ea88969fSopenharmony_ciMigrate from `panic!`-based errors for as little effort as possible! 9ea88969fSopenharmony_ci 10ea88969fSopenharmony_ciAlso, you can explicitly [append a dummy token stream][crate::dummy] to your errors. 11ea88969fSopenharmony_ci 12ea88969fSopenharmony_ciTo achieve his, this crate serves as a tiny shim around `proc_macro::Diagnostic` and 13ea88969fSopenharmony_ci`compile_error!`. It detects the most preferable way to emit errors based on compiler's version. 14ea88969fSopenharmony_ciWhen the underlying diagnostic type is finally stabilized, this crate will be simply 15ea88969fSopenharmony_cidelegating to it, requiring no changes in your code! 16ea88969fSopenharmony_ci 17ea88969fSopenharmony_ciSo you can just use this crate and have *both* some of `proc_macro::Diagnostic` functionality 18ea88969fSopenharmony_ciavailable on stable ahead of time and your error-reporting code future-proof. 19ea88969fSopenharmony_ci 20ea88969fSopenharmony_ci```toml 21ea88969fSopenharmony_ci[dependencies] 22ea88969fSopenharmony_ciproc-macro-error = "1.0" 23ea88969fSopenharmony_ci``` 24ea88969fSopenharmony_ci 25ea88969fSopenharmony_ci*Supports rustc 1.31 and up* 26ea88969fSopenharmony_ci 27ea88969fSopenharmony_ci[Documentation and guide][guide] 28ea88969fSopenharmony_ci 29ea88969fSopenharmony_ci## Quick example 30ea88969fSopenharmony_ci 31ea88969fSopenharmony_ciCode: 32ea88969fSopenharmony_ci 33ea88969fSopenharmony_ci```rust 34ea88969fSopenharmony_ci#[proc_macro] 35ea88969fSopenharmony_ci#[proc_macro_error] 36ea88969fSopenharmony_cipub fn make_fn(input: TokenStream) -> TokenStream { 37ea88969fSopenharmony_ci let mut input = TokenStream2::from(input).into_iter(); 38ea88969fSopenharmony_ci let name = input.next().unwrap(); 39ea88969fSopenharmony_ci if let Some(second) = input.next() { 40ea88969fSopenharmony_ci abort! { second, 41ea88969fSopenharmony_ci "I don't like this part!"; 42ea88969fSopenharmony_ci note = "I see what you did there..."; 43ea88969fSopenharmony_ci help = "I need only one part, you know?"; 44ea88969fSopenharmony_ci } 45ea88969fSopenharmony_ci } 46ea88969fSopenharmony_ci 47ea88969fSopenharmony_ci quote!( fn #name() {} ).into() 48ea88969fSopenharmony_ci} 49ea88969fSopenharmony_ci``` 50ea88969fSopenharmony_ci 51ea88969fSopenharmony_ciThis is how the error is rendered in a terminal: 52ea88969fSopenharmony_ci 53ea88969fSopenharmony_ci<p align="center"> 54ea88969fSopenharmony_ci<img src="https://user-images.githubusercontent.com/50968528/78830016-d3b46a80-79d6-11ea-9de2-972e8d7904ef.png" width="600"> 55ea88969fSopenharmony_ci</p> 56ea88969fSopenharmony_ci 57ea88969fSopenharmony_ciAnd this is what your users will see in their IDE: 58ea88969fSopenharmony_ci 59ea88969fSopenharmony_ci<p align="center"> 60ea88969fSopenharmony_ci<img src="https://user-images.githubusercontent.com/50968528/78830547-a9af7800-79d7-11ea-822e-59e29bda335c.png" width="600"> 61ea88969fSopenharmony_ci</p> 62ea88969fSopenharmony_ci 63ea88969fSopenharmony_ci## Examples 64ea88969fSopenharmony_ci 65ea88969fSopenharmony_ci### Panic-like usage 66ea88969fSopenharmony_ci 67ea88969fSopenharmony_ci```rust 68ea88969fSopenharmony_ciuse proc_macro_error::{ 69ea88969fSopenharmony_ci proc_macro_error, 70ea88969fSopenharmony_ci abort, 71ea88969fSopenharmony_ci abort_call_site, 72ea88969fSopenharmony_ci ResultExt, 73ea88969fSopenharmony_ci OptionExt, 74ea88969fSopenharmony_ci}; 75ea88969fSopenharmony_ciuse proc_macro::TokenStream; 76ea88969fSopenharmony_ciuse syn::{DeriveInput, parse_macro_input}; 77ea88969fSopenharmony_ciuse quote::quote; 78ea88969fSopenharmony_ci 79ea88969fSopenharmony_ci// This is your main entry point 80ea88969fSopenharmony_ci#[proc_macro] 81ea88969fSopenharmony_ci// This attribute *MUST* be placed on top of the #[proc_macro] function 82ea88969fSopenharmony_ci#[proc_macro_error] 83ea88969fSopenharmony_cipub fn make_answer(input: TokenStream) -> TokenStream { 84ea88969fSopenharmony_ci let input = parse_macro_input!(input as DeriveInput); 85ea88969fSopenharmony_ci 86ea88969fSopenharmony_ci if let Err(err) = some_logic(&input) { 87ea88969fSopenharmony_ci // we've got a span to blame, let's use it 88ea88969fSopenharmony_ci // This immediately aborts the proc-macro and shows the error 89ea88969fSopenharmony_ci // 90ea88969fSopenharmony_ci // You can use `proc_macro::Span`, `proc_macro2::Span`, and 91ea88969fSopenharmony_ci // anything that implements `quote::ToTokens` (almost every type from 92ea88969fSopenharmony_ci // `syn` and `proc_macro2`) 93ea88969fSopenharmony_ci abort!(err, "You made an error, go fix it: {}", err.msg); 94ea88969fSopenharmony_ci } 95ea88969fSopenharmony_ci 96ea88969fSopenharmony_ci // `Result` has some handy shortcuts if your error type implements 97ea88969fSopenharmony_ci // `Into<Diagnostic>`. `Option` has one unconditionally. 98ea88969fSopenharmony_ci more_logic(&input).expect_or_abort("What a careless user, behave!"); 99ea88969fSopenharmony_ci 100ea88969fSopenharmony_ci if !more_logic_for_logic_god(&input) { 101ea88969fSopenharmony_ci // We don't have an exact location this time, 102ea88969fSopenharmony_ci // so just highlight the proc-macro invocation itself 103ea88969fSopenharmony_ci abort_call_site!( 104ea88969fSopenharmony_ci "Bad, bad user! Now go stand in the corner and think about what you did!"); 105ea88969fSopenharmony_ci } 106ea88969fSopenharmony_ci 107ea88969fSopenharmony_ci // Now all the processing is done, return `proc_macro::TokenStream` 108ea88969fSopenharmony_ci quote!(/* stuff */).into() 109ea88969fSopenharmony_ci} 110ea88969fSopenharmony_ci``` 111ea88969fSopenharmony_ci 112ea88969fSopenharmony_ci### `proc_macro::Diagnostic`-like usage 113ea88969fSopenharmony_ci 114ea88969fSopenharmony_ci```rust 115ea88969fSopenharmony_ciuse proc_macro_error::*; 116ea88969fSopenharmony_ciuse proc_macro::TokenStream; 117ea88969fSopenharmony_ciuse syn::{spanned::Spanned, DeriveInput, ItemStruct, Fields, Attribute , parse_macro_input}; 118ea88969fSopenharmony_ciuse quote::quote; 119ea88969fSopenharmony_ci 120ea88969fSopenharmony_cifn process_attrs(attrs: &[Attribute]) -> Vec<Attribute> { 121ea88969fSopenharmony_ci attrs 122ea88969fSopenharmony_ci .iter() 123ea88969fSopenharmony_ci .filter_map(|attr| match process_attr(attr) { 124ea88969fSopenharmony_ci Ok(res) => Some(res), 125ea88969fSopenharmony_ci Err(msg) => { 126ea88969fSopenharmony_ci emit_error!(attr, "Invalid attribute: {}", msg); 127ea88969fSopenharmony_ci None 128ea88969fSopenharmony_ci } 129ea88969fSopenharmony_ci }) 130ea88969fSopenharmony_ci .collect() 131ea88969fSopenharmony_ci} 132ea88969fSopenharmony_ci 133ea88969fSopenharmony_cifn process_fields(_attrs: &Fields) -> Vec<TokenStream> { 134ea88969fSopenharmony_ci // processing fields in pretty much the same way as attributes 135ea88969fSopenharmony_ci unimplemented!() 136ea88969fSopenharmony_ci} 137ea88969fSopenharmony_ci 138ea88969fSopenharmony_ci#[proc_macro] 139ea88969fSopenharmony_ci#[proc_macro_error] 140ea88969fSopenharmony_cipub fn make_answer(input: TokenStream) -> TokenStream { 141ea88969fSopenharmony_ci let input = parse_macro_input!(input as ItemStruct); 142ea88969fSopenharmony_ci let attrs = process_attrs(&input.attrs); 143ea88969fSopenharmony_ci 144ea88969fSopenharmony_ci // abort right now if some errors were encountered 145ea88969fSopenharmony_ci // at the attributes processing stage 146ea88969fSopenharmony_ci abort_if_dirty(); 147ea88969fSopenharmony_ci 148ea88969fSopenharmony_ci let fields = process_fields(&input.fields); 149ea88969fSopenharmony_ci 150ea88969fSopenharmony_ci // no need to think about emitted errors 151ea88969fSopenharmony_ci // #[proc_macro_error] will handle them for you 152ea88969fSopenharmony_ci // 153ea88969fSopenharmony_ci // just return a TokenStream as you normally would 154ea88969fSopenharmony_ci quote!(/* stuff */).into() 155ea88969fSopenharmony_ci} 156ea88969fSopenharmony_ci``` 157ea88969fSopenharmony_ci 158ea88969fSopenharmony_ci## Real world examples 159ea88969fSopenharmony_ci 160ea88969fSopenharmony_ci* [`structopt-derive`](https://github.com/TeXitoi/structopt/tree/master/structopt-derive) 161ea88969fSopenharmony_ci (abort-like usage) 162ea88969fSopenharmony_ci* [`auto-impl`](https://github.com/auto-impl-rs/auto_impl/) (emit-like usage) 163ea88969fSopenharmony_ci 164ea88969fSopenharmony_ci## Limitations 165ea88969fSopenharmony_ci 166ea88969fSopenharmony_ci- Warnings are emitted only on nightly, they are ignored on stable. 167ea88969fSopenharmony_ci- "help" suggestions can't have their own span info on stable, 168ea88969fSopenharmony_ci (essentially inheriting the parent span). 169ea88969fSopenharmony_ci- If your macro happens to trigger a panic, no errors will be displayed. This is not a 170ea88969fSopenharmony_ci technical limitation but rather intentional design. `panic` is not for error reporting. 171ea88969fSopenharmony_ci 172ea88969fSopenharmony_ci## MSRV policy 173ea88969fSopenharmony_ci 174ea88969fSopenharmony_ci`proc_macro_error` will always be compatible with proc-macro Holy Trinity: 175ea88969fSopenharmony_ci`proc_macro2`, `syn`, `quote` crates. In other words, if the Trinity is available 176ea88969fSopenharmony_cito you - `proc_macro_error` is available too. 177ea88969fSopenharmony_ci 178ea88969fSopenharmony_ci> **Important!** 179ea88969fSopenharmony_ci> 180ea88969fSopenharmony_ci> If you want to use `#[proc_macro_error]` with `synstructure`, you're going 181ea88969fSopenharmony_ci> to have to put the attribute inside the `decl_derive!` invocation. Unfortunately, 182ea88969fSopenharmony_ci> due to some bug in pre-1.34 rustc, putting proc-macro attributes inside macro 183ea88969fSopenharmony_ci> invocations doesn't work, so your MSRV is effectively 1.34. 184ea88969fSopenharmony_ci 185ea88969fSopenharmony_ci## Motivation 186ea88969fSopenharmony_ci 187ea88969fSopenharmony_ciError handling in proc-macros sucks. There's not much of a choice today: 188ea88969fSopenharmony_ciyou either "bubble up" the error up to the top-level of the macro and convert it to 189ea88969fSopenharmony_cia [`compile_error!`][compl_err] invocation or just use a good old panic. Both these ways suck: 190ea88969fSopenharmony_ci 191ea88969fSopenharmony_ci- Former sucks because it's quite redundant to unroll a proper error handling 192ea88969fSopenharmony_ci just for critical errors that will crash the macro anyway; so people mostly 193ea88969fSopenharmony_ci choose not to bother with it at all and use panic. Simple `.expect` is too tempting. 194ea88969fSopenharmony_ci 195ea88969fSopenharmony_ci Also, if you do decide to implement this `Result`-based architecture in your macro 196ea88969fSopenharmony_ci you're going to have to rewrite it entirely once [`proc_macro::Diagnostic`][] is finally 197ea88969fSopenharmony_ci stable. Not cool. 198ea88969fSopenharmony_ci 199ea88969fSopenharmony_ci- Later sucks because there's no way to carry out the span info via `panic!`. 200ea88969fSopenharmony_ci `rustc` will highlight the invocation itself but not some specific token inside it. 201ea88969fSopenharmony_ci 202ea88969fSopenharmony_ci Furthermore, panics aren't for error-reporting at all; panics are for bug-detecting 203ea88969fSopenharmony_ci (like unwrapping on `None` or out-of-range indexing) or for early development stages 204ea88969fSopenharmony_ci when you need a prototype ASAP so error handling can wait. Mixing these usages only 205ea88969fSopenharmony_ci messes things up. 206ea88969fSopenharmony_ci 207ea88969fSopenharmony_ci- There is [`proc_macro::Diagnostic`][] which is awesome but it has been experimental 208ea88969fSopenharmony_ci for more than a year and is unlikely to be stabilized any time soon. 209ea88969fSopenharmony_ci 210ea88969fSopenharmony_ci This crate's API is intentionally designed to be compatible with `proc_macro::Diagnostic` 211ea88969fSopenharmony_ci and delegates to it whenever possible. Once `Diagnostics` is stable this crate 212ea88969fSopenharmony_ci will **always** delegate to it, no code changes will be required on user side. 213ea88969fSopenharmony_ci 214ea88969fSopenharmony_ciThat said, we need a solution, but this solution must meet these conditions: 215ea88969fSopenharmony_ci 216ea88969fSopenharmony_ci- It must be better than `panic!`. The main point: it must offer a way to carry the span information 217ea88969fSopenharmony_ci over to user. 218ea88969fSopenharmony_ci- It must take as little effort as possible to migrate from `panic!`. Ideally, a new 219ea88969fSopenharmony_ci macro with similar semantics plus ability to carry out span info. 220ea88969fSopenharmony_ci- It must maintain compatibility with [`proc_macro::Diagnostic`][] . 221ea88969fSopenharmony_ci- **It must be usable on stable**. 222ea88969fSopenharmony_ci 223ea88969fSopenharmony_ciThis crate aims to provide such a mechanism. All you have to do is annotate your top-level 224ea88969fSopenharmony_ci`#[proc_macro]` function with `#[proc_macro_error]` attribute and change panics to 225ea88969fSopenharmony_ci[`abort!`]/[`abort_call_site!`] where appropriate, see [the Guide][guide]. 226ea88969fSopenharmony_ci 227ea88969fSopenharmony_ci## Disclaimer 228ea88969fSopenharmony_ciPlease note that **this crate is not intended to be used in any way other 229ea88969fSopenharmony_cithan error reporting in procedural macros**, use `Result` and `?` (possibly along with one of the 230ea88969fSopenharmony_cimany helpers out there) for anything else. 231ea88969fSopenharmony_ci 232ea88969fSopenharmony_ci<br> 233ea88969fSopenharmony_ci 234ea88969fSopenharmony_ci#### License 235ea88969fSopenharmony_ci 236ea88969fSopenharmony_ci<sup> 237ea88969fSopenharmony_ciLicensed under either of <a href="LICENSE-APACHE">Apache License, Version 238ea88969fSopenharmony_ci2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. 239ea88969fSopenharmony_ci</sup> 240ea88969fSopenharmony_ci 241ea88969fSopenharmony_ci<br> 242ea88969fSopenharmony_ci 243ea88969fSopenharmony_ci<sub> 244ea88969fSopenharmony_ciUnless you explicitly state otherwise, any contribution intentionally submitted 245ea88969fSopenharmony_cifor inclusion in this crate by you, as defined in the Apache-2.0 license, shall 246ea88969fSopenharmony_cibe dual licensed as above, without any additional terms or conditions. 247ea88969fSopenharmony_ci</sub> 248ea88969fSopenharmony_ci 249ea88969fSopenharmony_ci 250ea88969fSopenharmony_ci[compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html 251ea88969fSopenharmony_ci[`proc_macro::Diagnostic`]: https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html 252ea88969fSopenharmony_ci 253ea88969fSopenharmony_ci[crate::dummy]: https://docs.rs/proc-macro-error/1/proc_macro_error/dummy/index.html 254ea88969fSopenharmony_ci[crate::multi]: https://docs.rs/proc-macro-error/1/proc_macro_error/multi/index.html 255ea88969fSopenharmony_ci 256ea88969fSopenharmony_ci[`abort_call_site!`]: https://docs.rs/proc-macro-error/1/proc_macro_error/macro.abort_call_site.html 257ea88969fSopenharmony_ci[`abort!`]: https://docs.rs/proc-macro-error/1/proc_macro_error/macro.abort.html 258ea88969fSopenharmony_ci[guide]: https://docs.rs/proc-macro-error 259