From 86993c25ab39d24a983eb76a208f51c502e17b90 Mon Sep 17 00:00:00 2001 From: Andrew Straw Date: Fri, 11 Jun 2021 22:17:47 +0200 Subject: [PATCH 01/11] allow #[from] and #[backtrace] on same field See https://github.com/dtolnay/thiserror/pull/93#pullrequestreview-666213667 --- impl/src/expand.rs | 79 ++++++++++++++++++++++++++++++---------------- impl/src/prop.rs | 28 ++++++++++++++++ 2 files changed, 80 insertions(+), 27 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index fa85cbb..ffc412e 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -58,18 +58,25 @@ fn impl_struct(input: Struct) -> TokenStream { self.#source.as_dyn_error().backtrace() } }; - let combinator = if type_is_option(backtrace_field.ty) { + if &source_field.member == backtrace { quote! { - #source_backtrace.or(self.#backtrace.as_ref()) + use thiserror::private::AsDynError; + #source_backtrace } } else { + let combinator = if type_is_option(backtrace_field.ty) { + quote! { + #source_backtrace.or(self.#backtrace.as_ref()) + } + } else { + quote! { + std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace)) + } + }; quote! { - std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace)) + use thiserror::private::AsDynError; + #combinator } - }; - quote! { - use thiserror::private::AsDynError; - #combinator } } else if type_is_option(backtrace_field.ty) { quote! { @@ -123,20 +130,21 @@ fn impl_struct(input: Struct) -> TokenStream { } }); - let from_impl = input.from_field().map(|from_field| { - let backtrace_field = input.backtrace_field(); - let from = from_field.ty; - let body = from_initializer(from_field, backtrace_field); - quote! { - #[allow(unused_qualifications)] - impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { - #[allow(deprecated)] - fn from(source: #from) -> Self { - #ty #body + let from_impl = input.from_and_distinct_backtrace_fields().map( + |(from_field, backtrace_field)| { + let from = from_field.ty; + let body = from_initializer(from_field, backtrace_field); + quote! { + #[allow(unused_qualifications)] + impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { + #[allow(deprecated)] + fn from(source: #from) -> Self { + #ty #body + } } } - } - }); + }, + ); let error_trait = spanned_error_trait(input.original); @@ -235,14 +243,32 @@ fn impl_enum(input: Enum) -> TokenStream { } } (Some(backtrace_field), _) => { + let source = variant.from_field().map(|f| &f.member); let backtrace = &backtrace_field.member; - let body = if type_is_option(backtrace_field.ty) { - quote!(backtrace.as_ref()) + if source == Some(backtrace) { + let varsource = quote!(source); + let source_backtrace = quote_spanned! {source.span()=> + #varsource.as_dyn_error().backtrace() + }; + + quote! { + #ty::#ident { + #source: #varsource, + .. + } => { + use thiserror::private::AsDynError; + #source_backtrace + } + } } else { - quote!(std::option::Option::Some(backtrace)) - }; - quote! { - #ty::#ident {#backtrace: backtrace, ..} => #body, + let body = if type_is_option(backtrace_field.ty) { + quote!(backtrace.as_ref()) + } else { + quote!(std::option::Option::Some(backtrace)) + }; + quote! { + #ty::#ident {#backtrace: backtrace, ..} => #body, + } } } (None, _) => quote! { @@ -315,8 +341,7 @@ fn impl_enum(input: Enum) -> TokenStream { }; let from_impls = input.variants.iter().filter_map(|variant| { - let from_field = variant.from_field()?; - let backtrace_field = variant.backtrace_field(); + let (from_field, backtrace_field) = variant.from_and_distinct_backtrace_fields()?; let variant = &variant.ident; let from = from_field.ty; let body = from_initializer(from_field, backtrace_field); diff --git a/impl/src/prop.rs b/impl/src/prop.rs index 059b74b..f80ca3d 100644 --- a/impl/src/prop.rs +++ b/impl/src/prop.rs @@ -6,6 +6,20 @@ impl Struct<'_> { from_field(&self.fields) } + pub(crate) fn from_and_distinct_backtrace_fields(&self) -> Option<(&Field, Option<&Field>)> { + self.from_field().map(|from_field| { + if let Some(backtrace_field) = self.backtrace_field() { + if backtrace_field.member == from_field.member { + (from_field, None) + } else { + (from_field, Some(backtrace_field)) + } + } else { + (from_field, None) + } + }) + } + pub(crate) fn source_field(&self) -> Option<&Field> { source_field(&self.fields) } @@ -47,6 +61,20 @@ impl Variant<'_> { from_field(&self.fields) } + pub(crate) fn from_and_distinct_backtrace_fields(&self) -> Option<(&Field, Option<&Field>)> { + self.from_field().map(|from_field| { + if let Some(backtrace_field) = self.backtrace_field() { + if backtrace_field.member == from_field.member { + (from_field, None) + } else { + (from_field, Some(backtrace_field)) + } + } else { + (from_field, None) + } + }) + } + pub(crate) fn source_field(&self) -> Option<&Field> { source_field(&self.fields) } From da2454f989faf8de31b6612a1fbd52e0c61adfee Mon Sep 17 00:00:00 2001 From: Andrew Straw Date: Sat, 12 Jun 2021 16:16:37 +0200 Subject: [PATCH 02/11] describe `#[from]` and `#[backtrace]` together --- README.md | 14 ++++++++++++++ src/lib.rs | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/README.md b/README.md index 76c436a..101a522 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,20 @@ pub enum DataStoreError { } ``` +- If a field is `#[from]` and `#[backtrace]`, the Error trait's `backtrace()` + method is forwarded to the `source`. + + ```rust + #[derive(Error, Debug)] + pub enum MyError { + Io { + #[from] + #[backtrace] + source: io::Error, + }, + } + ``` + - Errors may use `error(transparent)` to forward the source and Display methods straight through to an underlying error without adding an additional message. This would be appropriate for enums that need an "anything else" variant. diff --git a/src/lib.rs b/src/lib.rs index 02941c8..8884db3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,6 +161,22 @@ //! # }; //! ``` //! +//! - If a field is `#[from]` and `#[backtrace]`, the Error trait's `backtrace()` +//! method is forwarded to the field. +//! +//! ```rust +//! # const IGNORE: &str = stringify! { +//! #[derive(Error, Debug)] +//! pub enum MyError { +//! Io { +//! #[from] +//! #[backtrace] +//! source: io::Error, +//! }, +//! } +//! # }; +//! ``` +//! //! - Errors may use `error(transparent)` to forward the source and Display //! methods straight through to an underlying error without adding an //! additional message. This would be appropriate for enums that need an From 52dc286e79984bcadbaba399735250922219a56d Mon Sep 17 00:00:00 2001 From: Andrew Straw Date: Sat, 12 Jun 2021 17:03:49 +0200 Subject: [PATCH 03/11] test combined `#[from]` and `#[backtrace]` --- tests/test_backtrace.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 09bc13d..608b405 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -50,6 +50,14 @@ pub mod structs { backtrace: Backtrace, } + #[derive(Error, Debug)] + #[error("...")] + pub struct CombinedBacktraceFrom { + #[from] + #[backtrace] + source: Inner, + } + #[derive(Error, Debug)] #[error("...")] pub struct OptBacktraceFrom { @@ -93,6 +101,9 @@ pub mod structs { let error = BacktraceFrom::from(Inner); assert!(error.backtrace().is_some()); + let error = CombinedBacktraceFrom::from(Inner); + assert!(error.backtrace().is_some()); + let error = OptBacktraceFrom::from(Inner); assert!(error.backtrace().is_some()); @@ -153,6 +164,16 @@ pub mod enums { }, } + #[derive(Error, Debug)] + pub enum CombinedBacktraceFrom { + #[error("...")] + Test { + #[from] + #[backtrace] + source: Inner, + }, + } + #[derive(Error, Debug)] pub enum OptBacktraceFrom { #[error("...")] @@ -200,6 +221,9 @@ pub mod enums { let error = BacktraceFrom::from(Inner); assert!(error.backtrace().is_some()); + let error = CombinedBacktraceFrom::from(Inner); + assert!(error.backtrace().is_some()); + let error = OptBacktraceFrom::from(Inner); assert!(error.backtrace().is_some()); From cd2b9db3aa84733cb10d2bb3d3d6251ace9357da Mon Sep 17 00:00:00 2001 From: Andrew Straw Date: Sun, 13 Jun 2021 09:44:18 +0200 Subject: [PATCH 04/11] fixup tests for combined from and backtrace --- tests/test_backtrace.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 608b405..42e37ca 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -6,9 +6,16 @@ use thiserror::Error; #[error("...")] pub struct Inner; +#[cfg(thiserror_nightly_testing)] +#[derive(Error, Debug)] +#[error("...")] +pub struct InnerBacktrace { + backtrace: std::backtrace::Backtrace, +} + #[cfg(thiserror_nightly_testing)] pub mod structs { - use super::Inner; + use super::{Inner, InnerBacktrace}; use std::backtrace::Backtrace; use std::error::Error; use std::sync::Arc; @@ -55,7 +62,7 @@ pub mod structs { pub struct CombinedBacktraceFrom { #[from] #[backtrace] - source: Inner, + source: InnerBacktrace, } #[derive(Error, Debug)] @@ -101,7 +108,9 @@ pub mod structs { let error = BacktraceFrom::from(Inner); assert!(error.backtrace().is_some()); - let error = CombinedBacktraceFrom::from(Inner); + let error = CombinedBacktraceFrom::from(InnerBacktrace { + backtrace: Backtrace::capture(), + }); assert!(error.backtrace().is_some()); let error = OptBacktraceFrom::from(Inner); @@ -114,7 +123,7 @@ pub mod structs { #[cfg(thiserror_nightly_testing)] pub mod enums { - use super::Inner; + use super::{Inner, InnerBacktrace}; use std::backtrace::Backtrace; use std::error::Error; use std::sync::Arc; @@ -170,7 +179,7 @@ pub mod enums { Test { #[from] #[backtrace] - source: Inner, + source: InnerBacktrace, }, } @@ -221,7 +230,9 @@ pub mod enums { let error = BacktraceFrom::from(Inner); assert!(error.backtrace().is_some()); - let error = CombinedBacktraceFrom::from(Inner); + let error = CombinedBacktraceFrom::from(InnerBacktrace { + backtrace: Backtrace::capture(), + }); assert!(error.backtrace().is_some()); let error = OptBacktraceFrom::from(Inner); From ca33ed9bc7417d35f45f7de4b6d1ea9aa2801382 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 12:57:47 -0700 Subject: [PATCH 05/11] Touch up PR 137 --- impl/src/expand.rs | 59 ++++++++++++++++++++-------------------------- src/lib.rs | 4 ++-- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 5389482..42c02a5 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -58,25 +58,20 @@ fn impl_struct(input: Struct) -> TokenStream { self.#source.as_dyn_error().backtrace() } }; - if &source_field.member == backtrace { + let combinator = if source == backtrace { + source_backtrace + } else if type_is_option(backtrace_field.ty) { quote! { - use thiserror::private::AsDynError; - #source_backtrace + #source_backtrace.or(self.#backtrace.as_ref()) } } else { - let combinator = if type_is_option(backtrace_field.ty) { - quote! { - #source_backtrace.or(self.#backtrace.as_ref()) - } - } else { - quote! { - std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace)) - } - }; quote! { - use thiserror::private::AsDynError; - #combinator + std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace)) } + }; + quote! { + use thiserror::private::AsDynError; + #combinator } } else if type_is_option(backtrace_field.ty) { quote! { @@ -207,8 +202,9 @@ fn impl_enum(input: Enum) -> TokenStream { None }; - let backtrace_method = if input.has_backtrace() { - let arms = input.variants.iter().map(|variant| { + let backtrace_method = + if input.has_backtrace() { + let arms = input.variants.iter().map(|variant| { let ident = &variant.ident; match (variant.backtrace_field(), variant.source_field()) { (Some(backtrace_field), Some(source_field)) @@ -247,19 +243,14 @@ fn impl_enum(input: Enum) -> TokenStream { } } (Some(backtrace_field), _) => { - let source = variant.from_field().map(|f| &f.member); let backtrace = &backtrace_field.member; - if source == Some(backtrace) { + if variant.from_field().map_or(false, |f| f.member == *backtrace) { let varsource = quote!(source); - let source_backtrace = quote_spanned! {source.span()=> + let source_backtrace = quote_spanned! {backtrace.span()=> #varsource.as_dyn_error().backtrace() }; - quote! { - #ty::#ident { - #source: #varsource, - .. - } => { + #ty::#ident {#backtrace: #varsource, ..} => { use thiserror::private::AsDynError; #source_backtrace } @@ -280,17 +271,17 @@ fn impl_enum(input: Enum) -> TokenStream { }, } }); - Some(quote! { - fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { - #[allow(deprecated)] - match self { - #(#arms)* + Some(quote! { + fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { + #[allow(deprecated)] + match self { + #(#arms)* + } } - } - }) - } else { - None - }; + }) + } else { + None + }; let display_impl = if input.has_display() { let use_as_display = if input.variants.iter().any(|v| { diff --git a/src/lib.rs b/src/lib.rs index 3bebedf..a1416c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,8 +161,8 @@ //! # }; //! ``` //! -//! - If a field is `#[from]` and `#[backtrace]`, the Error trait's `backtrace()` -//! method is forwarded to the field. +//! - If a field is `#[from]` and `#[backtrace]`, the Error trait's +//! `backtrace()` method is forwarded to the field. //! //! ```rust //! # const IGNORE: &str = stringify! { From 9b542cef8fbb31e07c5e0ec5cdca682d020c0888 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 13:08:53 -0700 Subject: [PATCH 06/11] Adjust how distinct backtrace field is accessed --- impl/src/expand.rs | 28 ++++++++++++------------- impl/src/prop.rs | 51 +++++++++++++++++++++------------------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 42c02a5..6636eaf 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -129,21 +129,20 @@ fn impl_struct(input: Struct) -> TokenStream { } }); - let from_impl = input.from_and_distinct_backtrace_fields().map( - |(from_field, backtrace_field)| { - let from = from_field.ty; - let body = from_initializer(from_field, backtrace_field); - quote! { - #[allow(unused_qualifications)] - impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { - #[allow(deprecated)] - fn from(source: #from) -> Self { - #ty #body - } + let from_impl = input.from_field().map(|from_field| { + let backtrace_field = input.distinct_backtrace_field(); + let from = from_field.ty; + let body = from_initializer(from_field, backtrace_field); + quote! { + #[allow(unused_qualifications)] + impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { + #[allow(deprecated)] + fn from(source: #from) -> Self { + #ty #body } } - }, - ); + } + }); let error_trait = spanned_error_trait(input.original); @@ -342,7 +341,8 @@ fn impl_enum(input: Enum) -> TokenStream { }; let from_impls = input.variants.iter().filter_map(|variant| { - let (from_field, backtrace_field) = variant.from_and_distinct_backtrace_fields()?; + let from_field = variant.from_field()?; + let backtrace_field = variant.distinct_backtrace_field(); let variant = &variant.ident; let from = from_field.ty; let body = from_initializer(from_field, backtrace_field); diff --git a/impl/src/prop.rs b/impl/src/prop.rs index f80ca3d..f652e37 100644 --- a/impl/src/prop.rs +++ b/impl/src/prop.rs @@ -6,20 +6,6 @@ impl Struct<'_> { from_field(&self.fields) } - pub(crate) fn from_and_distinct_backtrace_fields(&self) -> Option<(&Field, Option<&Field>)> { - self.from_field().map(|from_field| { - if let Some(backtrace_field) = self.backtrace_field() { - if backtrace_field.member == from_field.member { - (from_field, None) - } else { - (from_field, Some(backtrace_field)) - } - } else { - (from_field, None) - } - }) - } - pub(crate) fn source_field(&self) -> Option<&Field> { source_field(&self.fields) } @@ -27,6 +13,18 @@ impl Struct<'_> { pub(crate) fn backtrace_field(&self) -> Option<&Field> { backtrace_field(&self.fields) } + + // The #[backtrace] field, if it is not the same as the #[from] field. + pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> { + let backtrace_field = self.backtrace_field()?; + if self.from_field().map_or(false, |from_field| { + from_field.member == backtrace_field.member + }) { + None + } else { + Some(backtrace_field) + } + } } impl Enum<'_> { @@ -61,20 +59,6 @@ impl Variant<'_> { from_field(&self.fields) } - pub(crate) fn from_and_distinct_backtrace_fields(&self) -> Option<(&Field, Option<&Field>)> { - self.from_field().map(|from_field| { - if let Some(backtrace_field) = self.backtrace_field() { - if backtrace_field.member == from_field.member { - (from_field, None) - } else { - (from_field, Some(backtrace_field)) - } - } else { - (from_field, None) - } - }) - } - pub(crate) fn source_field(&self) -> Option<&Field> { source_field(&self.fields) } @@ -82,6 +66,17 @@ impl Variant<'_> { pub(crate) fn backtrace_field(&self) -> Option<&Field> { backtrace_field(&self.fields) } + + pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> { + let backtrace_field = self.backtrace_field()?; + if self.from_field().map_or(false, |from_field| { + from_field.member == backtrace_field.member + }) { + None + } else { + Some(backtrace_field) + } + } } impl Field<'_> { From ed396c2074be980ffde77d63bd78427b0d2b4cf2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 13:14:31 -0700 Subject: [PATCH 07/11] Factor out distinct backtrace logic --- impl/src/prop.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/impl/src/prop.rs b/impl/src/prop.rs index f652e37..6d8a924 100644 --- a/impl/src/prop.rs +++ b/impl/src/prop.rs @@ -14,16 +14,9 @@ impl Struct<'_> { backtrace_field(&self.fields) } - // The #[backtrace] field, if it is not the same as the #[from] field. pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> { let backtrace_field = self.backtrace_field()?; - if self.from_field().map_or(false, |from_field| { - from_field.member == backtrace_field.member - }) { - None - } else { - Some(backtrace_field) - } + distinct_backtrace_field(backtrace_field, self.from_field()) } } @@ -69,13 +62,7 @@ impl Variant<'_> { pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> { let backtrace_field = self.backtrace_field()?; - if self.from_field().map_or(false, |from_field| { - from_field.member == backtrace_field.member - }) { - None - } else { - Some(backtrace_field) - } + distinct_backtrace_field(backtrace_field, self.from_field()) } } @@ -123,6 +110,20 @@ fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { None } +// The #[backtrace] field, if it is not the same as the #[from] field. +fn distinct_backtrace_field<'a, 'b>( + backtrace_field: &'a Field<'b>, + from_field: Option<&Field>, +) -> Option<&'a Field<'b>> { + if from_field.map_or(false, |from_field| { + from_field.member == backtrace_field.member + }) { + None + } else { + Some(backtrace_field) + } +} + fn type_is_backtrace(ty: &Type) -> bool { let path = match ty { Type::Path(ty) => &ty.path, From 2b37b9ed6226b5ea46116d4d0775275f086bc3a6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 13:40:17 -0700 Subject: [PATCH 08/11] Handle enum containing #[source] #[backtrace] field --- impl/src/expand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 6636eaf..393b383 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -243,7 +243,7 @@ fn impl_enum(input: Enum) -> TokenStream { } (Some(backtrace_field), _) => { let backtrace = &backtrace_field.member; - if variant.from_field().map_or(false, |f| f.member == *backtrace) { + if variant.source_field().map_or(false, |f| f.member == *backtrace) { let varsource = quote!(source); let source_backtrace = quote_spanned! {backtrace.span()=> #varsource.as_dyn_error().backtrace() From d49c5af10f8fb0448258ebfd400f32b6ed8c1372 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 13:41:57 -0700 Subject: [PATCH 09/11] Handle backtrace coming from Option source field --- impl/src/expand.rs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 393b383..1fb765e 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -201,9 +201,8 @@ fn impl_enum(input: Enum) -> TokenStream { None }; - let backtrace_method = - if input.has_backtrace() { - let arms = input.variants.iter().map(|variant| { + let backtrace_method = if input.has_backtrace() { + let arms = input.variants.iter().map(|variant| { let ident = &variant.ident; match (variant.backtrace_field(), variant.source_field()) { (Some(backtrace_field), Some(source_field)) @@ -245,8 +244,14 @@ fn impl_enum(input: Enum) -> TokenStream { let backtrace = &backtrace_field.member; if variant.source_field().map_or(false, |f| f.member == *backtrace) { let varsource = quote!(source); - let source_backtrace = quote_spanned! {backtrace.span()=> - #varsource.as_dyn_error().backtrace() + let source_backtrace = if type_is_option(backtrace_field.ty) { + quote_spanned! {backtrace.span()=> + #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace()) + } + } else { + quote_spanned! {backtrace.span()=> + #varsource.as_dyn_error().backtrace() + } }; quote! { #ty::#ident {#backtrace: #varsource, ..} => { @@ -270,17 +275,17 @@ fn impl_enum(input: Enum) -> TokenStream { }, } }); - Some(quote! { - fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { - #[allow(deprecated)] - match self { - #(#arms)* - } + Some(quote! { + fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { + #[allow(deprecated)] + match self { + #(#arms)* } - }) - } else { - None - }; + } + }) + } else { + None + }; let display_impl = if input.has_display() { let use_as_display = if input.variants.iter().any(|v| { From 799bb53b10324f72cf6042f126b467735693f1f1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 13:44:42 -0700 Subject: [PATCH 10/11] Extract combined backtrace-source case to separate match arm --- impl/src/expand.rs | 50 ++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 1fb765e..5f761b8 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -240,36 +240,38 @@ fn impl_enum(input: Enum) -> TokenStream { } } } - (Some(backtrace_field), _) => { + (Some(backtrace_field), Some(source_field)) + if backtrace_field.member == source_field.member => + { let backtrace = &backtrace_field.member; - if variant.source_field().map_or(false, |f| f.member == *backtrace) { - let varsource = quote!(source); - let source_backtrace = if type_is_option(backtrace_field.ty) { - quote_spanned! {backtrace.span()=> - #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace()) - } - } else { - quote_spanned! {backtrace.span()=> - #varsource.as_dyn_error().backtrace() - } - }; - quote! { - #ty::#ident {#backtrace: #varsource, ..} => { - use thiserror::private::AsDynError; - #source_backtrace - } + let varsource = quote!(source); + let source_backtrace = if type_is_option(source_field.ty) { + quote_spanned! {backtrace.span()=> + #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace()) } } else { - let body = if type_is_option(backtrace_field.ty) { - quote!(backtrace.as_ref()) - } else { - quote!(std::option::Option::Some(backtrace)) - }; - quote! { - #ty::#ident {#backtrace: backtrace, ..} => #body, + quote_spanned! {backtrace.span()=> + #varsource.as_dyn_error().backtrace() + } + }; + quote! { + #ty::#ident {#backtrace: #varsource, ..} => { + use thiserror::private::AsDynError; + #source_backtrace } } } + (Some(backtrace_field), _) => { + let backtrace = &backtrace_field.member; + let body = if type_is_option(backtrace_field.ty) { + quote!(backtrace.as_ref()) + } else { + quote!(std::option::Option::Some(backtrace)) + }; + quote! { + #ty::#ident {#backtrace: backtrace, ..} => #body, + } + } (None, _) => quote! { #ty::#ident {..} => std::option::Option::None, }, From 71c7ebec0ed3dd891f78bfe5cd9d9c3a616bab78 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 14:12:47 -0700 Subject: [PATCH 11/11] Update documentation of source-backtrace behavior --- README.md | 6 +++--- src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 101a522..e12b693 100644 --- a/README.md +++ b/README.md @@ -137,14 +137,14 @@ pub enum DataStoreError { } ``` -- If a field is `#[from]` and `#[backtrace]`, the Error trait's `backtrace()` - method is forwarded to the `source`. +- If a field is both a source (named `source`, or has `#[source]` or `#[from]` + attribute) *and* is marked `#[backtrace]`, then the Error trait's + `backtrace()` method is forwarded to the source's backtrace. ```rust #[derive(Error, Debug)] pub enum MyError { Io { - #[from] #[backtrace] source: io::Error, }, diff --git a/src/lib.rs b/src/lib.rs index a1416c0..2fae25c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,15 +161,15 @@ //! # }; //! ``` //! -//! - If a field is `#[from]` and `#[backtrace]`, the Error trait's -//! `backtrace()` method is forwarded to the field. +//! - If a field is both a source (named `source`, or has `#[source]` or +//! `#[from]` attribute) *and* is marked `#[backtrace]`, then the Error +//! trait's `backtrace()` method is forwarded to the source's backtrace. //! //! ```rust //! # const IGNORE: &str = stringify! { //! #[derive(Error, Debug)] //! pub enum MyError { //! Io { -//! #[from] //! #[backtrace] //! source: io::Error, //! },