A complete working custom derive that illustrates the hygiene properties of Macros 2.0. Currently requires a nightly Rust compiler >=1.24.0-nightly but we are working to stabilize all of the APIs used here.
This is the same example as the unhygienic stable heapsize
example. Please read that other example first before diving into this one, as
the comments here assume an understanding of how the other one works.
A major advantage of spans and the token-based procedural macro API is that we
get great control over where the compiler's error messages are displayed.
Consider the error the user sees if one of their field types does not implement
HeapSize
.
#[derive(HeapSize)]
struct Broken {
ok: String,
bad: std::thread::Thread,
}
The Macros 1.1 string-based procedural macro and a naively implemented token-based procedural macro result in the following error.
error[E0599]: no method named `heap_size_of_children` found for type `std::thread::Thread` in the current scope
--> src/main.rs:4:10
|
4 | #[derive(HeapSize)]
| ^^^^^^^^
With just a bit of work, as shown in the heapsize_derive
implementation here,
we can improve this error to point out exactly which field is not right.
error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied
--> src/main.rs:7:5
|
7 | bad: std::thread::Thread,
| ^^^ the trait `HeapSize` is not implemented for `std::thread::Thread`
Some unstable APIs in the proc-macro2
crate let us improve this further by
joining together the span of the field name and the field type. There is no
difference in our code -- everything is as shown in this directory -- but
building the example crate with cargo build
shows errors like the one above
and building with RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build
is
able to show errors like the following.
error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied
--> src/main.rs:7:5
|
7 | bad: std::thread::Thread,
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HeapSize` is not implemented for `std::thread::Thread`