From ea8537ad214b150aeefe3dc19c404a6f8ec26f53 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 31 Aug 2022 12:02:17 +0300 Subject: [PATCH 1/8] Replace `fn backtrace()` with `Provider` API --- doc/error.md | 2 +- src/error.rs | 96 +++++-- .../derives_for_enums_with_backtrace.rs | 267 +++++++++++++++--- ...erives_for_generic_enums_with_backtrace.rs | 240 ++++++++++++++-- ...ives_for_generic_structs_with_backtrace.rs | 258 +++++++++++++++-- .../derives_for_structs_with_backtrace.rs | 260 +++++++++++++++-- tests/error/nightly/mod.rs | 14 +- tests/error_tests.rs | 2 +- 8 files changed, 1001 insertions(+), 138 deletions(-) diff --git a/doc/error.md b/doc/error.md index ae9fc730..46b1e0c9 100644 --- a/doc/error.md +++ b/doc/error.md @@ -47,7 +47,7 @@ ignored for one of these methods by using `#[error(not(backtrace))]` or # Example usage ```rust -#![feature(backtrace)] +#![feature(error_generic_member_access, provide_any)] # #[macro_use] extern crate derive_more; # use std::error::Error as _; use std::backtrace::Backtrace; diff --git a/src/error.rs b/src/error.rs index 2eec67e4..5ff125c7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -32,7 +32,7 @@ pub fn expand( }) .collect(); - let (bounds, source, backtrace) = match state.derive_type { + let (bounds, source, provide) = match state.derive_type { DeriveType::Named | DeriveType::Unnamed => render_struct(&type_params, &state)?, DeriveType::Enum => render_enum(&type_params, &state)?, }; @@ -45,11 +45,11 @@ pub fn expand( } }); - let backtrace = backtrace.map(|backtrace| { + let provide = provide.map(|provide| { quote! { - fn backtrace(&self) -> Option<&::std::backtrace::Backtrace> { - #backtrace - } + fn provide<'_demand>(&'_demand self, demand: &mut ::std::any::Demand<'_demand>) { + #provide + } } }); @@ -82,7 +82,7 @@ pub fn expand( let render = quote! { impl#impl_generics ::std::error::Error for #ident#ty_generics #where_clause { #source - #backtrace + #provide } }; @@ -96,9 +96,9 @@ fn render_struct( let parsed_fields = parse_fields(type_params, state)?; let source = parsed_fields.render_source_as_struct(); - let backtrace = parsed_fields.render_backtrace_as_struct(); + let provide = parsed_fields.render_provide_as_struct(); - Ok((parsed_fields.bounds, source, backtrace)) + Ok((parsed_fields.bounds, source, provide)) } fn render_enum( @@ -107,7 +107,7 @@ fn render_enum( ) -> Result<(HashSet, Option, Option)> { let mut bounds = HashSet::default(); let mut source_match_arms = Vec::new(); - let mut backtrace_match_arms = Vec::new(); + let mut provide_match_arms = Vec::new(); for variant in state.enabled_variant_data().variants { let default_info = FullMetaInfo { @@ -131,16 +131,16 @@ fn render_enum( source_match_arms.push(expr); } - if let Some(expr) = parsed_fields.render_backtrace_as_enum_variant_match_arm() { - backtrace_match_arms.push(expr); + if let Some(expr) = parsed_fields.render_provide_as_enum_variant_match_arm() { + provide_match_arms.push(expr); } bounds.extend(parsed_fields.bounds.into_iter()); } - let render = |match_arms: &mut Vec| { + let render = |match_arms: &mut Vec, unmatched| { if !match_arms.is_empty() && match_arms.len() < state.variants.len() { - match_arms.push(quote!(_ => None)); + match_arms.push(quote!(_ => #unmatched)); } if !match_arms.is_empty() { @@ -156,10 +156,10 @@ fn render_enum( } }; - let source = render(&mut source_match_arms); - let backtrace = render(&mut backtrace_match_arms); + let source = render(&mut source_match_arms, quote!(None)); + let provide = render(&mut provide_match_arms, quote!(())); - Ok((bounds, source, backtrace)) + Ok((bounds, source, provide)) } fn allowed_attr_params() -> AttrParams { @@ -203,16 +203,68 @@ impl<'input, 'state> ParsedFields<'input, 'state> { Some(quote!(#pattern => #expr)) } - fn render_backtrace_as_struct(&self) -> Option { + fn render_provide_as_struct(&self) -> Option { let backtrace = self.backtrace?; - let backtrace_expr = &self.data.members[backtrace]; - Some(quote!(Some(&#backtrace_expr))) + + let source_provider = self.source.map(|source| { + let source_expr = &self.data.members[source]; + quote! { + ::std::error::Error::provide(&#source_expr, demand); + } + }); + let backtrace_provider = + if self.source.filter(|source| *source == backtrace).is_some() { + None + } else { + let backtrace_expr = &self.data.members[backtrace]; + Some(quote! { + demand.provide_ref::(&#backtrace_expr); + }) + }; + + if source_provider.is_some() || backtrace_provider.is_some() { + Some(quote! { + #backtrace_provider + #source_provider + }) + } else { + None + } } - fn render_backtrace_as_enum_variant_match_arm(&self) -> Option { + fn render_provide_as_enum_variant_match_arm(&self) -> Option { let backtrace = self.backtrace?; - let pattern = self.data.matcher(&[backtrace], &[quote!(backtrace)]); - Some(quote!(#pattern => Some(backtrace))) + + match self.source { + Some(source) if source == backtrace => { + let pattern = self.data.matcher(&[source], &[quote!(source)]); + Some(quote!( + #pattern => { + ::std::error::Error::provide(source, demand); + } + )) + } + Some(source) => { + let pattern = self.data.matcher( + &[source, backtrace], + &[quote!(source), quote!(backtrace)], + ); + Some(quote!( + #pattern => { + demand.provide_ref::(backtrace); + ::std::error::Error::provide(source, demand); + } + )) + } + None => { + let pattern = self.data.matcher(&[backtrace], &[quote!(backtrace)]); + Some(quote!( + #pattern => { + demand.provide_ref::(backtrace); + } + )) + } + } } } diff --git a/tests/error/nightly/derives_for_enums_with_backtrace.rs b/tests/error/nightly/derives_for_enums_with_backtrace.rs index 4524607a..e3a95dc4 100644 --- a/tests/error/nightly/derives_for_enums_with_backtrace.rs +++ b/tests/error/nightly/derives_for_enums_with_backtrace.rs @@ -1,4 +1,7 @@ #![allow(dead_code)] + +use std::any; + use super::*; derive_display!(TestErr); @@ -53,6 +56,29 @@ enum TestErr { backtrace: Backtrace, field: i32, }, + NamedImplicitNoBacktraceFromSource { + #[error(source)] + err: BacktraceErr, + }, + NamedExplicitNoBacktraceFromSource { + #[error(source, not(backtrace))] + err: BacktraceErr, + }, + NamedExplicitBacktraceFromSource { + #[error(backtrace, source)] + err: BacktraceErr, + }, + NamedImplicitDifferentSourceAndBacktrace { + #[error(source)] + err: BacktraceErr, + backtrace: Backtrace, + }, + NamedExplicitDifferentSourceAndBacktrace { + #[error(source)] + err: BacktraceErr, + #[error(backtrace)] + backtrace: Backtrace, + }, UnnamedImplicitNoBacktrace(i32, i32), UnnamedImplicitBacktrace(Backtrace, i32, i32), UnnamedExplicitNoBacktrace(#[error(not(backtrace))] Backtrace, i32), @@ -63,30 +89,50 @@ enum TestErr { ), UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, i32, i32), UnnamedExplicitSupressesImplicit(#[error(backtrace)] MyBacktrace, Backtrace, i32), + UnnamedImplicitNoBacktraceFromSource(BacktraceErr), + UnnamedExplicitNoBacktraceFromSource(#[error(not(backtrace))] BacktraceErr), + UnnamedExplicitBacktraceFromSource(#[error(backtrace)] BacktraceErr), + UnnamedImplicitDifferentSourceAndBacktrace( + #[error(source)] BacktraceErr, + Backtrace, + ), + UnnamedExplicitDifferentSourceAndBacktrace( + #[error(source)] BacktraceErr, + #[error(backtrace)] Backtrace, + ), } impl TestErr { fn get_stored_backtrace(&self) -> &Backtrace { match self { - Self::NamedImplicitBacktraceByFieldName { backtrace, .. } => backtrace, - Self::NamedImplicitBacktraceByFieldType { - implicit_backtrace, .. - } => implicit_backtrace, - Self::NamedExplicitBacktrace { - explicit_backtrace, .. - } => explicit_backtrace, - Self::NamedExplicitBacktraceByFieldNameRedundant { backtrace, .. } => { - backtrace + Self::NamedImplicitBacktraceByFieldName { backtrace, .. } + | Self::NamedImplicitBacktraceByFieldType { + implicit_backtrace: backtrace, + .. + } + | Self::NamedExplicitBacktrace { + explicit_backtrace: backtrace, + .. } - Self::NamedExplicitBacktraceByFieldTypeRedundant { - implicit_backtrace, + | Self::NamedExplicitBacktraceByFieldNameRedundant { backtrace, .. } + | Self::NamedExplicitBacktraceByFieldTypeRedundant { + implicit_backtrace: backtrace, .. - } => implicit_backtrace, - Self::NamedExplicitSupressesImplicit { not_backtrace, .. } => not_backtrace, - Self::UnnamedImplicitBacktrace(backtrace, _, _) => backtrace, - Self::UnnamedExplicitBacktrace(backtrace, _, _) => backtrace, - Self::UnnamedExplicitBacktraceRedundant(backtrace, _, _) => backtrace, - Self::UnnamedExplicitSupressesImplicit(backtrace, _, _) => backtrace, + } + | Self::NamedExplicitSupressesImplicit { + not_backtrace: backtrace, + .. + } + | Self::NamedImplicitDifferentSourceAndBacktrace { backtrace, .. } + | Self::NamedExplicitDifferentSourceAndBacktrace { backtrace, .. } + | Self::UnnamedImplicitBacktrace(backtrace, _, _) + | Self::UnnamedExplicitBacktrace(backtrace, _, _) + | Self::UnnamedExplicitBacktraceRedundant(backtrace, _, _) + | Self::UnnamedExplicitSupressesImplicit(backtrace, _, _) + | Self::UnnamedImplicitDifferentSourceAndBacktrace(_, backtrace) + | Self::UnnamedExplicitDifferentSourceAndBacktrace(_, backtrace) => { + backtrace + } _ => panic!("ERROR IN TEST IMPLEMENTATION"), } } @@ -98,20 +144,33 @@ impl TestErr { _ => panic!("ERROR IN TEST IMPLEMENTATION"), } } + + fn get_source_backtrace(&self) -> &Backtrace { + any::request_ref(match self { + Self::NamedExplicitBacktraceFromSource { err } + | Self::NamedExplicitDifferentSourceAndBacktrace { err, .. } + | Self::NamedImplicitDifferentSourceAndBacktrace { err, .. } + | Self::UnnamedExplicitBacktraceFromSource(err) + | Self::UnnamedExplicitDifferentSourceAndBacktrace(err, ..) + | Self::UnnamedImplicitDifferentSourceAndBacktrace(err, ..) => err, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + }) + .unwrap() + } } type MyBacktrace = Backtrace; #[test] fn unit() { - assert!(TestErr::Unit.backtrace().is_none()); + assert!(any::request_ref::(&TestErr::Unit).is_none()); } #[test] fn named_implicit_no_backtrace() { let err = TestErr::NamedImplicitNoBacktrace { field: 0 }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -121,7 +180,7 @@ fn named_implicit_backtrace_by_field_name() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -132,7 +191,7 @@ fn named_implicit_backtrace_by_field_type() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -143,7 +202,7 @@ fn named_explicit_no_backtrace_by_field_name() { field: 0, }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -153,7 +212,7 @@ fn named_explicit_no_backtrace_by_field_type() { field: 0, }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -163,7 +222,7 @@ fn named_explicit_backtrace() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -174,7 +233,7 @@ fn named_explicit_no_backtrace_redundant() { field: 0, }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -184,7 +243,7 @@ fn named_explicit_backtrace_by_field_name_redundant() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -195,7 +254,7 @@ fn named_explicit_backtrace_by_field_type_redundant() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -207,23 +266,95 @@ fn named_explicit_supresses_implicit() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); assert_bt!(!=, err, .get_unused_backtrace); } +#[test] +fn named_implicit_no_backtrace_from_source() { + let err = TestErr::NamedImplicitNoBacktraceFromSource { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_no_backtrace_from_source() { + let err = TestErr::NamedExplicitNoBacktraceFromSource { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_backtrace_from_source() { + let err = TestErr::NamedExplicitBacktraceFromSource { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_source_backtrace); +} + +#[test] +fn named_implicit_different_source_and_backtrace() { + let err = TestErr::NamedImplicitDifferentSourceAndBacktrace { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} + +#[test] +fn named_explicit_different_source_and_backtrace() { + let err = TestErr::NamedExplicitDifferentSourceAndBacktrace { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} + #[test] fn unnamed_implicit_no_backtrace() { let err = TestErr::UnnamedImplicitNoBacktrace(0, 0); - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] fn unnamed_implicit_backtrace() { let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -231,14 +362,14 @@ fn unnamed_implicit_backtrace() { fn unnamed_explicit_no_backtrace() { let err = TestErr::UnnamedExplicitNoBacktrace(Backtrace::force_capture(), 0); - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] fn unnamed_explicit_backtrace() { let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -247,7 +378,7 @@ fn unnamed_explicit_no_backtrace_redundant() { let err = TestErr::UnnamedExplicitNoBacktraceRedundant(Backtrace::force_capture(), 0); - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -255,7 +386,7 @@ fn unnamed_explicit_backtrace_redundant() { let err = TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -267,7 +398,73 @@ fn unnamed_explicit_supresses_implicit() { 0, ); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); assert_bt!(!=, err, .get_unused_backtrace); } + +#[test] +fn unnamed_implicit_no_backtrace_from_source() { + let err = TestErr::UnnamedImplicitNoBacktraceFromSource(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_no_backtrace_from_source() { + let err = TestErr::UnnamedExplicitNoBacktraceFromSource(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_from_source() { + let err = TestErr::UnnamedExplicitBacktraceFromSource(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_source_backtrace); +} + +#[test] +fn unnamed_implicit_different_source_and_backtrace() { + let err = TestErr::UnnamedImplicitDifferentSourceAndBacktrace( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} + +#[test] +fn unnamed_explicit_different_source_and_backtrace() { + let err = TestErr::UnnamedExplicitDifferentSourceAndBacktrace( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} diff --git a/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs b/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs index 1f6a28ca..8ca22e99 100644 --- a/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs +++ b/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs @@ -1,4 +1,7 @@ #![allow(dead_code)] + +use std::any; + use super::*; derive_display!(TestErr, T); @@ -104,14 +107,14 @@ type MyBacktrace = Backtrace; #[test] fn unit() { - assert!(TestErr::::Unit.backtrace().is_none()); + assert!(any::request_ref::(&TestErr::::Unit).is_none()); } #[test] fn named_implicit_no_backtrace() { let err = TestErr::NamedImplicitNoBacktrace { field: 0 }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -121,7 +124,7 @@ fn named_implicit_backtrace_by_field_name() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -132,7 +135,7 @@ fn named_implicit_backtrace_by_field_type() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -143,7 +146,7 @@ fn named_explicit_no_backtrace_by_field_name() { field: 0, }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -153,7 +156,7 @@ fn named_explicit_no_backtrace_by_field_type() { field: 0, }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -163,7 +166,7 @@ fn named_explicit_backtrace() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -174,7 +177,7 @@ fn named_explicit_no_backtrace_redundant() { field: 0, }; - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -184,7 +187,7 @@ fn named_explicit_backtrace_by_field_name_redundant() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -195,7 +198,7 @@ fn named_explicit_backtrace_by_field_type_redundant() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -207,7 +210,7 @@ fn named_explicit_supresses_implicit() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); assert_bt!(!=, err, .get_unused_backtrace); } @@ -216,14 +219,14 @@ fn named_explicit_supresses_implicit() { fn unnamed_implicit_no_backtrace() { let err = TestErr::UnnamedImplicitNoBacktrace(0, 0); - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] fn unnamed_implicit_backtrace() { let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -231,14 +234,14 @@ fn unnamed_implicit_backtrace() { fn unnamed_explicit_no_backtrace() { let err = TestErr::UnnamedExplicitNoBacktrace(Backtrace::force_capture(), 0); - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] fn unnamed_explicit_backtrace() { let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -247,7 +250,7 @@ fn unnamed_explicit_no_backtrace_redundant() { let err = TestErr::UnnamedExplicitNoBacktraceRedundant(Backtrace::force_capture(), 0); - assert!(err.backtrace().is_none()); + assert!(any::request_ref::(&err).is_none()); } #[test] @@ -255,7 +258,7 @@ fn unnamed_explicit_backtrace_redundant() { let err = TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); } @@ -267,7 +270,208 @@ fn unnamed_explicit_supresses_implicit() { 0, ); - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, .get_stored_backtrace); assert_bt!(!=, err, .get_unused_backtrace); } + +derive_display!(BoundedTestErr, T); +#[derive(Debug, Error)] +enum BoundedTestErr { + NamedImplicitNoBacktraceFromSource { + #[error(source)] + err: T, + }, + NamedExplicitNoBacktraceFromSource { + #[error(source, not(backtrace))] + err: T, + }, + NamedExplicitBacktraceFromSource { + #[error(backtrace, source)] + err: T, + }, + NamedImplicitDifferentSourceAndBacktrace { + #[error(source)] + err: T, + backtrace: Backtrace, + }, + NamedExplicitDifferentSourceAndBacktrace { + #[error(source)] + err: T, + #[error(backtrace)] + backtrace: Backtrace, + }, + UnnamedImplicitNoBacktraceFromSource(T), + UnnamedExplicitNoBacktraceFromSource(#[error(not(backtrace))] T), + UnnamedExplicitBacktraceFromSource(#[error(backtrace)] T), + UnnamedImplicitDifferentSourceAndBacktrace(#[error(source)] T, Backtrace), + UnnamedExplicitDifferentSourceAndBacktrace( + #[error(source)] T, + #[error(backtrace)] Backtrace, + ), +} + +impl BoundedTestErr { + fn get_stored_backtrace(&self) -> &Backtrace { + match self { + Self::NamedImplicitDifferentSourceAndBacktrace { backtrace, .. } + | Self::NamedExplicitDifferentSourceAndBacktrace { backtrace, .. } + | Self::UnnamedImplicitDifferentSourceAndBacktrace(_, backtrace) + | Self::UnnamedExplicitDifferentSourceAndBacktrace(_, backtrace) => { + backtrace + } + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + } + } + + fn get_source_backtrace(&self) -> &Backtrace { + any::request_ref(match self { + Self::NamedExplicitBacktraceFromSource { err } + | Self::NamedExplicitDifferentSourceAndBacktrace { err, .. } + | Self::NamedImplicitDifferentSourceAndBacktrace { err, .. } + | Self::UnnamedExplicitBacktraceFromSource(err) + | Self::UnnamedExplicitDifferentSourceAndBacktrace(err, ..) + | Self::UnnamedImplicitDifferentSourceAndBacktrace(err, ..) => err, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + }) + .unwrap() + } +} + +#[test] +fn named_implicit_no_backtrace_from_source() { + let err = BoundedTestErr::NamedImplicitNoBacktraceFromSource { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_no_backtrace_from_source() { + let err = BoundedTestErr::NamedExplicitNoBacktraceFromSource { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_backtrace_from_source() { + let err = BoundedTestErr::NamedExplicitBacktraceFromSource { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_source_backtrace); +} + +#[test] +fn named_implicit_different_source_and_backtrace() { + let err = BoundedTestErr::NamedImplicitDifferentSourceAndBacktrace { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} + +#[test] +fn named_explicit_different_source_and_backtrace() { + let err = BoundedTestErr::NamedExplicitDifferentSourceAndBacktrace { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} + +#[test] +fn unnamed_implicit_no_backtrace_from_source() { + let err = BoundedTestErr::UnnamedImplicitNoBacktraceFromSource(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_no_backtrace_from_source() { + let err = BoundedTestErr::UnnamedExplicitNoBacktraceFromSource(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_from_source() { + let err = BoundedTestErr::UnnamedExplicitBacktraceFromSource(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_source_backtrace); +} + +#[test] +fn unnamed_implicit_different_source_and_backtrace() { + let err = BoundedTestErr::UnnamedImplicitDifferentSourceAndBacktrace( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} + +#[test] +fn unnamed_explicit_different_source_and_backtrace() { + let err = BoundedTestErr::UnnamedExplicitDifferentSourceAndBacktrace( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_source_backtrace); +} diff --git a/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs b/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs index 59900dcc..83fa9c57 100644 --- a/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs +++ b/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs @@ -1,4 +1,7 @@ #![allow(dead_code)] + +use std::any; + use super::*; #[test] @@ -9,7 +12,7 @@ fn named_implicit_no_backtrace() { field: T, } - assert!(TestErr::::default().backtrace().is_none()); + assert!(any::request_ref::(&TestErr::::default()).is_none()); } #[test] @@ -27,7 +30,7 @@ fn named_implicit_backtrace_by_field_name() { backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err); } @@ -44,7 +47,7 @@ fn named_implicit_backtrace_by_field_type() { implicit_backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, implicit_backtrace); } @@ -60,11 +63,10 @@ fn named_explicit_no_backtrace_by_field_name() { type MyBacktrace = Backtrace; - assert!(TestErr { + assert!(any::request_ref::(&TestErr { backtrace: Backtrace::force_capture(), field: 0 - } - .backtrace() + }) .is_none()); } @@ -78,11 +80,10 @@ fn named_explicit_no_backtrace_by_field_type() { field: T, } - assert!(TestErr { + assert!(any::request_ref::(&TestErr { implicit_backtrace: Backtrace::force_capture(), field: 0 - } - .backtrace() + }) .is_none()); } @@ -102,7 +103,7 @@ fn named_explicit_backtrace() { explicit_backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, explicit_backtrace); } @@ -119,11 +120,10 @@ fn named_explicit_no_backtrace_redundant() { type MyBacktrace = Backtrace; - assert!(TestErr { + assert!(any::request_ref::(&TestErr { not_backtrace: Backtrace::force_capture(), field: 0 - } - .backtrace() + }) .is_none()); } @@ -143,7 +143,7 @@ fn named_explicit_backtrace_by_field_name_redundant() { backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err); } @@ -161,7 +161,7 @@ fn named_explicit_backtrace_by_field_type_redundant() { implicit_backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, implicit_backtrace); } @@ -184,18 +184,128 @@ fn named_explicit_supresses_implicit() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, not_backtrace); assert_bt!(!=, err); } +#[test] +fn named_implicit_no_backtrace_from_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(source)] + err: T, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_no_backtrace_from_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(source, not(backtrace))] + err: T, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_backtrace_from_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace, source)] + err: T, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, any::request_ref::(&err.err).unwrap()); +} + +#[test] +fn named_implicit_different_source_and_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(source)] + err: T, + backtrace: Backtrace, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, backtrace); + assert_bt!(!=, err, any::request_ref::(&err.err).unwrap()); +} + +#[test] +fn named_explicit_different_source_and_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(source)] + err: T, + #[error(backtrace)] + backtrace: Backtrace, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, backtrace); + assert_bt!(!=, err, any::request_ref::(&err.err).unwrap()); +} + #[test] fn unnamed_implicit_no_backtrace() { derive_display!(TestErr, T); #[derive(Default, Debug, Error)] struct TestErr(T, T); - assert!(TestErr::::default().backtrace().is_none()); + assert!(any::request_ref::(&TestErr::::default()).is_none()); } #[test] @@ -205,8 +315,8 @@ fn unnamed_implicit_backtrace() { struct TestErr(Backtrace, T, T); let err = TestErr(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); } #[test] @@ -215,7 +325,10 @@ fn unnamed_explicit_no_backtrace() { #[derive(Debug, Error)] struct TestErr(#[error(not(backtrace))] Backtrace, T); - assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); + assert!( + any::request_ref::(&TestErr(Backtrace::force_capture(), 0)) + .is_none() + ); } #[test] @@ -227,8 +340,8 @@ fn unnamed_explicit_backtrace() { type MyBacktrace = Backtrace; let err = TestErr(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); } #[test] @@ -242,7 +355,10 @@ fn unnamed_explicit_no_backtrace_redundant() { type MyBacktrace = Backtrace; - assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); + assert!( + any::request_ref::(&TestErr(Backtrace::force_capture(), 0)) + .is_none() + ); } #[test] @@ -252,8 +368,8 @@ fn unnamed_explicit_backtrace_redundant() { struct TestErr(#[error(backtrace)] Backtrace, T, T); let err = TestErr(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); } #[test] @@ -270,7 +386,93 @@ fn unnamed_explicit_supresses_implicit() { 0, ); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); - assert_bt!(!=, err, 1); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); + assert_bt!(!=, err, .1); +} + +#[test] +fn unnamed_implicit_no_backtrace_from_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(T); + + let err = TestErr(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_no_backtrace_from_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(not(backtrace))] T); + + let err = TestErr(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_from_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] T); + + let err = TestErr(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, any::request_ref::(&err.0).unwrap()); +} + +#[test] +fn unnamed_implicit_different_source_and_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] T, Backtrace); + + let err = TestErr( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .1); + assert_bt!(!=, err, any::request_ref::(&err.0).unwrap()); +} + +#[test] +fn unnamed_explicit_different_source_and_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] T, #[error(backtrace)] Backtrace); + + let err = TestErr( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .1); + assert_bt!(!=, err, any::request_ref::(&err.0).unwrap()); } diff --git a/tests/error/nightly/derives_for_structs_with_backtrace.rs b/tests/error/nightly/derives_for_structs_with_backtrace.rs index 28ea775b..e63483d6 100644 --- a/tests/error/nightly/derives_for_structs_with_backtrace.rs +++ b/tests/error/nightly/derives_for_structs_with_backtrace.rs @@ -1,9 +1,12 @@ #![allow(dead_code)] + +use std::any; + use super::*; #[test] fn unit() { - assert!(SimpleErr.backtrace().is_none()); + assert!(any::request_ref::(&SimpleErr).is_none()); } #[test] @@ -14,7 +17,7 @@ fn named_implicit_no_backtrace() { field: i32, } - assert!(TestErr::default().backtrace().is_none()); + assert!(any::request_ref::(&TestErr::default()).is_none()); } #[test] @@ -32,7 +35,7 @@ fn named_implicit_backtrace_by_field_name() { backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err); } @@ -49,7 +52,7 @@ fn named_implicit_backtrace_by_field_type() { implicit_backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, implicit_backtrace); } @@ -65,11 +68,10 @@ fn named_explicit_no_backtrace_by_field_name() { type MyBacktrace = Backtrace; - assert!(TestErr { + assert!(any::request_ref::(&TestErr { backtrace: Backtrace::force_capture(), field: 0 - } - .backtrace() + }) .is_none()); } @@ -83,11 +85,10 @@ fn named_explicit_no_backtrace_by_field_type() { field: i32, } - assert!(TestErr { + assert!(any::request_ref::(&TestErr { implicit_backtrace: Backtrace::force_capture(), field: 0 - } - .backtrace() + }) .is_none()); } @@ -107,7 +108,7 @@ fn named_explicit_backtrace() { explicit_backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, explicit_backtrace); } @@ -124,11 +125,10 @@ fn named_explicit_no_backtrace_redundant() { type MyBacktrace = Backtrace; - assert!(TestErr { + assert!(any::request_ref::(&TestErr { not_backtrace: Backtrace::force_capture(), field: 0 - } - .backtrace() + }) .is_none()); } @@ -148,7 +148,7 @@ fn named_explicit_backtrace_by_field_name_redundant() { backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err); } @@ -166,7 +166,7 @@ fn named_explicit_backtrace_by_field_type_redundant() { implicit_backtrace: Backtrace::force_capture(), field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, implicit_backtrace); } @@ -189,18 +189,128 @@ fn named_explicit_supresses_implicit() { field: 0, }; - assert!(err.backtrace().is_some()); + assert!(any::request_ref::(&err).is_some()); assert_bt!(==, err, not_backtrace); assert_bt!(!=, err); } +#[test] +fn named_implicit_no_backtrace_from_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(source)] + err: BacktraceErr, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_no_backtrace_from_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(source, not(backtrace))] + err: BacktraceErr, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn named_explicit_backtrace_from_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace, source)] + err: BacktraceErr, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, any::request_ref::(&err.err).unwrap()); +} + +#[test] +fn named_implicit_different_source_and_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(source)] + err: BacktraceErr, + backtrace: Backtrace, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, backtrace); + assert_bt!(!=, err, any::request_ref::(&err.err).unwrap()); +} + +#[test] +fn named_explicit_different_source_and_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(source)] + err: BacktraceErr, + #[error(backtrace)] + backtrace: Backtrace, + } + + let err = TestErr { + err: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + }; + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, backtrace); + assert_bt!(!=, err, any::request_ref::(&err.err).unwrap()); +} + #[test] fn unnamed_implicit_no_backtrace() { derive_display!(TestErr); #[derive(Default, Debug, Error)] struct TestErr(i32, i32); - assert!(TestErr::default().backtrace().is_none()); + assert!(any::request_ref::(&TestErr::default()).is_none()); } #[test] @@ -210,8 +320,8 @@ fn unnamed_implicit_backtrace() { struct TestErr(Backtrace, i32, i32); let err = TestErr(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); } #[test] @@ -220,7 +330,10 @@ fn unnamed_explicit_no_backtrace() { #[derive(Debug, Error)] struct TestErr(#[error(not(backtrace))] Backtrace, i32); - assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); + assert!( + any::request_ref::(&TestErr(Backtrace::force_capture(), 0)) + .is_none() + ); } #[test] @@ -232,8 +345,8 @@ fn unnamed_explicit_backtrace() { type MyBacktrace = Backtrace; let err = TestErr(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); } #[test] @@ -247,7 +360,10 @@ fn unnamed_explicit_no_backtrace_redundant() { type MyBacktrace = Backtrace; - assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); + assert!( + any::request_ref::(&TestErr(Backtrace::force_capture(), 0)) + .is_none() + ); } #[test] @@ -257,8 +373,8 @@ fn unnamed_explicit_backtrace_redundant() { struct TestErr(#[error(backtrace)] Backtrace, i32, i32); let err = TestErr(Backtrace::force_capture(), 0, 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); } #[test] @@ -275,7 +391,93 @@ fn unnamed_explicit_supresses_implicit() { 0, ); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 0); - assert_bt!(!=, err, 1); + assert!(any::request_ref::(&err).is_some()); + assert_bt!(==, err, .0); + assert_bt!(!=, err, .1); +} + +#[test] +fn unnamed_implicit_no_backtrace_from_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(BacktraceErr); + + let err = TestErr(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_no_backtrace_from_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(not(backtrace))] BacktraceErr); + + let err = TestErr(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_none()); + assert!(any::request_value::(&err).is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_from_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] BacktraceErr); + + let err = TestErr(BacktraceErr { + backtrace: Backtrace::force_capture(), + }); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, any::request_ref::(&err.0).unwrap()); +} + +#[test] +fn unnamed_implicit_different_source_and_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] BacktraceErr, Backtrace); + + let err = TestErr( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .1); + assert_bt!(!=, err, any::request_ref::(&err.0).unwrap()); +} + +#[test] +fn unnamed_explicit_different_source_and_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] BacktraceErr, #[error(backtrace)] Backtrace); + + let err = TestErr( + BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + (|| Backtrace::force_capture())(), // ensure backtraces are different + ); + + assert!(err.source().is_some()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .1); + assert_bt!(!=, err, any::request_ref::(&err.0).unwrap()); } diff --git a/tests/error/nightly/mod.rs b/tests/error/nightly/mod.rs index 57de7ba2..4467d52f 100644 --- a/tests/error/nightly/mod.rs +++ b/tests/error/nightly/mod.rs @@ -40,14 +40,20 @@ use super::*; /// ``` macro_rules! assert_bt { (@impl $macro:ident, $error:expr, $backtrace:expr) => { - $macro!($error.backtrace().unwrap().to_string(), $backtrace.to_string()); + $macro!(std::any::request_ref::(&$error).unwrap().to_string(), $backtrace.to_string()); }; (@expand $macro:ident, $error:expr, .$backtrace:ident) => { assert_bt!(@impl $macro, $error, $error.$backtrace()) }; - (@expand $macro:ident, $error:expr, $backtrace:tt) => { + (@expand $macro:ident, $error:expr, .$backtrace:tt) => { assert_bt!(@impl $macro, $error, $error.$backtrace) }; + (@expand $macro:ident, $error:expr, $backtrace:ident) => { + assert_bt!(@impl $macro, $error, $error.$backtrace) + }; + (@expand $macro:ident, $error:expr, $backtrace:expr) => { + assert_bt!(@impl $macro, $error, $backtrace) + }; (@expand $macro:ident, $error:expr) => { assert_bt!(@expand $macro, $error, backtrace) }; @@ -79,7 +85,7 @@ impl Default for BacktraceErr { } impl Error for BacktraceErr { - fn backtrace(&self) -> Option<&Backtrace> { - Some(&self.backtrace) + fn provide<'a>(&'a self, demand: &mut std::any::Demand<'a>) { + demand.provide_ref(&self.backtrace).provide_value(|| 42); } } diff --git a/tests/error_tests.rs b/tests/error_tests.rs index c2798bd1..962e6c20 100644 --- a/tests/error_tests.rs +++ b/tests/error_tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "nightly", feature(backtrace))] +#![cfg_attr(feature = "nightly", feature(error_generic_member_access, provide_any))] #[macro_use] extern crate derive_more; From 406b245efc5cf1a9d1fefb1173a93cd9800b9143 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 31 Aug 2022 12:06:40 +0300 Subject: [PATCH 2/8] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08ed3288..8b33a06a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use a deterministic `HashSet` in all derives, this is needed for rust analyzer to work correctly. +- Use `Provider` API for backtraces in `Error` derive ## 0.99.10 - 2020-09-11 From 7c9c10300c77505039156636732bcca807de267e Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 31 Aug 2022 12:20:05 +0300 Subject: [PATCH 3/8] Fix docs --- doc/error.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/error.md b/doc/error.md index 46b1e0c9..9973b709 100644 --- a/doc/error.md +++ b/doc/error.md @@ -50,7 +50,7 @@ ignored for one of these methods by using `#[error(not(backtrace))]` or #![feature(error_generic_member_access, provide_any)] # #[macro_use] extern crate derive_more; # use std::error::Error as _; -use std::backtrace::Backtrace; +use std::{any, backtrace::Backtrace}; // std::error::Error requires std::fmt::Debug and std::fmt::Display, // so we can also use derive_more::Display for fully declarative @@ -90,17 +90,31 @@ enum CompoundError { WithSource { source: Simple, }, + #[from(ignore)] + WithBacktraceFromSource { + #[error(backtrace)] + source: Simple, + }, + WithDifferentBacktrace { + source: Simple, + backtrace: Backtrace, + }, WithExplicitSource { #[error(source)] explicit_source: WithSource, }, + #[from(ignore)] + WithBacktraceFromExplicitSource { + #[error(backtrace, source)] + explicit_source: WithSource, + }, Tuple(WithExplicitSource), WithoutSource(#[error(not(source))] Tuple), } fn main() { assert!(Simple.source().is_none()); - assert!(Simple.backtrace().is_none()); + assert!(any::request_ref::(&Simple).is_none()); assert!(WithSource::default().source().is_some()); assert!(WithExplicitSource::default().source().is_some()); assert!(Tuple::default().source().is_some()); @@ -110,7 +124,7 @@ fn main() { backtrace: Backtrace::capture(), }; assert!(with_source_and_backtrace.source().is_some()); - assert!(with_source_and_backtrace.backtrace().is_some()); + assert!(any::request_ref::(&with_source_and_backtrace).is_some()); assert!(CompoundError::Simple.source().is_none()); assert!(CompoundError::from(Simple).source().is_some()); From 8546c8cb27a9302e4b7e683144e78b2b09869ef9 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 31 Aug 2022 12:23:40 +0300 Subject: [PATCH 4/8] Fix docs --- doc/error.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/error.md b/doc/error.md index 9973b709..dafa3457 100644 --- a/doc/error.md +++ b/doc/error.md @@ -95,6 +95,7 @@ enum CompoundError { #[error(backtrace)] source: Simple, }, + #[display(fmt = "{source}")] WithDifferentBacktrace { source: Simple, backtrace: Backtrace, From b1f7b50440ddf6afc4bf62ae28786d6e0fc02d77 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 8 Sep 2022 12:50:44 +0300 Subject: [PATCH 5/8] Minor corrections --- CHANGELOG.md | 2 +- src/error.rs | 49 ++++++++++++++++++++++--------------------------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b33a06a..92834d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use a deterministic `HashSet` in all derives, this is needed for rust analyzer to work correctly. -- Use `Provider` API for backtraces in `Error` derive +- Use `Provider` API for backtraces in `Error` derive. ## 0.99.10 - 2020-09-11 diff --git a/src/error.rs b/src/error.rs index 5ff125c7..81669317 100644 --- a/src/error.rs +++ b/src/error.rs @@ -143,17 +143,13 @@ fn render_enum( match_arms.push(quote!(_ => #unmatched)); } - if !match_arms.is_empty() { - let expr = quote! { + (!match_arms.is_empty()).then(|| { + quote! { match self { #(#match_arms),* } - }; - - Some(expr) - } else { - None - } + } + }) }; let source = render(&mut source_match_arms, quote!(None)); @@ -212,24 +208,23 @@ impl<'input, 'state> ParsedFields<'input, 'state> { ::std::error::Error::provide(&#source_expr, demand); } }); - let backtrace_provider = - if self.source.filter(|source| *source == backtrace).is_some() { - None - } else { + let backtrace_provider = self + .source + .filter(|source| *source == backtrace) + .is_none() + .then(|| { let backtrace_expr = &self.data.members[backtrace]; - Some(quote! { + quote! { demand.provide_ref::(&#backtrace_expr); - }) - }; + } + }); - if source_provider.is_some() || backtrace_provider.is_some() { - Some(quote! { + (source_provider.is_some() || backtrace_provider.is_some()).then(|| { + quote! { #backtrace_provider #source_provider - }) - } else { - None - } + } + }) } fn render_provide_as_enum_variant_match_arm(&self) -> Option { @@ -238,31 +233,31 @@ impl<'input, 'state> ParsedFields<'input, 'state> { match self.source { Some(source) if source == backtrace => { let pattern = self.data.matcher(&[source], &[quote!(source)]); - Some(quote!( + Some(quote! { #pattern => { ::std::error::Error::provide(source, demand); } - )) + }) } Some(source) => { let pattern = self.data.matcher( &[source, backtrace], &[quote!(source), quote!(backtrace)], ); - Some(quote!( + Some(quote! { #pattern => { demand.provide_ref::(backtrace); ::std::error::Error::provide(source, demand); } - )) + }) } None => { let pattern = self.data.matcher(&[backtrace], &[quote!(backtrace)]); - Some(quote!( + Some(quote! { #pattern => { demand.provide_ref::(backtrace); } - )) + }) } } } From 422b37de4438d0e1de1d0803d6230a1eff03ca25 Mon Sep 17 00:00:00 2001 From: ilslv Date: Thu, 8 Sep 2022 15:12:03 +0300 Subject: [PATCH 6/8] Fix unit tests --- tests/error/nightly/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/error/nightly/mod.rs b/tests/error/nightly/mod.rs index 4467d52f..c9d432ff 100644 --- a/tests/error/nightly/mod.rs +++ b/tests/error/nightly/mod.rs @@ -86,6 +86,8 @@ impl Default for BacktraceErr { impl Error for BacktraceErr { fn provide<'a>(&'a self, demand: &mut std::any::Demand<'a>) { - demand.provide_ref(&self.backtrace).provide_value(|| 42); + demand + .provide_ref::(&self.backtrace) + .provide_value::(42); } } From db6eabc44e3ba438f2a9214b4e2fce845f003ced Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 12 Sep 2022 08:10:51 +0300 Subject: [PATCH 7/8] Remove mentions of `backtrace()` method in docs --- doc/error.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/error.md b/doc/error.md index dafa3457..d1b9f8dd 100644 --- a/doc/error.md +++ b/doc/error.md @@ -2,8 +2,8 @@ # Using #[derive(Error)] Deriving `Error` will generate an `Error` implementation, that contains -(depending on the type) a `source()` and a `backtrace()` method. Please note, -at the time of writing `backtrace` is only supported on nightly rust. So you +(depending on the type) a `source()` and a `provide()` method. Please note, +at the time of writing `provide()` is only supported on nightly rust. So you have to use that to make use of it. For a struct, these methods always do the same. For an `enum` they have separate @@ -17,15 +17,6 @@ often [`From`] as well. [`Display`]: display.html [`From`]: from.html -## When and how does it derive `backtrace()`? - -1. It's a struct/variant with named fields and one of the fields is - called `backtrace`. Then it would return that field as the `backtrace`. -2. It's a tuple struct/variant and the type of exactly one of the fields is - called `Backtrace`. Then it would return that field as the `backtrace`. -3. One of the fields is annotated with `#[error(backtrace)]`. Then it would - return that field as the `backtrace`. - ## When and how does it derive `source()`? 1. It's a struct/variant with named fields and one is the fields is @@ -36,6 +27,15 @@ often [`From`] as well. 3. One of the fields is annotated with `#[error(backtrace)]`. Then it would return that field as the `backtrace`. +## When and how does it derive `provide()`? + +1. It's a struct/variant with named fields and one of the fields is + called `backtrace`. Then it would return that field as the `backtrace`. +2. It's a tuple struct/variant and the type of exactly one of the fields is + called `Backtrace`. Then it would return that field as the `backtrace`. +3. One of the fields is annotated with `#[error(backtrace)]`. Then it would + return that field as the `backtrace`. + ## Ignoring fields for derives It's possible to ignore a field or a whole enum variant completely for this From 56e9d159a14aa3698442d2a88affd199a62c8220 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 12 Sep 2022 08:15:42 +0300 Subject: [PATCH 8/8] Correction --- doc/error.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/error.md b/doc/error.md index d1b9f8dd..50b7037f 100644 --- a/doc/error.md +++ b/doc/error.md @@ -24,8 +24,8 @@ often [`From`] as well. 2. It's a tuple struct/variant and there's exactly one field that is not used as the `backtrace`. So either a tuple struct with one field, or one with two where one is the `backtrace`. Then it returns this field as the `source`. -3. One of the fields is annotated with `#[error(backtrace)]`. Then it would - return that field as the `backtrace`. +3. One of the fields is annotated with `#[error(source)]`. Then it would + return that field as the `source`. ## When and how does it derive `provide()`?