Skip to content

Commit

Permalink
Merge pull request #258 from de-vri-es/as-dyn-error-span
Browse files Browse the repository at this point in the history
Change span of `as_dyn_error()` to point compile error at attribute.
  • Loading branch information
dtolnay committed Oct 19, 2023
2 parents f4eac7e + a49f7c6 commit 4850c6f
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 8 deletions.
15 changes: 8 additions & 7 deletions impl/src/expand.rs
Expand Up @@ -23,14 +23,14 @@ fn impl_struct(input: Struct) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut error_inferred_bounds = InferredBounds::new();

let source_body = if input.attrs.transparent.is_some() {
let source_body = if let Some(transparent_attr) = &input.attrs.transparent {
let only_field = &input.fields[0];
if only_field.contains_generic {
error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error));
}
let member = &only_field.member;
Some(quote! {
std::error::Error::source(self.#member.as_dyn_error())
Some(quote_spanned! {
transparent_attr.span => std::error::Error::source(self.#member.as_dyn_error())
})
} else if let Some(source_field) = input.source_field() {
let source = &source_field.member;
Expand All @@ -43,7 +43,8 @@ fn impl_struct(input: Struct) -> TokenStream {
} else {
None
};
let dyn_error = quote_spanned!(source.span()=> self.#source #asref.as_dyn_error());
let dyn_error =
quote_spanned!(source_field.source_span() => self.#source #asref.as_dyn_error());
Some(quote! {
::core::option::Option::Some(#dyn_error)
})
Expand Down Expand Up @@ -193,13 +194,13 @@ fn impl_enum(input: Enum) -> TokenStream {
let source_method = if input.has_source() {
let arms = input.variants.iter().map(|variant| {
let ident = &variant.ident;
if variant.attrs.transparent.is_some() {
if let Some(transparent_attr) = &variant.attrs.transparent {
let only_field = &variant.fields[0];
if only_field.contains_generic {
error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error));
}
let member = &only_field.member;
let source = quote!(std::error::Error::source(transparent.as_dyn_error()));
let source = quote_spanned!(transparent_attr.span => std::error::Error::source(transparent.as_dyn_error()));
quote! {
#ty::#ident {#member: transparent} => #source,
}
Expand All @@ -215,7 +216,7 @@ fn impl_enum(input: Enum) -> TokenStream {
None
};
let varsource = quote!(source);
let dyn_error = quote_spanned!(source.span()=> #varsource #asref.as_dyn_error());
let dyn_error = quote_spanned!(source_field.source_span()=> #varsource #asref.as_dyn_error());
quote! {
#ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error),
}
Expand Down
13 changes: 12 additions & 1 deletion impl/src/prop.rs
@@ -1,5 +1,6 @@
use crate::ast::{Enum, Field, Struct, Variant};
use syn::{Member, Type};
use proc_macro2::Span;
use syn::{spanned::Spanned, Member, Type};

impl Struct<'_> {
pub(crate) fn from_field(&self) -> Option<&Field> {
Expand Down Expand Up @@ -70,6 +71,16 @@ impl Field<'_> {
pub(crate) fn is_backtrace(&self) -> bool {
type_is_backtrace(self.ty)
}

pub(crate) fn source_span(&self) -> Span {
if let Some(source_attr) = &self.attrs.source {
source_attr.path().span()
} else if let Some(from_attr) = &self.attrs.from {
from_attr.path().span()
} else {
self.member.span()
}
}
}

fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/source-enum-unnamed-field-not-error.rs
@@ -0,0 +1,12 @@
use thiserror::Error;

#[derive(Debug)]
pub struct NotError;

#[derive(Error, Debug)]
#[error("...")]
pub enum ErrorEnum {
Broken(#[source] NotError),
}

fn main() {}
22 changes: 22 additions & 0 deletions tests/ui/source-enum-unnamed-field-not-error.stderr
@@ -0,0 +1,22 @@
error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but its trait bounds were not satisfied
--> tests/ui/source-enum-unnamed-field-not-error.rs:9:14
|
4 | pub struct NotError;
| -------------------
| |
| doesn't satisfy `NotError: AsDynError<'_>`
| doesn't satisfy `NotError: std::error::Error`
...
9 | Broken(#[source] NotError),
| ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`NotError: std::error::Error`
which is required by `NotError: AsDynError<'_>`
`&NotError: std::error::Error`
which is required by `&NotError: AsDynError<'_>`
note: the trait `std::error::Error` must be implemented
--> $RUST/core/src/error.rs
|
| pub trait Error: Debug + Display {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10 changes: 10 additions & 0 deletions tests/ui/source-struct-unnamed-field-not-error.rs
@@ -0,0 +1,10 @@
use thiserror::Error;

#[derive(Debug)]
struct NotError;

#[derive(Error, Debug)]
#[error("...")]
pub struct ErrorStruct(#[source] NotError);

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/source-struct-unnamed-field-not-error.stderr
@@ -0,0 +1,21 @@
error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its trait bounds were not satisfied
--> tests/ui/source-struct-unnamed-field-not-error.rs:8:26
|
4 | struct NotError;
| ---------------
| |
| method `as_dyn_error` not found for this struct
| doesn't satisfy `NotError: AsDynError<'_>`
| doesn't satisfy `NotError: std::error::Error`
...
8 | pub struct ErrorStruct(#[source] NotError);
| ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`NotError: std::error::Error`
which is required by `NotError: AsDynError<'_>`
note: the trait `std::error::Error` must be implemented
--> $RUST/core/src/error.rs
|
| pub trait Error: Debug + Display {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11 changes: 11 additions & 0 deletions tests/ui/transparent-enum-not-error.rs
@@ -0,0 +1,11 @@
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Other {
message: String,
}
}

fn main() {}
23 changes: 23 additions & 0 deletions tests/ui/transparent-enum-not-error.stderr
@@ -0,0 +1,23 @@
error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its trait bounds were not satisfied
--> tests/ui/transparent-enum-not-error.rs:5:13
|
5 | #[error(transparent)]
| ^^^^^^^^^^^ method cannot be called on `&String` due to unsatisfied trait bounds
|
::: $RUST/alloc/src/string.rs
|
| pub struct String {
| -----------------
| |
| doesn't satisfy `String: AsDynError<'_>`
| doesn't satisfy `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
which is required by `String: AsDynError<'_>`
`&String: std::error::Error`
which is required by `&String: AsDynError<'_>`
`str: Sized`
which is required by `str: AsDynError<'_>`
`str: std::error::Error`
which is required by `str: AsDynError<'_>`
9 changes: 9 additions & 0 deletions tests/ui/transparent-enum-unnamed-field-not-error.rs
@@ -0,0 +1,9 @@
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Other(String),
}

fn main() {}
23 changes: 23 additions & 0 deletions tests/ui/transparent-enum-unnamed-field-not-error.stderr
@@ -0,0 +1,23 @@
error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its trait bounds were not satisfied
--> tests/ui/transparent-enum-unnamed-field-not-error.rs:5:13
|
5 | #[error(transparent)]
| ^^^^^^^^^^^ method cannot be called on `&String` due to unsatisfied trait bounds
|
::: $RUST/alloc/src/string.rs
|
| pub struct String {
| -----------------
| |
| doesn't satisfy `String: AsDynError<'_>`
| doesn't satisfy `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
which is required by `String: AsDynError<'_>`
`&String: std::error::Error`
which is required by `&String: AsDynError<'_>`
`str: Sized`
which is required by `str: AsDynError<'_>`
`str: std::error::Error`
which is required by `str: AsDynError<'_>`
9 changes: 9 additions & 0 deletions tests/ui/transparent-struct-not-error.rs
@@ -0,0 +1,9 @@
use thiserror::Error;

#[derive(Error, Debug)]
#[error(transparent)]
pub struct Error {
message: String,
}

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/transparent-struct-not-error.stderr
@@ -0,0 +1,21 @@
error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trait bounds were not satisfied
--> tests/ui/transparent-struct-not-error.rs:4:9
|
4 | #[error(transparent)]
| ^^^^^^^^^^^ method cannot be called on `String` due to unsatisfied trait bounds
|
::: $RUST/alloc/src/string.rs
|
| pub struct String {
| -----------------
| |
| doesn't satisfy `String: AsDynError<'_>`
| doesn't satisfy `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
which is required by `String: AsDynError<'_>`
`str: Sized`
which is required by `str: AsDynError<'_>`
`str: std::error::Error`
which is required by `str: AsDynError<'_>`
7 changes: 7 additions & 0 deletions tests/ui/transparent-struct-unnamed-field-not-error.rs
@@ -0,0 +1,7 @@
use thiserror::Error;

#[derive(Error, Debug)]
#[error(transparent)]
pub struct Error(String);

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/transparent-struct-unnamed-field-not-error.stderr
@@ -0,0 +1,21 @@
error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trait bounds were not satisfied
--> tests/ui/transparent-struct-unnamed-field-not-error.rs:4:9
|
4 | #[error(transparent)]
| ^^^^^^^^^^^ method cannot be called on `String` due to unsatisfied trait bounds
|
::: $RUST/alloc/src/string.rs
|
| pub struct String {
| -----------------
| |
| doesn't satisfy `String: AsDynError<'_>`
| doesn't satisfy `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
which is required by `String: AsDynError<'_>`
`str: Sized`
which is required by `str: AsDynError<'_>`
`str: std::error::Error`
which is required by `str: AsDynError<'_>`

0 comments on commit 4850c6f

Please sign in to comment.