blob: 8a3dd1cdaed7d971b5a17e155e572cba8ca3d423 [file] [log] [blame]
//! Facility to emit dummy implementations (or whatever) in case
//! an error happen.
//!
//! `compile_error!` does not abort a compilation right away. This means
//! `rustc` doesn't just show you the error and abort, it carries on the
//! compilation process looking for other errors to report.
//!
//! Let's consider an example:
//!
//! ```rust,ignore
//! use proc_macro::TokenStream;
//! use proc_macro_error::*;
//!
//! trait MyTrait {
//! fn do_thing();
//! }
//!
//! // this proc macro is supposed to generate MyTrait impl
//! #[proc_macro_derive(MyTrait)]
//! #[proc_macro_error]
//! fn example(input: TokenStream) -> TokenStream {
//! // somewhere deep inside
//! abort!(span, "something's wrong");
//!
//! // this implementation will be generated if no error happened
//! quote! {
//! impl MyTrait for #name {
//! fn do_thing() {/* whatever */}
//! }
//! }
//! }
//!
//! // ================
//! // in main.rs
//!
//! // this derive triggers an error
//! #[derive(MyTrait)] // first BOOM!
//! struct Foo;
//!
//! fn main() {
//! Foo::do_thing(); // second BOOM!
//! }
//! ```
//!
//! The problem is: the generated token stream contains only `compile_error!`
//! invocation, the impl was not generated. That means user will see two compilation
//! errors:
//!
//! ```text
//! error: something's wrong
//! --> $DIR/probe.rs:9:10
//! |
//! 9 |#[proc_macro_derive(MyTrait)]
//! | ^^^^^^^
//!
//! error[E0599]: no function or associated item named `do_thing` found for type `Foo` in the current scope
//! --> src\main.rs:3:10
//! |
//! 1 | struct Foo;
//! | ----------- function or associated item `do_thing` not found for this
//! 2 | fn main() {
//! 3 | Foo::do_thing(); // second BOOM!
//! | ^^^^^^^^ function or associated item not found in `Foo`
//! ```
//!
//! But the second error is meaningless! We definitely need to fix this.
//!
//! Most used approach in cases like this is "dummy implementation" -
//! omit `impl MyTrait for #name` and fill functions bodies with `unimplemented!()`.
//!
//! This is how you do it:
//!
//! ```rust,ignore
//! use proc_macro::TokenStream;
//! use proc_macro_error::*;
//!
//! trait MyTrait {
//! fn do_thing();
//! }
//!
//! // this proc macro is supposed to generate MyTrait impl
//! #[proc_macro_derive(MyTrait)]
//! #[proc_macro_error]
//! fn example(input: TokenStream) -> TokenStream {
//! // first of all - we set a dummy impl which will be appended to
//! // `compile_error!` invocations in case a trigger does happen
//! set_dummy(quote! {
//! impl MyTrait for #name {
//! fn do_thing() { unimplemented!() }
//! }
//! });
//!
//! // somewhere deep inside
//! abort!(span, "something's wrong");
//!
//! // this implementation will be generated if no error happened
//! quote! {
//! impl MyTrait for #name {
//! fn do_thing() {/* whatever */}
//! }
//! }
//! }
//!
//! // ================
//! // in main.rs
//!
//! // this derive triggers an error
//! #[derive(MyTrait)] // first BOOM!
//! struct Foo;
//!
//! fn main() {
//! Foo::do_thing(); // no more errors!
//! }
//! ```
use proc_macro2::TokenStream;
use std::cell::Cell;
use crate::check_correctness;
thread_local! {
static DUMMY_IMPL: Cell<Option<TokenStream>> = Cell::new(None);
}
/// Sets dummy token stream which will be appended to `compile_error!(msg);...`
/// invocations in case you'll emit any errors.
///
/// See [module level docs for usage example][self].
pub fn set_dummy(dummy: TokenStream) -> Option<TokenStream> {
check_correctness();
DUMMY_IMPL.with(|old_dummy| old_dummy.replace(Some(dummy)))
}
pub(crate) fn cleanup() -> Option<TokenStream> {
DUMMY_IMPL.with(|old_dummy| old_dummy.replace(None))
}