Skip to content

Commit

Permalink
Emit an Error impl even in the presence of bad attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Dec 15, 2023
1 parent 0444cd5 commit 7e5ff62
Show file tree
Hide file tree
Showing 22 changed files with 327 additions and 14 deletions.
28 changes: 26 additions & 2 deletions impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,39 @@ 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<TokenStream> {
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<TokenStream> {
let input = Input::from_syn(input)?;
input.validate()?;
Ok(match input {
Input::Struct(input) => impl_struct(input),
Input::Enum(input) => impl_enum(input),
})
}

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();
Expand Down
4 changes: 1 addition & 3 deletions impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
14 changes: 14 additions & 0 deletions tests/ui/bad-field-attr.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/concat-display.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/duplicate-enum-source.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/duplicate-fmt.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/duplicate-struct-source.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/duplicate-transparent.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/from-backtrace-backtrace.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/from-not-source.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
4 changes: 2 additions & 2 deletions tests/ui/invalid-input-impl-anyway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
20 changes: 13 additions & 7 deletions tests/ui/invalid-input-impl-anyway.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
28 changes: 28 additions & 0 deletions tests/ui/lifetime.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/missing-fmt.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/transparent-display.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/transparent-enum-many.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/transparent-enum-source.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/transparent-struct-many.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/transparent-struct-source.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
14 changes: 14 additions & 0 deletions tests/ui/unexpected-field-fmt.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Loading

0 comments on commit 7e5ff62

Please sign in to comment.