diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 2cf0967a..2183d4cc 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -7,8 +7,18 @@ use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::collections::BTreeSet as Set; use syn::{DeriveInput, GenericArgument, Member, PathArguments, Result, Token, Type}; -pub fn derive(node: &DeriveInput) -> Result { - let input = Input::from_syn(node)?; +pub fn derive(input: &DeriveInput) -> TokenStream { + match try_expand(input) { + Ok(expanded) => expanded, + // If there are invalid attributes in the input, expand to an Error impl + // anyway to minimize spurious knock-on errors in other code that uses + // this type as an Error. + Err(error) => fallback(input, error), + } +} + +fn try_expand(input: &DeriveInput) -> Result { + let input = Input::from_syn(input)?; input.validate()?; Ok(match input { Input::Struct(input) => impl_struct(input), @@ -16,6 +26,20 @@ pub fn derive(node: &DeriveInput) -> Result { }) } +fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream { + let ty = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let error = error.to_compile_error(); + + quote! { + #error + + #[allow(unused_qualifications)] + impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {} + } +} + fn impl_struct(input: Struct) -> TokenStream { let ty = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); diff --git a/impl/src/lib.rs b/impl/src/lib.rs index 39951789..b6471ff3 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -33,7 +33,5 @@ use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(Error, attributes(backtrace, error, from, source))] pub fn derive_error(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - expand::derive(&input) - .unwrap_or_else(|err| err.to_compile_error()) - .into() + expand::derive(&input).into() } diff --git a/tests/ui/bad-field-attr.stderr b/tests/ui/bad-field-attr.stderr index 5fb57441..2476eb5d 100644 --- a/tests/ui/bad-field-attr.stderr +++ b/tests/ui/bad-field-attr.stderr @@ -3,3 +3,17 @@ error: #[error(transparent)] needs to go outside the enum or struct, not on an i | 5 | pub struct Error(#[error(transparent)] std::io::Error); | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/bad-field-attr.rs:5:12 + | +5 | pub struct Error(#[error(transparent)] std::io::Error); + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/concat-display.stderr b/tests/ui/concat-display.stderr index dbecd69f..aa49a8c2 100644 --- a/tests/ui/concat-display.stderr +++ b/tests/ui/concat-display.stderr @@ -8,3 +8,17 @@ error: expected string literal | ------------------------- in this macro invocation | = note: this error originates in the macro `error_type` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/concat-display.rs:13:13 + | +13 | error_type!(Error, "foo"); + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/duplicate-enum-source.stderr b/tests/ui/duplicate-enum-source.stderr index 4a4b2d39..6a9fc6e0 100644 --- a/tests/ui/duplicate-enum-source.stderr +++ b/tests/ui/duplicate-enum-source.stderr @@ -3,3 +3,17 @@ error: duplicate #[source] attribute | 8 | #[source] | ^^^^^^^^^ + +error[E0277]: `ErrorEnum` doesn't implement `std::fmt::Display` + --> tests/ui/duplicate-enum-source.rs:4:10 + | +4 | pub enum ErrorEnum { + | ^^^^^^^^^ `ErrorEnum` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `ErrorEnum` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/duplicate-fmt.stderr b/tests/ui/duplicate-fmt.stderr index 532b16bd..dba5ed57 100644 --- a/tests/ui/duplicate-fmt.stderr +++ b/tests/ui/duplicate-fmt.stderr @@ -3,3 +3,17 @@ error: only one #[error(...)] attribute is allowed | 5 | #[error("...")] | ^^^^^^^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/duplicate-fmt.rs:6:12 + | +6 | pub struct Error; + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/duplicate-struct-source.stderr b/tests/ui/duplicate-struct-source.stderr index c8de5747..af9769f6 100644 --- a/tests/ui/duplicate-struct-source.stderr +++ b/tests/ui/duplicate-struct-source.stderr @@ -3,3 +3,17 @@ error: duplicate #[source] attribute | 7 | #[source] | ^^^^^^^^^ + +error[E0277]: `ErrorStruct` doesn't implement `std::fmt::Display` + --> tests/ui/duplicate-struct-source.rs:4:12 + | +4 | pub struct ErrorStruct { + | ^^^^^^^^^^^ `ErrorStruct` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `ErrorStruct` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/duplicate-transparent.stderr b/tests/ui/duplicate-transparent.stderr index a8308790..03bda29e 100644 --- a/tests/ui/duplicate-transparent.stderr +++ b/tests/ui/duplicate-transparent.stderr @@ -3,3 +3,17 @@ error: duplicate #[error(transparent)] attribute | 5 | #[error(transparent)] | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/duplicate-transparent.rs:6:12 + | +6 | pub struct Error(anyhow::Error); + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/from-backtrace-backtrace.stderr b/tests/ui/from-backtrace-backtrace.stderr index 5c0b9a3b..259aea53 100644 --- a/tests/ui/from-backtrace-backtrace.stderr +++ b/tests/ui/from-backtrace-backtrace.stderr @@ -3,3 +3,17 @@ error: deriving From requires no fields other than source and backtrace | 9 | #[from] | ^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/from-backtrace-backtrace.rs:8:12 + | +8 | pub struct Error( + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/from-not-source.stderr b/tests/ui/from-not-source.stderr index 97136017..92f58703 100644 --- a/tests/ui/from-not-source.stderr +++ b/tests/ui/from-not-source.stderr @@ -3,3 +3,17 @@ error: #[from] is only supported on the source field, not any other field | 7 | #[from] | ^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/from-not-source.rs:4:12 + | +4 | pub struct Error { + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/invalid-input-impl-anyway.rs b/tests/ui/invalid-input-impl-anyway.rs index f9a10d84..0a0bcbee 100644 --- a/tests/ui/invalid-input-impl-anyway.rs +++ b/tests/ui/invalid-input-impl-anyway.rs @@ -5,7 +5,7 @@ use thiserror::Error; pub struct MyError; fn main() { - // FIXME: there should be no error on the following line. Thiserror should - // emit an Error impl regardless of the bad attribute. + // No error on the following line. Thiserror emits an Error impl despite the + // bad attribute. _ = &MyError as &dyn std::error::Error; } diff --git a/tests/ui/invalid-input-impl-anyway.stderr b/tests/ui/invalid-input-impl-anyway.stderr index 297b1001..1b58b6c1 100644 --- a/tests/ui/invalid-input-impl-anyway.stderr +++ b/tests/ui/invalid-input-impl-anyway.stderr @@ -4,10 +4,16 @@ error: expected attribute arguments in parentheses: #[error(...)] 4 | #[error] | ^^^^^ -error[E0277]: the trait bound `MyError: std::error::Error` is not satisfied - --> tests/ui/invalid-input-impl-anyway.rs:10:9 - | -10 | _ = &MyError as &dyn std::error::Error; - | ^^^^^^^^ the trait `std::error::Error` is not implemented for `MyError` - | - = note: required for the cast from `&MyError` to `&dyn std::error::Error` +error[E0277]: `MyError` doesn't implement `std::fmt::Display` + --> tests/ui/invalid-input-impl-anyway.rs:5:12 + | +5 | pub struct MyError; + | ^^^^^^^ `MyError` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `MyError` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/lifetime.stderr b/tests/ui/lifetime.stderr index 8b58136e..b2d71380 100644 --- a/tests/ui/lifetime.stderr +++ b/tests/ui/lifetime.stderr @@ -9,3 +9,31 @@ error: non-static lifetimes are not allowed in the source of an error, because s | 15 | Foo(#[from] Generic<&'a str>), | ^^^^^^^^^^^^^^^^ + +error[E0277]: `Error<'a>` doesn't implement `std::fmt::Display` + --> tests/ui/lifetime.rs:6:8 + | +6 | struct Error<'a>(#[from] Inner<'a>); + | ^^^^^^^^^ `Error<'a>` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error<'a>` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` + +error[E0277]: `Enum<'a>` doesn't implement `std::fmt::Display` + --> tests/ui/lifetime.rs:13:6 + | +13 | enum Enum<'a> { + | ^^^^^^^^ `Enum<'a>` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Enum<'a>` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/missing-fmt.stderr b/tests/ui/missing-fmt.stderr index c0be3735..9f34ff1f 100644 --- a/tests/ui/missing-fmt.stderr +++ b/tests/ui/missing-fmt.stderr @@ -3,3 +3,17 @@ error: missing #[error("...")] display attribute | 7 | B(usize), | ^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/missing-fmt.rs:4:10 + | +4 | pub enum Error { + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/transparent-display.stderr b/tests/ui/transparent-display.stderr index 54d958b2..e35d7320 100644 --- a/tests/ui/transparent-display.stderr +++ b/tests/ui/transparent-display.stderr @@ -3,3 +3,17 @@ error: cannot have both #[error(transparent)] and a display attribute | 5 | #[error("...")] | ^^^^^^^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/transparent-display.rs:6:12 + | +6 | pub struct Error(anyhow::Error); + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/transparent-enum-many.stderr b/tests/ui/transparent-enum-many.stderr index a9adfa5a..849a8aba 100644 --- a/tests/ui/transparent-enum-many.stderr +++ b/tests/ui/transparent-enum-many.stderr @@ -4,3 +4,17 @@ error: #[error(transparent)] requires exactly one field 5 | / #[error(transparent)] 6 | | Other(anyhow::Error, String), | |________________________________^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/transparent-enum-many.rs:4:10 + | +4 | pub enum Error { + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/transparent-enum-source.stderr b/tests/ui/transparent-enum-source.stderr index ccb90677..fb33d127 100644 --- a/tests/ui/transparent-enum-source.stderr +++ b/tests/ui/transparent-enum-source.stderr @@ -3,3 +3,17 @@ error: transparent variant can't contain #[source] | 6 | Other(#[source] anyhow::Error), | ^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/transparent-enum-source.rs:4:10 + | +4 | pub enum Error { + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/transparent-struct-many.stderr b/tests/ui/transparent-struct-many.stderr index c0e3806e..add231a6 100644 --- a/tests/ui/transparent-struct-many.stderr +++ b/tests/ui/transparent-struct-many.stderr @@ -3,3 +3,17 @@ error: #[error(transparent)] requires exactly one field | 4 | #[error(transparent)] | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/transparent-struct-many.rs:5:12 + | +5 | pub struct Error { + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/transparent-struct-source.stderr b/tests/ui/transparent-struct-source.stderr index 3012ca31..54d8230e 100644 --- a/tests/ui/transparent-struct-source.stderr +++ b/tests/ui/transparent-struct-source.stderr @@ -3,3 +3,17 @@ error: transparent error struct can't contain #[source] | 5 | pub struct Error(#[source] anyhow::Error); | ^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/transparent-struct-source.rs:5:12 + | +5 | pub struct Error(#[source] anyhow::Error); + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/unexpected-field-fmt.stderr b/tests/ui/unexpected-field-fmt.stderr index bf3c24df..65d4ff9a 100644 --- a/tests/ui/unexpected-field-fmt.stderr +++ b/tests/ui/unexpected-field-fmt.stderr @@ -3,3 +3,17 @@ error: not expected here; the #[error(...)] attribute belongs on top of a struct | 6 | #[error("...")] | ^^^^^^^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/unexpected-field-fmt.rs:4:10 + | +4 | pub enum Error { + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/unexpected-struct-source.stderr b/tests/ui/unexpected-struct-source.stderr index 6f15841d..f075ed89 100644 --- a/tests/ui/unexpected-struct-source.stderr +++ b/tests/ui/unexpected-struct-source.stderr @@ -3,3 +3,17 @@ error: not expected here; the #[source] attribute belongs on a specific field | 4 | #[source] | ^^^^^^^^^ + +error[E0277]: `Error` doesn't implement `std::fmt::Display` + --> tests/ui/unexpected-struct-source.rs:5:12 + | +5 | pub struct Error; + | ^^^^^ `Error` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `Error` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` diff --git a/tests/ui/union.stderr b/tests/ui/union.stderr index 3ec4d71c..d8588ffa 100644 --- a/tests/ui/union.stderr +++ b/tests/ui/union.stderr @@ -6,3 +6,36 @@ error: union as errors are not supported 6 | | num: usize, 7 | | } | |_^ + +error[E0277]: `U` doesn't implement `std::fmt::Display` + --> tests/ui/union.rs:4:11 + | +4 | pub union U { + | ^ `U` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `U` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^^^ required by this bound in `Error` + +error[E0277]: `U` doesn't implement `Debug` + --> tests/ui/union.rs:4:11 + | +4 | pub union U { + | ^ `U` cannot be formatted using `{:?}` + | + = help: the trait `Debug` is not implemented for `U` + = note: add `#[derive(Debug)]` to `U` or manually `impl Debug for U` +note: required by a bound in `std::error::Error` + --> $RUST/core/src/error.rs + | + | pub trait Error: Debug + Display { + | ^^^^^ required by this bound in `Error` +help: consider annotating `U` with `#[derive(Debug)]` + | +4 + #[derive(Debug)] +5 | pub union U { + |