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 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-error3 = { version = "3.0", 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_error3::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_warning.html
258//! [`emit_call_site_error!`]: macro.emit_call_site_error.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(feature = "nightly", feature(proc_macro_diagnostic))]
275#![forbid(unsafe_code)]
276
277#[doc(hidden)]
278pub extern crate proc_macro;
279
280pub use crate::{
281 diagnostic::{Diagnostic, DiagnosticExt, Level},
282 dummy::{append_dummy, set_dummy},
283};
284pub use proc_macro_error_attr3::proc_macro_error;
285
286use proc_macro2::Span;
287use quote::{quote, ToTokens};
288
289use std::cell::Cell;
290use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
291
292pub mod dummy;
293
294mod diagnostic;
295mod macros;
296mod sealed;
297
298#[cfg(not(feature = "nightly"))]
299#[path = "imp/fallback.rs"]
300mod imp;
301
302#[cfg(feature = "nightly")]
303#[path = "imp/delegate.rs"]
304mod imp;
305
306#[derive(Debug, Clone, Copy)]
307#[must_use = "A SpanRange does nothing unless used"]
308pub struct SpanRange {
309 pub first: Span,
310 pub last: Span,
311}
312
313impl SpanRange {
314 /// Create a range with the `first` and `last` spans being the same.
315 pub fn single_span(span: Span) -> Self {
316 SpanRange {
317 first: span,
318 last: span,
319 }
320 }
321
322 /// Create a `SpanRange` resolving at call site.
323 pub fn call_site() -> Self {
324 SpanRange::single_span(Span::call_site())
325 }
326
327 /// Construct span range from a `TokenStream`. This method always preserves all the
328 /// range.
329 ///
330 /// ### Note
331 ///
332 /// If the stream is empty, the result is `SpanRange::call_site()`. If the stream
333 /// consists of only one `TokenTree`, the result is `SpanRange::single_span(tt.span())`
334 /// that doesn't lose anything.
335 pub fn from_tokens(ts: &dyn ToTokens) -> Self {
336 let mut spans = ts.to_token_stream().into_iter().map(|tt| tt.span());
337 let first = spans.next().unwrap_or_else(Span::call_site);
338 let last = spans.last().unwrap_or(first);
339
340 SpanRange { first, last }
341 }
342
343 /// Join two span ranges. The resulting range will start at `self.first` and end at
344 /// `other.last`.
345 pub fn join_range(self, other: SpanRange) -> Self {
346 SpanRange {
347 first: self.first,
348 last: other.last,
349 }
350 }
351
352 /// Collapse the range into single span, preserving as much information as possible.
353 #[must_use]
354 pub fn collapse(self) -> Span {
355 self.first.join(self.last).unwrap_or(self.first)
356 }
357}
358
359/// This traits expands `Result<T, Into<Diagnostic>>` with some handy shortcuts.
360pub trait ResultExt {
361 type Ok;
362
363 /// Behaves like `Result::unwrap`: if self is `Ok` yield the contained value,
364 /// otherwise abort macro execution via `abort!`.
365 fn unwrap_or_abort(self) -> Self::Ok;
366
367 /// Behaves like `Result::expect`: if self is `Ok` yield the contained value,
368 /// otherwise abort macro execution via `abort!`.
369 /// If it aborts then resulting error message will be preceded with `message`.
370 fn expect_or_abort(self, msg: &str) -> Self::Ok;
371}
372
373/// This traits expands `Option` with some handy shortcuts.
374pub trait OptionExt {
375 type Some;
376
377 /// Behaves like `Option::expect`: if self is `Some` yield the contained value,
378 /// otherwise abort macro execution via `abort_call_site!`.
379 /// If it aborts the `message` will be used for [`compile_error!`][compl_err] invocation.
380 ///
381 /// [compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html
382 fn expect_or_abort(self, msg: &str) -> Self::Some;
383}
384
385/// Abort macro execution and display all the emitted errors, if any.
386///
387/// Does nothing if no errors were emitted (warnings do not count).
388pub fn abort_if_dirty() {
389 imp::abort_if_dirty();
390}
391
392impl<T, E: Into<Diagnostic>> ResultExt for Result<T, E> {
393 type Ok = T;
394
395 fn unwrap_or_abort(self) -> T {
396 match self {
397 Ok(res) => res,
398 Err(e) => e.into().abort(),
399 }
400 }
401
402 fn expect_or_abort(self, message: &str) -> T {
403 match self {
404 Ok(res) => res,
405 Err(e) => {
406 let mut e = e.into();
407 e.msg = format!("{}: {}", message, e.msg);
408 e.abort()
409 }
410 }
411 }
412}
413
414impl<T> OptionExt for Option<T> {
415 type Some = T;
416
417 fn expect_or_abort(self, message: &str) -> T {
418 match self {
419 Some(res) => res,
420 None => abort_call_site!(message),
421 }
422 }
423}
424
425/// This is the entry point for a proc-macro.
426///
427/// **NOT PUBLIC API, SUBJECT TO CHANGE WITHOUT ANY NOTICE**
428#[doc(hidden)]
429pub fn entry_point<F>(f: F, proc_macro_hack: bool) -> proc_macro::TokenStream
430where
431 F: FnOnce() -> proc_macro::TokenStream + UnwindSafe,
432{
433 ENTERED_ENTRY_POINT.with(|flag| flag.set(flag.get() + 1));
434 let caught = catch_unwind(f);
435 let dummy = dummy::cleanup();
436 let err_storage = imp::cleanup();
437 ENTERED_ENTRY_POINT.with(|flag| flag.set(flag.get() - 1));
438
439 let gen_error = || {
440 if proc_macro_hack {
441 quote! {{
442 macro_rules! proc_macro_call {
443 () => ( unimplemented!() )
444 }
445
446 #(#err_storage)*
447 #dummy
448
449 unimplemented!()
450 }}
451 } else {
452 quote!( #(#err_storage)* #dummy )
453 }
454 };
455
456 match caught {
457 Ok(ts) => {
458 if err_storage.is_empty() {
459 ts
460 } else {
461 gen_error().into()
462 }
463 }
464
465 Err(boxed) => match boxed.downcast::<AbortNow>() {
466 Ok(_) => gen_error().into(),
467 Err(boxed) => resume_unwind(boxed),
468 },
469 }
470}
471
472fn abort_now() -> ! {
473 check_correctness();
474 std::panic::panic_any(AbortNow)
475}
476
477thread_local! {
478 static ENTERED_ENTRY_POINT: Cell<usize> = const { Cell::new(0) };
479}
480
481struct AbortNow;
482
483fn check_correctness() {
484 assert!(
485 ENTERED_ENTRY_POINT.with(Cell::get) != 0,
486 "proc-macro-error3 API cannot be used outside of `entry_point` invocation, \
487 perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]"
488 );
489}
490
491/// **ALL THE STUFF INSIDE IS NOT PUBLIC API!!!**
492#[doc(hidden)]
493pub mod __export {
494 // reexports for use in macros
495 pub use proc_macro;
496 pub use proc_macro2;
497
498 use proc_macro2::Span;
499 use quote::ToTokens;
500
501 use crate::SpanRange;
502
503 // inspired by
504 // https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md#simple-application
505
506 pub trait SpanAsSpanRange {
507 #[allow(non_snake_case)]
508 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
509 }
510
511 pub trait Span2AsSpanRange {
512 #[allow(non_snake_case)]
513 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
514 }
515
516 pub trait ToTokensAsSpanRange {
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 SpanRangeAsSpanRange {
522 #[allow(non_snake_case)]
523 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
524 }
525
526 impl<T: ToTokens> ToTokensAsSpanRange for &T {
527 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
528 let mut ts = self.to_token_stream().into_iter();
529 let first = match ts.next() {
530 Some(t) => t.span(),
531 None => Span::call_site(),
532 };
533
534 let last = match ts.last() {
535 Some(t) => t.span(),
536 None => first,
537 };
538
539 SpanRange { first, last }
540 }
541 }
542
543 impl Span2AsSpanRange for Span {
544 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
545 SpanRange {
546 first: *self,
547 last: *self,
548 }
549 }
550 }
551
552 impl SpanAsSpanRange for proc_macro::Span {
553 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
554 SpanRange {
555 first: (*self).into(),
556 last: (*self).into(),
557 }
558 }
559 }
560
561 impl SpanRangeAsSpanRange for SpanRange {
562 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
563 *self
564 }
565 }
566}