| diagnostic.rs |
|
11112 |
| dummy.rs |
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_error2::*;
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_error2::*;
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!
}
``` |
4392 |
| imp |
|
|
| lib.rs |
|
19156 |
| macros.rs |
|
9076 |
| sealed.rs |
|
58 |