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 116use proc_macro2::TokenStream; 117use std::cell::RefCell; 118 119use crate::check_correctness; 120 121thread_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). 129pub fn set_dummy(dummy: TokenStream) -> Option<TokenStream> { 130 check_correctness(); 131 DUMMY_IMPL.with(|old_dummy| 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. 136pub fn append_dummy(dummy: TokenStream) { 137 check_correctness(); 138 DUMMY_IMPL.with(|old_dummy| { 139 let mut cell = old_dummy.borrow_mut(); 140 if let Some(ts) = cell.as_mut() { 141 ts.extend(dummy); 142 } else { 143 *cell = Some(dummy); 144 } 145 }); 146} 147 148pub(crate) fn cleanup() -> Option<TokenStream> { 149 DUMMY_IMPL.with(|old_dummy| old_dummy.replace(None)) 150} 151