Skip to content

Commit

Permalink
Merge pull request #266 from dtolnay/fallback
Browse files Browse the repository at this point in the history
Emit an Error impl even in the presence of bad attributes
  • Loading branch information
dtolnay committed Dec 15, 2023
2 parents 0444cd5 + 1754825 commit 02c6a55
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 15 deletions.
40 changes: 38 additions & 2 deletions impl/src/expand.rs
Expand Up @@ -7,15 +7,51 @@ 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
where
// Work around trivial bounds being unstable.
// https://github.com/rust-lang/rust/issues/48214
for<'workaround> #ty #ty_generics: ::core::fmt::Debug,
{}

#[allow(unused_qualifications)]
impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause {
fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::unreachable!()
}
}
}
}

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
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()
}
4 changes: 2 additions & 2 deletions tests/ui/invalid-input-impl-anyway.rs
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;
}
8 changes: 0 additions & 8 deletions tests/ui/invalid-input-impl-anyway.stderr
Expand Up @@ -3,11 +3,3 @@ 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`

0 comments on commit 02c6a55

Please sign in to comment.