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