Skip to content

Commit a49f7c6

Browse files
committed
Change span of as_dyn_error() to point compile error at attribute.
1 parent f4eac7e commit a49f7c6

14 files changed

+209
-8
lines changed

impl/src/expand.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ fn impl_struct(input: Struct) -> TokenStream {
2323
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
2424
let mut error_inferred_bounds = InferredBounds::new();
2525

26-
let source_body = if input.attrs.transparent.is_some() {
26+
let source_body = if let Some(transparent_attr) = &input.attrs.transparent {
2727
let only_field = &input.fields[0];
2828
if only_field.contains_generic {
2929
error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error));
3030
}
3131
let member = &only_field.member;
32-
Some(quote! {
33-
std::error::Error::source(self.#member.as_dyn_error())
32+
Some(quote_spanned! {
33+
transparent_attr.span => std::error::Error::source(self.#member.as_dyn_error())
3434
})
3535
} else if let Some(source_field) = input.source_field() {
3636
let source = &source_field.member;
@@ -43,7 +43,8 @@ fn impl_struct(input: Struct) -> TokenStream {
4343
} else {
4444
None
4545
};
46-
let dyn_error = quote_spanned!(source.span()=> self.#source #asref.as_dyn_error());
46+
let dyn_error =
47+
quote_spanned!(source_field.source_span() => self.#source #asref.as_dyn_error());
4748
Some(quote! {
4849
::core::option::Option::Some(#dyn_error)
4950
})
@@ -193,13 +194,13 @@ fn impl_enum(input: Enum) -> TokenStream {
193194
let source_method = if input.has_source() {
194195
let arms = input.variants.iter().map(|variant| {
195196
let ident = &variant.ident;
196-
if variant.attrs.transparent.is_some() {
197+
if let Some(transparent_attr) = &variant.attrs.transparent {
197198
let only_field = &variant.fields[0];
198199
if only_field.contains_generic {
199200
error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error));
200201
}
201202
let member = &only_field.member;
202-
let source = quote!(std::error::Error::source(transparent.as_dyn_error()));
203+
let source = quote_spanned!(transparent_attr.span => std::error::Error::source(transparent.as_dyn_error()));
203204
quote! {
204205
#ty::#ident {#member: transparent} => #source,
205206
}
@@ -215,7 +216,7 @@ fn impl_enum(input: Enum) -> TokenStream {
215216
None
216217
};
217218
let varsource = quote!(source);
218-
let dyn_error = quote_spanned!(source.span()=> #varsource #asref.as_dyn_error());
219+
let dyn_error = quote_spanned!(source_field.source_span()=> #varsource #asref.as_dyn_error());
219220
quote! {
220221
#ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error),
221222
}

impl/src/prop.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::ast::{Enum, Field, Struct, Variant};
2-
use syn::{Member, Type};
2+
use proc_macro2::Span;
3+
use syn::{spanned::Spanned, Member, Type};
34

45
impl Struct<'_> {
56
pub(crate) fn from_field(&self) -> Option<&Field> {
@@ -70,6 +71,16 @@ impl Field<'_> {
7071
pub(crate) fn is_backtrace(&self) -> bool {
7172
type_is_backtrace(self.ty)
7273
}
74+
75+
pub(crate) fn source_span(&self) -> Span {
76+
if let Some(source_attr) = &self.attrs.source {
77+
source_attr.path().span()
78+
} else if let Some(from_attr) = &self.attrs.from {
79+
from_attr.path().span()
80+
} else {
81+
self.member.span()
82+
}
83+
}
7384
}
7485

7586
fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use thiserror::Error;
2+
3+
#[derive(Debug)]
4+
pub struct NotError;
5+
6+
#[derive(Error, Debug)]
7+
#[error("...")]
8+
pub enum ErrorEnum {
9+
Broken(#[source] NotError),
10+
}
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but its trait bounds were not satisfied
2+
--> tests/ui/source-enum-unnamed-field-not-error.rs:9:14
3+
|
4+
4 | pub struct NotError;
5+
| -------------------
6+
| |
7+
| doesn't satisfy `NotError: AsDynError<'_>`
8+
| doesn't satisfy `NotError: std::error::Error`
9+
...
10+
9 | Broken(#[source] NotError),
11+
| ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds
12+
|
13+
= note: the following trait bounds were not satisfied:
14+
`NotError: std::error::Error`
15+
which is required by `NotError: AsDynError<'_>`
16+
`&NotError: std::error::Error`
17+
which is required by `&NotError: AsDynError<'_>`
18+
note: the trait `std::error::Error` must be implemented
19+
--> $RUST/core/src/error.rs
20+
|
21+
| pub trait Error: Debug + Display {
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use thiserror::Error;
2+
3+
#[derive(Debug)]
4+
struct NotError;
5+
6+
#[derive(Error, Debug)]
7+
#[error("...")]
8+
pub struct ErrorStruct(#[source] NotError);
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its trait bounds were not satisfied
2+
--> tests/ui/source-struct-unnamed-field-not-error.rs:8:26
3+
|
4+
4 | struct NotError;
5+
| ---------------
6+
| |
7+
| method `as_dyn_error` not found for this struct
8+
| doesn't satisfy `NotError: AsDynError<'_>`
9+
| doesn't satisfy `NotError: std::error::Error`
10+
...
11+
8 | pub struct ErrorStruct(#[source] NotError);
12+
| ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds
13+
|
14+
= note: the following trait bounds were not satisfied:
15+
`NotError: std::error::Error`
16+
which is required by `NotError: AsDynError<'_>`
17+
note: the trait `std::error::Error` must be implemented
18+
--> $RUST/core/src/error.rs
19+
|
20+
| pub trait Error: Debug + Display {
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use thiserror::Error;
2+
3+
#[derive(Error, Debug)]
4+
pub enum Error {
5+
#[error(transparent)]
6+
Other {
7+
message: String,
8+
}
9+
}
10+
11+
fn main() {}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its trait bounds were not satisfied
2+
--> tests/ui/transparent-enum-not-error.rs:5:13
3+
|
4+
5 | #[error(transparent)]
5+
| ^^^^^^^^^^^ method cannot be called on `&String` due to unsatisfied trait bounds
6+
|
7+
::: $RUST/alloc/src/string.rs
8+
|
9+
| pub struct String {
10+
| -----------------
11+
| |
12+
| doesn't satisfy `String: AsDynError<'_>`
13+
| doesn't satisfy `String: std::error::Error`
14+
|
15+
= note: the following trait bounds were not satisfied:
16+
`String: std::error::Error`
17+
which is required by `String: AsDynError<'_>`
18+
`&String: std::error::Error`
19+
which is required by `&String: AsDynError<'_>`
20+
`str: Sized`
21+
which is required by `str: AsDynError<'_>`
22+
`str: std::error::Error`
23+
which is required by `str: AsDynError<'_>`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use thiserror::Error;
2+
3+
#[derive(Error, Debug)]
4+
pub enum Error {
5+
#[error(transparent)]
6+
Other(String),
7+
}
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its trait bounds were not satisfied
2+
--> tests/ui/transparent-enum-unnamed-field-not-error.rs:5:13
3+
|
4+
5 | #[error(transparent)]
5+
| ^^^^^^^^^^^ method cannot be called on `&String` due to unsatisfied trait bounds
6+
|
7+
::: $RUST/alloc/src/string.rs
8+
|
9+
| pub struct String {
10+
| -----------------
11+
| |
12+
| doesn't satisfy `String: AsDynError<'_>`
13+
| doesn't satisfy `String: std::error::Error`
14+
|
15+
= note: the following trait bounds were not satisfied:
16+
`String: std::error::Error`
17+
which is required by `String: AsDynError<'_>`
18+
`&String: std::error::Error`
19+
which is required by `&String: AsDynError<'_>`
20+
`str: Sized`
21+
which is required by `str: AsDynError<'_>`
22+
`str: std::error::Error`
23+
which is required by `str: AsDynError<'_>`
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use thiserror::Error;
2+
3+
#[derive(Error, Debug)]
4+
#[error(transparent)]
5+
pub struct Error {
6+
message: String,
7+
}
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trait bounds were not satisfied
2+
--> tests/ui/transparent-struct-not-error.rs:4:9
3+
|
4+
4 | #[error(transparent)]
5+
| ^^^^^^^^^^^ method cannot be called on `String` due to unsatisfied trait bounds
6+
|
7+
::: $RUST/alloc/src/string.rs
8+
|
9+
| pub struct String {
10+
| -----------------
11+
| |
12+
| doesn't satisfy `String: AsDynError<'_>`
13+
| doesn't satisfy `String: std::error::Error`
14+
|
15+
= note: the following trait bounds were not satisfied:
16+
`String: std::error::Error`
17+
which is required by `String: AsDynError<'_>`
18+
`str: Sized`
19+
which is required by `str: AsDynError<'_>`
20+
`str: std::error::Error`
21+
which is required by `str: AsDynError<'_>`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use thiserror::Error;
2+
3+
#[derive(Error, Debug)]
4+
#[error(transparent)]
5+
pub struct Error(String);
6+
7+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trait bounds were not satisfied
2+
--> tests/ui/transparent-struct-unnamed-field-not-error.rs:4:9
3+
|
4+
4 | #[error(transparent)]
5+
| ^^^^^^^^^^^ method cannot be called on `String` due to unsatisfied trait bounds
6+
|
7+
::: $RUST/alloc/src/string.rs
8+
|
9+
| pub struct String {
10+
| -----------------
11+
| |
12+
| doesn't satisfy `String: AsDynError<'_>`
13+
| doesn't satisfy `String: std::error::Error`
14+
|
15+
= note: the following trait bounds were not satisfied:
16+
`String: std::error::Error`
17+
which is required by `String: AsDynError<'_>`
18+
`str: Sized`
19+
which is required by `str: AsDynError<'_>`
20+
`str: std::error::Error`
21+
which is required by `str: AsDynError<'_>`

0 commit comments

Comments
 (0)