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 278extern crate proc_macro; 279 280pub use crate::{ 281 diagnostic::{Diagnostic, DiagnosticExt, Level}, 282 dummy::{append_dummy, set_dummy}, 283}; 284pub use proc_macro_error_attr::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(use_fallback)] 299#[path = "imp/fallback.rs"] 300mod imp; 301 302#[cfg(not(use_fallback))] 303#[path = "imp/delegate.rs"] 304mod imp; 305 306#[derive(Debug, Clone, Copy)] 307pub struct SpanRange { 308 pub first: Span, 309 pub last: Span, 310} 311 312impl SpanRange { 313 /// Create a range with the `first` and `last` spans being the same. 314 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. 322 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. 334 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`. 344 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. 352 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. 358pub 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!`. 363 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`. 368 fn expect_or_abort(self, msg: &str) -> Self::Ok; 369} 370 371/// This traits expands `Option` with some handy shortcuts. 372pub 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 380 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). 386pub fn abort_if_dirty() { 387 imp::abort_if_dirty(); 388} 389 390impl<T, E: Into<Diagnostic>> ResultExt for Result<T, E> { 391 type Ok = T; 392 393 fn unwrap_or_abort(self) -> T { 394 match self { 395 Ok(res) => res, 396 Err(e) => e.into().abort(), 397 } 398 } 399 400 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 412impl<T> OptionExt for Option<T> { 413 type Some = T; 414 415 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)] 427pub fn entry_point<F>(f: F, proc_macro_hack: bool) -> proc_macro::TokenStream 428where 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 470fn abort_now() -> ! { 471 check_correctness(); 472 panic!(AbortNow) 473} 474 475thread_local! { 476 static ENTERED_ENTRY_POINT: Cell<usize> = Cell::new(0); 477} 478 479struct AbortNow; 480 481fn 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)] 492pub 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)] 507 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)] 512 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)] 517 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)] 522 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange; 523 } 524 525 impl<T: ToTokens> ToTokensAsSpanRange for &T { 526 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 { 538 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 { 547 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 { 556 fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange { 557 *self 558 } 559 } 560} 561