magiclen / educe

This crate offers procedural macros designed to facilitate the swift implementation of Rust's built-in traits.
MIT License
127 stars 11 forks source link

Name collision if an enum field is named `f` or `builder` #14

Closed ijackson closed 8 months ago

ijackson commented 8 months ago

One way to produce a bizarre error:

#[derive(Educe)]
#[educe(Debug)]
pub enum Enum {
    Variant { f: i32 },
}

Actual output:

error[E0599]: no method named `debug_struct` found for reference `&i32` in the current scope
 --> src/main.rs:3:10
  |
3 | #[derive(Educe)]
  |          ^^^^^ method not found in `&i32`

Expected output: it compiles.

The reason is that the expansion looks like this (excerpt from cargo expand):

    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Variant { f } => {
                let mut builder = f.debug_struct("Variant");
                builder.field("f", f);
                builder.finish()
            }
        }
    }

So f was rebound by the pattern.

IMO the correct way to fix this is to bind each field to a local variable with a prefix:

            Self::Variant { f: v_f } => {
                let mut builder = f.debug_struct("Variant");
                builder.field("f", v_f);

This used to work in educe 0.4

ijackson commented 8 months ago

A field named builder goes wrong too:

error[E0308]: mismatched types
 --> src/main.rs:6:15
  |
3 | #[derive(Educe)]
  |          ----- arguments to this method are incorrect
...
6 |     Variant { builder: i32 },
  |               ^^^^^^^ expected `&dyn Debug`, found `DebugStruct<'_, '_>`
  |
  = note: expected reference `&dyn Debug`
                found struct `DebugStruct<'_, '_>`

If DebugStruct implemented Debug this would compile and produce wrong output...

magiclen commented 8 months ago

Thanks for your report!

ijackson commented 8 months ago

YW. Another way to fix it that wouldn't involve pasting identifiers:

            v @ Self::Variant { .. } => {
                let mut builder = f.debug_struct("Variant");
                builder.field("f", &v.f);