README.md
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