[][src]Module proc_macro_error::dummy

compile_error! does not interrupt 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:

This example is not tested
trait MyTrait {
    fn do_thing();
}

// this proc macro is supposed to generate MyTrait impl
#[proc_macro_derive(MyTrait)]
fn example(input: TokenStream) -> TokenStream {
    // somewhere deep inside
    span_error!(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:

error: set_dummy test
 --> $DIR/probe.rs:9:10
  |
9 |#[proc_macro_derive(MyTrait)]
  |                    ^^^^^^^

error[E0277]: the trait bound `Foo: Default` is not satisfied

  --> $DIR/probe.rs:14:10

   |
98 |     #[derive(MyTrait)]
   |              ^^^^^^^ the trait `Default` is not implemented for `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:

This example is not tested
 trait MyTrait {
     fn do_thing();
 }

 // this proc macro is supposed to generate MyTrait impl
 #[proc_macro_derive(MyTrait)]
 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
     proc_macro_error::set_dummy(Some(quote! {
         impl MyTrait for #name {
             fn do_thing() { unimplemented!() }
         }
     }));

     // somewhere deep inside
     span_error!(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!
 }

Functions

set_dummy

Sets dummy token stream which will be appended to compile_error!(msg);... invocations, should a trigger happen. Returns an old dummy, if set.