dtolnay / thiserror

derive(Error) for struct and enum error types

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

E0425 deriving Error for an enum declared in a macro

opened this issue · comments

On rustc 1.43.1 stable, the following code results in error[E0425]: cannot find value `_0` in this scope:

use thiserror::Error;

macro_rules! decl_error {
    ($($Variant:ident($Value:ident)),*) => {
        #[derive(Debug, Error)] // <- E0425 here
        pub enum Foo {
            $(
                #[error("{0:?}")]
                $Variant($Value),
            )*
        }
    };
}

decl_error!(
    Foo(u8),
    Bar(u16),
    Baz(u32)
);

// For comparison, normal usage does not result in errors.
#[derive(Debug, Error)]
pub enum Bar {
    #[error("{0:?}")]
    Foo(u8),
    #[error("{0:?}")]
    Bar(u16),
    #[error("{0:?}")]
    Baz(u32)
}

Curiously, the output of cargo-expand seems fine. Any possibility of a bug in rustc?

Output of cargo-expand

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
use thiserror::Error;
pub enum Foo {
    #[error("{0:?}")]
    Foo(u8),
    #[error("{0:?}")]
    Bar(u16),
    #[error("{0:?}")]
    Baz(u32),
}
#[automatically_derived]
#[allow(unused_qualifications)]
impl ::core::fmt::Debug for Foo {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match (&*self,) {
            (&Foo::Foo(ref __self_0),) => {
                let mut debug_trait_builder = f.debug_tuple("Foo");
                let _ = debug_trait_builder.field(&&(*__self_0));
                debug_trait_builder.finish()
            }
            (&Foo::Bar(ref __self_0),) => {
                let mut debug_trait_builder = f.debug_tuple("Bar");
                let _ = debug_trait_builder.field(&&(*__self_0));
                debug_trait_builder.finish()
            }
            (&Foo::Baz(ref __self_0),) => {
                let mut debug_trait_builder = f.debug_tuple("Baz");
                let _ = debug_trait_builder.field(&&(*__self_0));
                debug_trait_builder.finish()
            }
        }
    }
}
impl std::error::Error for Foo {}
impl std::fmt::Display for Foo {
    fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        #[allow(unused_variables, deprecated)]
        match self {
            Foo::Foo(_0) => __formatter.write_fmt(::core::fmt::Arguments::new_v1(
                &[""],
                &match (&_0,) {
                    (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
                },
            )),
            Foo::Bar(_0) => __formatter.write_fmt(::core::fmt::Arguments::new_v1(
                &[""],
                &match (&_0,) {
                    (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
                },
            )),
            Foo::Baz(_0) => __formatter.write_fmt(::core::fmt::Arguments::new_v1(
                &[""],
                &match (&_0,) {
                    (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
                },
            )),
        }
    }
}
pub enum Bar {
    #[error("{0:?}")]
    Foo(u8),
    #[error("{0:?}")]
    Bar(u16),
    #[error("{0:?}")]
    Baz(u32),
}
#[automatically_derived]
#[allow(unused_qualifications)]
impl ::core::fmt::Debug for Bar {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match (&*self,) {
            (&Bar::Foo(ref __self_0),) => {
                let mut debug_trait_builder = f.debug_tuple("Foo");
                let _ = debug_trait_builder.field(&&(*__self_0));
                debug_trait_builder.finish()
            }
            (&Bar::Bar(ref __self_0),) => {
                let mut debug_trait_builder = f.debug_tuple("Bar");
                let _ = debug_trait_builder.field(&&(*__self_0));
                debug_trait_builder.finish()
            }
            (&Bar::Baz(ref __self_0),) => {
                let mut debug_trait_builder = f.debug_tuple("Baz");
                let _ = debug_trait_builder.field(&&(*__self_0));
                debug_trait_builder.finish()
            }
        }
    }
}
impl std::error::Error for Bar {}
impl std::fmt::Display for Bar {
    fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        #[allow(unused_variables, deprecated)]
        match self {
            Bar::Foo(_0) => __formatter.write_fmt(::core::fmt::Arguments::new_v1(
                &[""],
                &match (&_0,) {
                    (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
                },
            )),
            Bar::Bar(_0) => __formatter.write_fmt(::core::fmt::Arguments::new_v1(
                &[""],
                &match (&_0,) {
                    (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
                },
            )),
            Bar::Baz(_0) => __formatter.write_fmt(::core::fmt::Arguments::new_v1(
                &[""],
                &match (&_0,) {
                    (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
                },
            )),
        }
    }
}

Thanks for the nice repro. You are right that this is due to a compiler bug (rust-lang/rust#43081) but I've published a workaround in thiserror 1.0.18.

Thanks for the information and swift workaround!