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