Skip to main content

proc_macro_error3/
lib.rs

1//! # proc-macro-error3
2//!
3//! This crate aims to make error reporting in proc-macros simple and easy to use.
4//! Migrate from `panic!`-based errors for as little effort as possible!
5//!
6//! (Also, you can explicitly [append a dummy token stream](dummy/index.html) to your errors).
7//!
8//! To achieve his, this crate serves as a tiny shim around `proc_macro::Diagnostic` and
9//! `compile_error!`. It detects the best way of emitting available based on compiler's version.
10//! When the underlying diagnostic type is finally stabilized, this crate will simply be
11//! delegating to it requiring no changes in your code!
12//!
13//! So you can just use this crate and have *both* some of `proc_macro::Diagnostic` functionality
14//! available on stable ahead of time *and* your error-reporting code future-proof.
15//!
16//! ## Cargo features
17//!
18//! This crate enables the `syn3-error` feature by default, providing `impl From<syn::Error> for
19//! Diagnostic` conversion with `syn` v3. The legacy `syn-error` feature is an alias for
20//! `syn3-error`. Crates that still parse with `syn` v2 can disable default features and opt into
21//! `syn2-error` instead:
22//!
23//! ```toml
24//! [dependencies]
25//! proc-macro-error3 = { version = "3.0", default-features = false, features = ["syn2-error"] }
26//! ```
27//!
28//! If you don't use `syn` and want to cut off some compilation time, you can disable `syn`
29//! integrations by disabling default features
30//!
31//! ```toml
32//! [dependencies]
33//! proc-macro-error3 = { version = "3.0", default-features = false }
34//! ```
35//!
36//! ***Please note that disabling these features makes sense only if you don't depend on `syn`
37//! directly or indirectly, and you very likely do.**
38//!
39//! ## Real world examples
40//!
41//! * [`structopt-derive`](https://github.com/TeXitoi/structopt/tree/master/structopt-derive)
42//!   (abort-like usage)
43//! * [`auto-impl`](https://github.com/auto-impl-rs/auto_impl/) (emit-like usage)
44//!
45//! ## Limitations
46//!
47//! - Warnings are emitted only on nightly, they are ignored on stable.
48//! - "help" suggestions can't have their own span info on stable,
49//!   (essentially inheriting the parent span).
50//! - If a panic occurs somewhere in your macro no errors will be displayed. This is not a
51//!   technical limitation but rather intentional design. `panic` is not for error reporting.
52//!
53//! ### `#[proc_macro_error]` attribute
54//!
55//! **This attribute MUST be present on the top level of your macro** (the function
56//! annotated with any of `#[proc_macro]`, `#[proc_macro_derive]`, `#[proc_macro_attribute]`).
57//!
58//! This attribute performs the setup and cleanup necessary to make things work.
59//!
60//! In most cases you'll need the simple `#[proc_macro_error]` form without any
61//! additional settings. Feel free to [skip the "Syntax" section](#macros).
62//!
63//! #### Syntax
64//!
65//! `#[proc_macro_error]` or `#[proc_macro_error(settings...)]`, where `settings...`
66//! is a comma-separated list of:
67//!
68//! - `proc_macro_hack`:
69//!
70//!   In order to correctly cooperate with `#[proc_macro_hack]`, `#[proc_macro_error]`
71//!   attribute must be placed *before* (above) it, like this:
72//!
73//!   ```no_run
74//!   # use proc_macro2::TokenStream;
75//!   # const IGNORE: &str = "
76//!   #[proc_macro_error]
77//!   #[proc_macro_hack]
78//!   #[proc_macro]
79//!   # ";
80//!   fn my_macro(input: TokenStream) -> TokenStream {
81//!       unimplemented!()
82//!   }
83//!   ```
84//!
85//!   If, for some reason, you can't place it like that you can use
86//!   `#[proc_macro_error(proc_macro_hack)]` instead.
87//!
88//!   # Note
89//!
90//!   If `proc-macro-hack` was detected (by any means) `allow_not_macro`
91//!   and `assert_unwind_safe` will be applied automatically.
92//!
93//! - `allow_not_macro`:
94//!
95//!   By default, the attribute checks that it's applied to a proc-macro.
96//!   If none of `#[proc_macro]`, `#[proc_macro_derive]` nor `#[proc_macro_attribute]` are
97//!   present it will panic. It's the intention - this crate is supposed to be used only with
98//!   proc-macros.
99//!
100//!   This setting is made to bypass the check, useful in certain circumstances.
101//!
102//!   Pay attention: the function this attribute is applied to must return
103//!   `proc_macro::TokenStream`.
104//!
105//!   This setting is implied if `proc-macro-hack` was detected.
106//!
107//! `assert_unwind_safe`:
108//!
109//!   By default, your code must be [unwind safe]. If your code is not unwind safe,
110//!   but you believe it's correct, you can use this setting to bypass the check.
111//!   You would need this for code that uses `lazy_static` or `thread_local` with
112//!   `Cell/RefCell` inside (and the like).
113//!
114//!   This setting is implied if `#[proc_macro_error]` is applied to a function
115//!   marked as `#[proc_macro]`, `#[proc_macro_derive]` or `#[proc_macro_attribute]`.
116//!
117//!   This setting is also implied if `proc-macro-hack` was detected.
118//!
119//! ## Macros
120//!
121//! Most of the time you want to use the macros. Syntax is described in the next section below.
122//!
123//! You'll need to decide how you want to emit errors:
124//!
125//! * Emit the error and abort. Very much panic-like usage. Served by [`abort!`] and
126//!   [`abort_call_site!`].
127//! * Emit the error but do not abort right away, looking for other errors to report.
128//!   Served by [`emit_error!`] and [`emit_call_site_error!`].
129//!
130//! You **can** mix these usages.
131//!
132//! `abort` and `emit_error` take a "source span" as the first argument. This source
133//! will be used to highlight the place the error originates from. It must be one of:
134//!
135//! * *Something* that implements [`ToTokens`] (most types in `syn` and `proc-macro2` do).
136//!   This source is the preferable one since it doesn't lose span information on multi-token
137//!   spans, see [this issue](https://gitlab.com/CreepySkeleton/proc-macro-error/-/issues/6)
138//!   for details.
139//! * [`proc_macro::Span`]
140//! * [`proc-macro2::Span`]
141//!
142//! The rest is your message in format-like style.
143//!
144//! See [the next section](#syntax-1) for detailed syntax.
145//!
146//! - [`abort!`]:
147//!
148//!   Very much panic-like usage - abort right away and show the error.
149//!   Expands to [`!`] (never type).
150//!
151//! - [`abort_call_site!`]:
152//!
153//!     Shortcut for `abort!(Span::call_site(), ...)`. Expands to [`!`] (never type).
154//!
155//! - [`emit_error!`]:
156//!
157//!   [`proc_macro::Diagnostic`]-like usage - emit the error but keep going,
158//!   looking for other errors to report.
159//!   The compilation will fail nonetheless. Expands to [`()`] (unit type).
160//!
161//! - [`emit_call_site_error!`]:
162//!
163//!   Shortcut for `emit_error!(Span::call_site(), ...)`. Expands to [`()`] (unit type).
164//!
165//! - [`emit_warning!`]:
166//!
167//!   Like `emit_error!` but emit a warning instead of error. The compilation won't fail
168//!   because of warnings.
169//!   Expands to [`()`] (unit type).
170//!
171//!   **Beware**: warnings are nightly only, they are completely ignored on stable.
172//!
173//! - [`emit_call_site_warning!`]:
174//!
175//!   Shortcut for `emit_warning!(Span::call_site(), ...)`. Expands to [`()`] (unit type).
176//!
177//! - [`diagnostic`]:
178//!
179//!   Build an instance of `Diagnostic` in format-like style.
180//!
181//! #### Syntax
182//!
183//! All the macros have pretty much the same syntax:
184//!
185//! 1.  ```ignore
186//!     abort!(single_expr)
187//!     ```
188//!     Shortcut for `Diagnostic::from(expr).abort()`.
189//!
190//! 2.  ```ignore
191//!     abort!(span, message)
192//!     ```
193//!     The first argument is an expression the span info should be taken from.
194//!
195//!     The second argument is the error message, it must implement [`ToString`].
196//!
197//! 3.  ```ignore
198//!     abort!(span, format_literal, format_args...)
199//!     ```
200//!
201//!     This form is pretty much the same as 2, except `format!(format_literal, format_args...)`
202//!     will be used to for the message instead of [`ToString`].
203//!
204//! That's it. `abort!`, `emit_warning`, `emit_error` share this exact syntax.
205//!
206//! `abort_call_site!`, `emit_call_site_warning`, `emit_call_site_error` lack 1 form
207//! and do not take span in 2'th and 3'th forms. Those are essentially shortcuts for
208//! `macro!(Span::call_site(), args...)`.
209//!
210//! `diagnostic!` requires a [`Level`] instance between `span` and second argument
211//! (1'th form is the same).
212//!
213//! > **Important!**
214//! >
215//! > If you have some type from `proc_macro` or `syn` to point to, do not call `.span()`
216//! > on it but rather use it directly:
217//! > ```no_run
218//! > # use proc_macro_error3::abort;
219//! > # #[cfg(all(feature = "syn2-error", not(feature = "syn3-error")))] use syn2 as syn;
220//! > # #[cfg(feature = "syn3-error")] use syn3 as syn;
221//! > let err = syn::Error::new(proc_macro2::Span::call_site(), "bad input");
222//! > abort!(err);
223//! > //     ^^^ <-- avoid .span()
224//! > ```
225//! >
226//! > `.span()` calls work too, but you may experience regressions in message quality.
227//!
228//! #### Note attachments
229//!
230//! 3.  Every macro can have "note" attachments (only 2 and 3 form).
231//!   ```ignore
232//!   let opt_help = if have_some_info { Some("did you mean `this`?") } else { None };
233//!
234//!   abort!(
235//!       span, message; // <--- attachments start with `;` (semicolon)
236//!
237//!       help = "format {} {}", "arg1", "arg2"; // <--- every attachment ends with `;`,
238//!                                              //      maybe except the last one
239//!
240//!       note = "to_string"; // <--- one arg uses `.to_string()` instead of `format!()`
241//!
242//!       yay = "I see what {} did here", "you"; // <--- "help =" and "hint =" are mapped
243//!                                              // to Diagnostic::help,
244//!                                              // anything else is Diagnostic::note
245//!
246//!       wow = note_span => "custom span"; // <--- attachments can have their own span
247//!                                         //      it takes effect only on nightly though
248//!
249//!       hint =? opt_help; // <-- "optional" attachment, get displayed only if `Some`
250//!                         //     must be single `Option` expression
251//!
252//!       note =? note_span => opt_help // <-- optional attachments can have custom spans too
253//!   );
254//!   ```
255//!
256
257//! ### Diagnostic type
258//!
259//! [`Diagnostic`] type is intentionally designed to be API compatible with [`proc_macro::Diagnostic`].
260//! Not all API is implemented, only the part that can be reasonably implemented on stable.
261//!
262//!
263//! [`abort!`]: macro.abort.html
264//! [`abort_call_site!`]: macro.abort_call_site.html
265//! [`emit_warning!`]: macro.emit_warning.html
266//! [`emit_error!`]: macro.emit_error.html
267//! [`emit_call_site_warning!`]: macro.emit_call_site_warning.html
268//! [`emit_call_site_error!`]: macro.emit_call_site_error.html
269//! [`diagnostic!`]: macro.diagnostic.html
270//! [`Diagnostic`]: struct.Diagnostic.html
271//!
272//! [`proc_macro::Span`]: https://doc.rust-lang.org/proc_macro/struct.Span.html
273//! [`proc_macro::Diagnostic`]: https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html
274//!
275//! [unwind safe]: https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html#what-is-unwind-safety
276//! [`!`]: https://doc.rust-lang.org/std/primitive.never.html
277//! [`()`]: https://doc.rust-lang.org/std/primitive.unit.html
278//! [`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html
279//!
280//! [`proc-macro2::Span`]: https://docs.rs/proc-macro2/1.0.10/proc_macro2/struct.Span.html
281//! [`ToTokens`]: https://docs.rs/quote/1.0.3/quote/trait.ToTokens.html
282//!
283
284#![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic))]
285#![forbid(unsafe_code)]
286
287#[doc(hidden)]
288pub extern crate proc_macro;
289
290pub use crate::{
291    diagnostic::{Diagnostic, DiagnosticExt, Level},
292    dummy::{append_dummy, set_dummy},
293};
294pub use proc_macro_error_attr3::proc_macro_error;
295
296use proc_macro2::Span;
297use quote::{quote, ToTokens};
298
299use std::cell::Cell;
300use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
301
302pub mod dummy;
303
304mod diagnostic;
305mod macros;
306mod sealed;
307
308#[cfg(not(feature = "nightly"))]
309#[path = "imp/fallback.rs"]
310mod imp;
311
312#[cfg(feature = "nightly")]
313#[path = "imp/delegate.rs"]
314mod imp;
315
316#[derive(Debug, Clone, Copy)]
317#[must_use = "A SpanRange does nothing unless used"]
318pub struct SpanRange {
319    pub first: Span,
320    pub last: Span,
321}
322
323impl SpanRange {
324    /// Create a range with the `first` and `last` spans being the same.
325    pub fn single_span(span: Span) -> Self {
326        SpanRange {
327            first: span,
328            last: span,
329        }
330    }
331
332    /// Create a `SpanRange` resolving at call site.
333    pub fn call_site() -> Self {
334        SpanRange::single_span(Span::call_site())
335    }
336
337    /// Construct span range from a `TokenStream`. This method always preserves all the
338    /// range.
339    ///
340    /// ### Note
341    ///
342    /// If the stream is empty, the result is `SpanRange::call_site()`. If the stream
343    /// consists of only one `TokenTree`, the result is `SpanRange::single_span(tt.span())`
344    /// that doesn't lose anything.
345    pub fn from_tokens(ts: &dyn ToTokens) -> Self {
346        let mut spans = ts.to_token_stream().into_iter().map(|tt| tt.span());
347        let first = spans.next().unwrap_or_else(Span::call_site);
348        let last = spans.last().unwrap_or(first);
349
350        SpanRange { first, last }
351    }
352
353    /// Join two span ranges. The resulting range will start at `self.first` and end at
354    /// `other.last`.
355    pub fn join_range(self, other: SpanRange) -> Self {
356        SpanRange {
357            first: self.first,
358            last: other.last,
359        }
360    }
361
362    /// Collapse the range into single span, preserving as much information as possible.
363    #[must_use]
364    pub fn collapse(self) -> Span {
365        self.first.join(self.last).unwrap_or(self.first)
366    }
367}
368
369/// This traits expands `Result<T, Into<Diagnostic>>` with some handy shortcuts.
370pub trait ResultExt {
371    type Ok;
372
373    /// Behaves like `Result::unwrap`: if self is `Ok` yield the contained value,
374    /// otherwise abort macro execution via `abort!`.
375    fn unwrap_or_abort(self) -> Self::Ok;
376
377    /// Behaves like `Result::expect`: if self is `Ok` yield the contained value,
378    /// otherwise abort macro execution via `abort!`.
379    /// If it aborts then resulting error message will be preceded with `message`.
380    fn expect_or_abort(self, msg: &str) -> Self::Ok;
381}
382
383/// This traits expands `Option` with some handy shortcuts.
384pub trait OptionExt {
385    type Some;
386
387    /// Behaves like `Option::expect`: if self is `Some` yield the contained value,
388    /// otherwise abort macro execution via `abort_call_site!`.
389    /// If it aborts the `message` will be used for [`compile_error!`][compl_err] invocation.
390    ///
391    /// [compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html
392    fn expect_or_abort(self, msg: &str) -> Self::Some;
393}
394
395/// Abort macro execution and display all the emitted errors, if any.
396///
397/// Does nothing if no errors were emitted (warnings do not count).
398pub fn abort_if_dirty() {
399    imp::abort_if_dirty();
400}
401
402impl<T, E: Into<Diagnostic>> ResultExt for Result<T, E> {
403    type Ok = T;
404
405    fn unwrap_or_abort(self) -> T {
406        match self {
407            Ok(res) => res,
408            Err(e) => e.into().abort(),
409        }
410    }
411
412    fn expect_or_abort(self, message: &str) -> T {
413        match self {
414            Ok(res) => res,
415            Err(e) => {
416                let mut e = e.into();
417                e.msg = format!("{}: {}", message, e.msg);
418                e.abort()
419            }
420        }
421    }
422}
423
424impl<T> OptionExt for Option<T> {
425    type Some = T;
426
427    fn expect_or_abort(self, message: &str) -> T {
428        match self {
429            Some(res) => res,
430            None => abort_call_site!(message),
431        }
432    }
433}
434
435/// This is the entry point for a proc-macro.
436///
437/// **NOT PUBLIC API, SUBJECT TO CHANGE WITHOUT ANY NOTICE**
438#[doc(hidden)]
439pub fn entry_point<F>(f: F, proc_macro_hack: bool) -> proc_macro::TokenStream
440where
441    F: FnOnce() -> proc_macro::TokenStream + UnwindSafe,
442{
443    ENTERED_ENTRY_POINT.with(|flag| flag.set(flag.get() + 1));
444    let caught = catch_unwind(f);
445    let dummy = dummy::cleanup();
446    let err_storage = imp::cleanup();
447    ENTERED_ENTRY_POINT.with(|flag| flag.set(flag.get() - 1));
448
449    let gen_error = || {
450        if proc_macro_hack {
451            quote! {{
452                macro_rules! proc_macro_call {
453                    () => ( unimplemented!() )
454                }
455
456                #(#err_storage)*
457                #dummy
458
459                unimplemented!()
460            }}
461        } else {
462            quote!( #(#err_storage)* #dummy )
463        }
464    };
465
466    match caught {
467        Ok(ts) => {
468            if err_storage.is_empty() {
469                ts
470            } else {
471                gen_error().into()
472            }
473        }
474
475        Err(boxed) => match boxed.downcast::<AbortNow>() {
476            Ok(_) => gen_error().into(),
477            Err(boxed) => resume_unwind(boxed),
478        },
479    }
480}
481
482fn abort_now() -> ! {
483    check_correctness();
484    std::panic::panic_any(AbortNow)
485}
486
487thread_local! {
488    static ENTERED_ENTRY_POINT: Cell<usize> = const { Cell::new(0) };
489}
490
491struct AbortNow;
492
493fn check_correctness() {
494    assert!(
495        ENTERED_ENTRY_POINT.with(Cell::get) != 0,
496        "proc-macro-error3 API cannot be used outside of `entry_point` invocation, \
497             perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]"
498    );
499}
500
501/// **ALL THE STUFF INSIDE IS NOT PUBLIC API!!!**
502#[doc(hidden)]
503pub mod __export {
504    // reexports for use in macros
505    pub use proc_macro;
506    pub use proc_macro2;
507
508    use proc_macro2::Span;
509    use quote::ToTokens;
510
511    use crate::SpanRange;
512
513    // inspired by
514    // https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md#simple-application
515
516    pub trait SpanAsSpanRange {
517        #[allow(non_snake_case)]
518        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
519    }
520
521    pub trait Span2AsSpanRange {
522        #[allow(non_snake_case)]
523        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
524    }
525
526    pub trait ToTokensAsSpanRange {
527        #[allow(non_snake_case)]
528        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
529    }
530
531    pub trait SpanRangeAsSpanRange {
532        #[allow(non_snake_case)]
533        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
534    }
535
536    impl<T: ToTokens> ToTokensAsSpanRange for &T {
537        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
538            let mut ts = self.to_token_stream().into_iter();
539            let first = match ts.next() {
540                Some(t) => t.span(),
541                None => Span::call_site(),
542            };
543
544            let last = match ts.last() {
545                Some(t) => t.span(),
546                None => first,
547            };
548
549            SpanRange { first, last }
550        }
551    }
552
553    impl Span2AsSpanRange for Span {
554        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
555            SpanRange {
556                first: *self,
557                last: *self,
558            }
559        }
560    }
561
562    impl SpanAsSpanRange for proc_macro::Span {
563        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
564            SpanRange {
565                first: (*self).into(),
566                last: (*self).into(),
567            }
568        }
569    }
570
571    impl SpanRangeAsSpanRange for SpanRange {
572        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
573            *self
574        }
575    }
576}