1ea88969fSopenharmony_ci//! Facility to emit dummy implementations (or whatever) in case
2ea88969fSopenharmony_ci//! an error happen.
3ea88969fSopenharmony_ci//!
4ea88969fSopenharmony_ci//! `compile_error!` does not abort a compilation right away. This means
5ea88969fSopenharmony_ci//! `rustc` doesn't just show you the error and abort, it carries on the
6ea88969fSopenharmony_ci//! compilation process looking for other errors to report.
7ea88969fSopenharmony_ci//!
8ea88969fSopenharmony_ci//! Let's consider an example:
9ea88969fSopenharmony_ci//!
10ea88969fSopenharmony_ci//! ```rust,ignore
11ea88969fSopenharmony_ci//! use proc_macro::TokenStream;
12ea88969fSopenharmony_ci//! use proc_macro_error::*;
13ea88969fSopenharmony_ci//!
14ea88969fSopenharmony_ci//! trait MyTrait {
15ea88969fSopenharmony_ci//!     fn do_thing();
16ea88969fSopenharmony_ci//! }
17ea88969fSopenharmony_ci//!
18ea88969fSopenharmony_ci//! // this proc macro is supposed to generate MyTrait impl
19ea88969fSopenharmony_ci//! #[proc_macro_derive(MyTrait)]
20ea88969fSopenharmony_ci//! #[proc_macro_error]
21ea88969fSopenharmony_ci//! fn example(input: TokenStream) -> TokenStream {
22ea88969fSopenharmony_ci//!     // somewhere deep inside
23ea88969fSopenharmony_ci//!     abort!(span, "something's wrong");
24ea88969fSopenharmony_ci//!
25ea88969fSopenharmony_ci//!     // this implementation will be generated if no error happened
26ea88969fSopenharmony_ci//!     quote! {
27ea88969fSopenharmony_ci//!         impl MyTrait for #name {
28ea88969fSopenharmony_ci//!             fn do_thing() {/* whatever */}
29ea88969fSopenharmony_ci//!         }
30ea88969fSopenharmony_ci//!     }
31ea88969fSopenharmony_ci//! }
32ea88969fSopenharmony_ci//!
33ea88969fSopenharmony_ci//! // ================
34ea88969fSopenharmony_ci//! // in main.rs
35ea88969fSopenharmony_ci//!
36ea88969fSopenharmony_ci//! // this derive triggers an error
37ea88969fSopenharmony_ci//! #[derive(MyTrait)] // first BOOM!
38ea88969fSopenharmony_ci//! struct Foo;
39ea88969fSopenharmony_ci//!
40ea88969fSopenharmony_ci//! fn main() {
41ea88969fSopenharmony_ci//!     Foo::do_thing(); // second BOOM!
42ea88969fSopenharmony_ci//! }
43ea88969fSopenharmony_ci//! ```
44ea88969fSopenharmony_ci//!
45ea88969fSopenharmony_ci//! The problem is: the generated token stream contains only `compile_error!`
46ea88969fSopenharmony_ci//! invocation, the impl was not generated. That means user will see two compilation
47ea88969fSopenharmony_ci//! errors:
48ea88969fSopenharmony_ci//!
49ea88969fSopenharmony_ci//! ```text
50ea88969fSopenharmony_ci//! error: something's wrong
51ea88969fSopenharmony_ci//!  --> $DIR/probe.rs:9:10
52ea88969fSopenharmony_ci//!   |
53ea88969fSopenharmony_ci//! 9 |#[proc_macro_derive(MyTrait)]
54ea88969fSopenharmony_ci//!   |                    ^^^^^^^
55ea88969fSopenharmony_ci//!
56ea88969fSopenharmony_ci//! error[E0599]: no function or associated item named `do_thing` found for type `Foo` in the current scope
57ea88969fSopenharmony_ci//!  --> src\main.rs:3:10
58ea88969fSopenharmony_ci//!   |
59ea88969fSopenharmony_ci//! 1 | struct Foo;
60ea88969fSopenharmony_ci//!   | ----------- function or associated item `do_thing` not found for this
61ea88969fSopenharmony_ci//! 2 | fn main() {
62ea88969fSopenharmony_ci//! 3 |     Foo::do_thing(); // second BOOM!
63ea88969fSopenharmony_ci//!   |          ^^^^^^^^ function or associated item not found in `Foo`
64ea88969fSopenharmony_ci//! ```
65ea88969fSopenharmony_ci//!
66ea88969fSopenharmony_ci//! But the second error is meaningless! We definitely need to fix this.
67ea88969fSopenharmony_ci//!
68ea88969fSopenharmony_ci//! Most used approach in cases like this is "dummy implementation" -
69ea88969fSopenharmony_ci//! omit `impl MyTrait for #name` and fill functions bodies with `unimplemented!()`.
70ea88969fSopenharmony_ci//!
71ea88969fSopenharmony_ci//! This is how you do it:
72ea88969fSopenharmony_ci//!
73ea88969fSopenharmony_ci//! ```rust,ignore
74ea88969fSopenharmony_ci//! use proc_macro::TokenStream;
75ea88969fSopenharmony_ci//! use proc_macro_error::*;
76ea88969fSopenharmony_ci//!
77ea88969fSopenharmony_ci//!  trait MyTrait {
78ea88969fSopenharmony_ci//!      fn do_thing();
79ea88969fSopenharmony_ci//!  }
80ea88969fSopenharmony_ci//!
81ea88969fSopenharmony_ci//!  // this proc macro is supposed to generate MyTrait impl
82ea88969fSopenharmony_ci//!  #[proc_macro_derive(MyTrait)]
83ea88969fSopenharmony_ci//!  #[proc_macro_error]
84ea88969fSopenharmony_ci//!  fn example(input: TokenStream) -> TokenStream {
85ea88969fSopenharmony_ci//!      // first of all - we set a dummy impl which will be appended to
86ea88969fSopenharmony_ci//!      // `compile_error!` invocations in case a trigger does happen
87ea88969fSopenharmony_ci//!      set_dummy(quote! {
88ea88969fSopenharmony_ci//!          impl MyTrait for #name {
89ea88969fSopenharmony_ci//!              fn do_thing() { unimplemented!() }
90ea88969fSopenharmony_ci//!          }
91ea88969fSopenharmony_ci//!      });
92ea88969fSopenharmony_ci//!
93ea88969fSopenharmony_ci//!      // somewhere deep inside
94ea88969fSopenharmony_ci//!      abort!(span, "something's wrong");
95ea88969fSopenharmony_ci//!
96ea88969fSopenharmony_ci//!      // this implementation will be generated if no error happened
97ea88969fSopenharmony_ci//!      quote! {
98ea88969fSopenharmony_ci//!          impl MyTrait for #name {
99ea88969fSopenharmony_ci//!              fn do_thing() {/* whatever */}
100ea88969fSopenharmony_ci//!          }
101ea88969fSopenharmony_ci//!      }
102ea88969fSopenharmony_ci//!  }
103ea88969fSopenharmony_ci//!
104ea88969fSopenharmony_ci//!  // ================
105ea88969fSopenharmony_ci//!  // in main.rs
106ea88969fSopenharmony_ci//!
107ea88969fSopenharmony_ci//!  // this derive triggers an error
108ea88969fSopenharmony_ci//!  #[derive(MyTrait)] // first BOOM!
109ea88969fSopenharmony_ci//!  struct Foo;
110ea88969fSopenharmony_ci//!
111ea88969fSopenharmony_ci//!  fn main() {
112ea88969fSopenharmony_ci//!      Foo::do_thing(); // no more errors!
113ea88969fSopenharmony_ci//!  }
114ea88969fSopenharmony_ci//! ```
115ea88969fSopenharmony_ci
116ea88969fSopenharmony_ciuse proc_macro2::TokenStream;
117ea88969fSopenharmony_ciuse std::cell::RefCell;
118ea88969fSopenharmony_ci
119ea88969fSopenharmony_ciuse crate::check_correctness;
120ea88969fSopenharmony_ci
121ea88969fSopenharmony_cithread_local! {
122ea88969fSopenharmony_ci    static DUMMY_IMPL: RefCell<Option<TokenStream>> = RefCell::new(None);
123ea88969fSopenharmony_ci}
124ea88969fSopenharmony_ci
125ea88969fSopenharmony_ci/// Sets dummy token stream which will be appended to `compile_error!(msg);...`
126ea88969fSopenharmony_ci/// invocations in case you'll emit any errors.
127ea88969fSopenharmony_ci///
128ea88969fSopenharmony_ci/// See [guide](../index.html#guide).
129ea88969fSopenharmony_cipub fn set_dummy(dummy: TokenStream) -> Option<TokenStream> {
130ea88969fSopenharmony_ci    check_correctness();
131ea88969fSopenharmony_ci    DUMMY_IMPL.with(|old_dummy| old_dummy.replace(Some(dummy)))
132ea88969fSopenharmony_ci}
133ea88969fSopenharmony_ci
134ea88969fSopenharmony_ci/// Same as [`set_dummy`] but, instead of resetting, appends tokens to the
135ea88969fSopenharmony_ci/// existing dummy (if any). Behaves as `set_dummy` if no dummy is present.
136ea88969fSopenharmony_cipub fn append_dummy(dummy: TokenStream) {
137ea88969fSopenharmony_ci    check_correctness();
138ea88969fSopenharmony_ci    DUMMY_IMPL.with(|old_dummy| {
139ea88969fSopenharmony_ci        let mut cell = old_dummy.borrow_mut();
140ea88969fSopenharmony_ci        if let Some(ts) = cell.as_mut() {
141ea88969fSopenharmony_ci            ts.extend(dummy);
142ea88969fSopenharmony_ci        } else {
143ea88969fSopenharmony_ci            *cell = Some(dummy);
144ea88969fSopenharmony_ci        }
145ea88969fSopenharmony_ci    });
146ea88969fSopenharmony_ci}
147ea88969fSopenharmony_ci
148ea88969fSopenharmony_cipub(crate) fn cleanup() -> Option<TokenStream> {
149ea88969fSopenharmony_ci    DUMMY_IMPL.with(|old_dummy| old_dummy.replace(None))
150ea88969fSopenharmony_ci}
151