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