diff --git a/impl/src/expand.rs b/impl/src/expand.rs index f98a262..f2f7116 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -59,45 +59,53 @@ fn impl_struct(input: Struct) -> TokenStream { } }); - let backtrace_method = input.backtrace_field().map(|backtrace_field| { + let provide_method = input.backtrace_field().map(|backtrace_field| { + let demand = quote!(demand); let backtrace = &backtrace_field.member; let body = if let Some(source_field) = input.source_field() { let source = &source_field.member; - let source_backtrace = if type_is_option(source_field.ty) { + let source_provide = if type_is_option(source_field.ty) { quote_spanned! {source.span()=> - self.#source.as_ref().and_then(|source| source.as_dyn_error().backtrace()) + if let std::option::Option::Some(source) = &self.#source { + source.as_dyn_error().provide(#demand); + } } } else { quote_spanned! {source.span()=> - self.#source.as_dyn_error().backtrace() + self.#source.as_dyn_error().provide(#demand); } }; - let combinator = if source == backtrace { - source_backtrace + let self_provide = if source == backtrace { + None } else if type_is_option(backtrace_field.ty) { - quote! { - #source_backtrace.or(self.#backtrace.as_ref()) - } + Some(quote! { + if let std::option::Option::Some(backtrace) = &self.#backtrace { + #demand.provide_ref::(backtrace); + } + }) } else { - quote! { - std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace)) - } + Some(quote! { + #demand.provide_ref::(&self.#backtrace); + }) }; quote! { use thiserror::__private::AsDynError; - #combinator + #source_provide + #self_provide } } else if type_is_option(backtrace_field.ty) { quote! { - self.#backtrace.as_ref() + if let std::option::Option::Some(backtrace) = &self.#backtrace { + #demand.provide_ref::(backtrace); + } } } else { quote! { - std::option::Option::Some(&self.#backtrace) + #demand.provide_ref::(&self.#backtrace); } }; quote! { - fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { + fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) { #body } } @@ -177,7 +185,7 @@ fn impl_struct(input: Struct) -> TokenStream { #[allow(unused_qualifications)] impl #impl_generics #error_trait for #ty #ty_generics #error_where_clause { #source_method - #backtrace_method + #provide_method } #display_impl #from_impl @@ -237,7 +245,8 @@ fn impl_enum(input: Enum) -> TokenStream { None }; - let backtrace_method = if input.has_backtrace() { + let provide_method = if input.has_backtrace() { + let demand = quote!(demand); let arms = input.variants.iter().map(|variant| { let ident = &variant.ident; match (variant.backtrace_field(), variant.source_field()) { @@ -247,22 +256,26 @@ fn impl_enum(input: Enum) -> TokenStream { let backtrace = &backtrace_field.member; let source = &source_field.member; let varsource = quote!(source); - let source_backtrace = if type_is_option(source_field.ty) { + let source_provide = if type_is_option(source_field.ty) { quote_spanned! {source.span()=> - #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace()) + if let std::option::Option::Some(source) = #varsource { + source.as_dyn_error().provide(#demand); + } } } else { quote_spanned! {source.span()=> - #varsource.as_dyn_error().backtrace() + #varsource.as_dyn_error().provide(#demand); } }; - let combinator = if type_is_option(backtrace_field.ty) { + let self_provide = if type_is_option(backtrace_field.ty) { quote! { - #source_backtrace.or(backtrace.as_ref()) + if let std::option::Option::Some(backtrace) = backtrace { + #demand.provide_ref::(backtrace); + } } } else { quote! { - std::option::Option::Some(#source_backtrace.unwrap_or(backtrace)) + #demand.provide_ref::(backtrace); } }; quote! { @@ -272,7 +285,8 @@ fn impl_enum(input: Enum) -> TokenStream { .. } => { use thiserror::__private::AsDynError; - #combinator + #source_provide + #self_provide } } } @@ -281,40 +295,50 @@ fn impl_enum(input: Enum) -> TokenStream { { let backtrace = &backtrace_field.member; let varsource = quote!(source); - let source_backtrace = if type_is_option(source_field.ty) { + let source_provide = if type_is_option(source_field.ty) { quote_spanned! {backtrace.span()=> - #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace()) + if let std::option::Option::Some(source) = #varsource { + source.as_dyn_error().provide(#demand); + } } } else { quote_spanned! {backtrace.span()=> - #varsource.as_dyn_error().backtrace() + #varsource.as_dyn_error().provide(#demand); } }; quote! { #ty::#ident {#backtrace: #varsource, ..} => { use thiserror::__private::AsDynError; - #source_backtrace + #source_provide } } } (Some(backtrace_field), _) => { let backtrace = &backtrace_field.member; let body = if type_is_option(backtrace_field.ty) { - quote!(backtrace.as_ref()) + quote! { + if let std::option::Option::Some(backtrace) = backtrace { + #demand.provide_ref::(backtrace); + } + } } else { - quote!(std::option::Option::Some(backtrace)) + quote! { + #demand.provide_ref::(backtrace); + } }; quote! { - #ty::#ident {#backtrace: backtrace, ..} => #body, + #ty::#ident {#backtrace: backtrace, ..} => { + #body + } } } (None, _) => quote! { - #ty::#ident {..} => std::option::Option::None, + #ty::#ident {..} => {} }, } }); Some(quote! { - fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { + fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) { #[allow(deprecated)] match self { #(#arms)* @@ -420,7 +444,7 @@ fn impl_enum(input: Enum) -> TokenStream { #[allow(unused_qualifications)] impl #impl_generics #error_trait for #ty #ty_generics #error_where_clause { #source_method - #backtrace_method + #provide_method } #display_impl #(#from_impls)* diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 6addb5d..052c6f4 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -1,6 +1,7 @@ -// FIXME: needs to be updated to provide_ref/request_ref API. -#![cfg(any())] -#![cfg_attr(thiserror_nightly_testing, feature(backtrace))] +#![cfg_attr( + thiserror_nightly_testing, + feature(error_generic_member_access, provide_any) +)] use thiserror::Error; @@ -18,8 +19,8 @@ pub struct InnerBacktrace { #[cfg(thiserror_nightly_testing)] pub mod structs { use super::{Inner, InnerBacktrace}; + use std::any; use std::backtrace::Backtrace; - use std::error::Error; use std::sync::Arc; use thiserror::Error; @@ -90,44 +91,44 @@ pub mod structs { let error = PlainBacktrace { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = ExplicitBacktrace { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = OptBacktrace { backtrace: Some(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = ArcBacktrace { backtrace: Arc::new(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = BacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = CombinedBacktraceFrom::from(InnerBacktrace { backtrace: Backtrace::capture(), }); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = OptBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = ArcBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); } } #[cfg(thiserror_nightly_testing)] pub mod enums { use super::{Inner, InnerBacktrace}; + use std::any; use std::backtrace::Backtrace; - use std::error::Error; use std::sync::Arc; use thiserror::Error; @@ -212,36 +213,36 @@ pub mod enums { let error = PlainBacktrace::Test { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = ExplicitBacktrace::Test { backtrace: Backtrace::capture(), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = OptBacktrace::Test { backtrace: Some(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = ArcBacktrace::Test { backtrace: Arc::new(Backtrace::capture()), }; - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = BacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = CombinedBacktraceFrom::from(InnerBacktrace { backtrace: Backtrace::capture(), }); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = OptBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); let error = ArcBacktraceFrom::from(Inner); - assert!(error.backtrace().is_some()); + assert!(any::request_ref::(&error).is_some()); } } diff --git a/tests/test_option.rs b/tests/test_option.rs index 7638751..ed5287d 100644 --- a/tests/test_option.rs +++ b/tests/test_option.rs @@ -1,6 +1,7 @@ -// FIXME: needs to be updated to provide_ref/request_ref API. -#![cfg(any())] -#![cfg_attr(thiserror_nightly_testing, feature(backtrace))] +#![cfg_attr( + thiserror_nightly_testing, + feature(error_generic_member_access, provide_any) +)] #[cfg(thiserror_nightly_testing)] pub mod structs {