1ea88969fSopenharmony_ci# Makes error reporting in procedural macros nice and easy
2ea88969fSopenharmony_ci
3ea88969fSopenharmony_ci[![travis ci](https://travis-ci.org/CreepySkeleton/proc-macro-error.svg?branch=master)](https://travis-ci.org/CreepySkeleton/proc-macro-error)
4ea88969fSopenharmony_ci[![docs.rs](https://docs.rs/proc-macro-error/badge.svg)](https://docs.rs/proc-macro-error)
5ea88969fSopenharmony_ci[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](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