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