From 2f3f61fc674a1ce87109d0c284e81beea07386d2 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Mon, 14 Aug 2023 17:40:15 +0300 Subject: [PATCH 1/9] Add support for forwarding source in Error derive --- impl/src/error.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/impl/src/error.rs b/impl/src/error.rs index 99771b4d..358e527f 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -110,9 +110,13 @@ fn render_enum( let mut source_match_arms = Vec::new(); let mut provide_match_arms = Vec::new(); - for variant in state.enabled_variant_data().variants { + let variant_data = state.enabled_variant_data(); + + for (variant, variant_info) in variant_data.variants.iter().zip(&variant_data.infos) + { let default_info = FullMetaInfo { enabled: true, + forward: variant_info.forward, ..FullMetaInfo::default() }; @@ -160,10 +164,10 @@ fn render_enum( fn allowed_attr_params() -> AttrParams { AttrParams { - enum_: vec!["ignore"], - struct_: vec!["ignore"], - variant: vec!["ignore"], - field: vec!["ignore", "source", "backtrace"], + enum_: vec!["ignore", "forward"], + struct_: vec!["ignore", "forward"], + variant: vec!["ignore", "forward"], + field: vec!["ignore", "source", "backtrace", "forward"], } } @@ -189,13 +193,21 @@ impl<'input, 'state> ParsedFields<'input, 'state> { fn render_source_as_struct(&self) -> Option { let source = self.source?; let ident = &self.data.members[source]; - Some(render_some(quote! { #ident })) + if self.data.infos[source].forward { + Some(quote! { ::derive_more::Error::source(&#ident) }) + } else { + Some(render_some(quote! { #ident })) + } } fn render_source_as_enum_variant_match_arm(&self) -> Option { let source = self.source?; let pattern = self.data.matcher(&[source], &[quote! { source }]); - let expr = render_some(quote! { source }); + let expr = if self.data.infos[source].forward { + quote! { ::derive_more::Error::source(source) } + } else { + render_some(quote! { source }) + }; Some(quote! { #pattern => #expr }) } From 05fd28cb0ddfe730feed806bb96268b8b15e34d5 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Mon, 14 Aug 2023 17:41:10 +0300 Subject: [PATCH 2/9] Add tests for `#[error(forward)]` --- tests/error/derives_forward.rs | 108 +++++++++++++ tests/error/mod.rs | 1 + tests/error/nightly/derives_forward.rs | 202 +++++++++++++++++++++++++ tests/error/nightly/mod.rs | 1 + 4 files changed, 312 insertions(+) create mode 100644 tests/error/derives_forward.rs create mode 100644 tests/error/nightly/derives_forward.rs diff --git a/tests/error/derives_forward.rs b/tests/error/derives_forward.rs new file mode 100644 index 00000000..61ce18cf --- /dev/null +++ b/tests/error/derives_forward.rs @@ -0,0 +1,108 @@ +use super::*; + +derive_display!(Inner); +#[derive(Debug, Error)] +struct Inner { + source: SimpleErr, +} + +derive_display!(StructAttr); +#[derive(Debug, Error)] +#[error(forward)] +struct StructAttr { + source: Inner, +} + +#[test] +fn struct_attr() { + let err = StructAttr { + source: Inner { source: SimpleErr }, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +derive_display!(EnumAttr); +#[derive(Debug, Error)] +#[error(forward)] +enum EnumAttr { + A { source: Inner }, + B { source: Inner }, +} + +#[test] +fn enum_attr() { + let err_a = EnumAttr::A { + source: Inner { source: SimpleErr }, + }; + + let err_b = EnumAttr::B { + source: Inner { source: SimpleErr }, + }; + + assert!(err_a.source().is_some()); + assert!(err_a.source().unwrap().is::()); + + assert!(err_b.source().is_some()); + assert!(err_b.source().unwrap().is::()); +} + +derive_display!(VariantAttr); +#[derive(Debug, Error)] +enum VariantAttr { + #[error(forward)] + A { + source: Inner, + }, + B { + source: Inner, + }, +} + +#[test] +fn variant_attr() { + let err_a = VariantAttr::A { + source: Inner { source: SimpleErr }, + }; + + let err_b = VariantAttr::B { + source: Inner { source: SimpleErr }, + }; + + assert!(err_a.source().is_some()); + assert!(err_a.source().unwrap().is::()); + + assert!(err_b.source().is_some()); + assert!(err_b.source().unwrap().is::()); +} + +derive_display!(FieldAttr); +#[derive(Debug, Error)] +enum FieldAttr { + A { + #[error(forward, source)] + field: Inner, + }, + B { + #[error(source)] + field: Inner, + }, +} + +#[test] +fn field_attr() { + let err_a = FieldAttr::A { + field: Inner { source: SimpleErr }, + }; + + let err_b = FieldAttr::B { + field: Inner { source: SimpleErr }, + }; + + assert!(err_a.source().is_some()); + assert!(err_a.source().unwrap().is::()); + + assert!(err_b.source().is_some()); + assert!(err_b.source().unwrap().is::()); +} diff --git a/tests/error/mod.rs b/tests/error/mod.rs index 352d40aa..9cc47b48 100644 --- a/tests/error/mod.rs +++ b/tests/error/mod.rs @@ -47,6 +47,7 @@ mod derives_for_enums_with_source; mod derives_for_generic_enums_with_source; mod derives_for_generic_structs_with_source; mod derives_for_structs_with_source; +mod derives_forward; #[cfg(all(feature = "std", nightly))] mod nightly; diff --git a/tests/error/nightly/derives_forward.rs b/tests/error/nightly/derives_forward.rs new file mode 100644 index 00000000..73c3c864 --- /dev/null +++ b/tests/error/nightly/derives_forward.rs @@ -0,0 +1,202 @@ +use std::any; + +use super::*; + +derive_display!(Inner); +#[derive(Debug, Error)] +struct Inner { + #[error(backtrace)] + source: BacktraceErr, +} + +derive_display!(StructAttr); +#[derive(Debug, Error)] +#[error(forward)] +struct StructAttr { + #[error(backtrace)] + source: Inner, +} + +impl StructAttr { + fn get_source_backtrace(&self) -> &Backtrace { + any::request_ref(&self.source.source).unwrap() + } +} + +#[test] +fn struct_attr() { + let err = StructAttr { + source: Inner { + source: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); + assert!(any::request_ref::(&err).is_some()); + assert_eq!(any::request_value::(&err), Some(42)); + assert_bt!(==, err, .get_source_backtrace); +} + +derive_display!(EnumAttr); +#[derive(Debug, Error)] +#[error(forward)] +enum EnumAttr { + A { + #[error(backtrace)] + source: Inner, + }, + B { + #[error(backtrace)] + source: Inner, + }, +} + +impl EnumAttr { + fn get_source_backtrace(&self) -> &Backtrace { + any::request_ref(match self { + Self::A { source } => &source.source, + Self::B { source } => &source.source, + }) + .unwrap() + } +} + +#[test] +fn enum_attr() { + let err_a = EnumAttr::A { + source: Inner { + source: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }, + }; + let err_b = EnumAttr::B { + source: Inner { + source: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }, + }; + + assert!(err_a.source().is_some()); + assert!(err_a.source().unwrap().is::()); + assert!(any::request_ref::(&err_a).is_some()); + assert_eq!(any::request_value::(&err_a), Some(42)); + assert_bt!(==, err_a, .get_source_backtrace); + + assert!(err_b.source().is_some()); + assert!(err_b.source().unwrap().is::()); + assert!(any::request_ref::(&err_b).is_some()); + assert_eq!(any::request_value::(&err_b), Some(42)); + assert_bt!(==, err_b, .get_source_backtrace); +} + +derive_display!(VariantAttr); +#[derive(Debug, Error)] +enum VariantAttr { + #[error(forward)] + A { + #[error(backtrace)] + source: Inner, + }, + B { + #[error(backtrace)] + source: Inner, + }, +} + +impl VariantAttr { + fn get_source_backtrace(&self) -> &Backtrace { + any::request_ref(match self { + Self::A { source } => &source.source, + Self::B { source } => &source.source, + }) + .unwrap() + } +} + +#[test] +fn variant_attr() { + let err_a = VariantAttr::A { + source: Inner { + source: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }, + }; + let err_b = VariantAttr::B { + source: Inner { + source: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }, + }; + + assert!(err_a.source().is_some()); + assert!(err_a.source().unwrap().is::()); + assert!(any::request_ref::(&err_a).is_some()); + assert_eq!(any::request_value::(&err_a), Some(42)); + assert_bt!(==, err_a, .get_source_backtrace); + + assert!(err_b.source().is_some()); + assert!(err_b.source().unwrap().is::()); + assert!(any::request_ref::(&err_b).is_some()); + assert_eq!(any::request_value::(&err_b), Some(42)); + assert_bt!(==, err_b, .get_source_backtrace); +} + +derive_display!(FieldAttr); +#[derive(Debug, Error)] +enum FieldAttr { + A { + #[error(forward, backtrace)] + source: Inner, + }, + B { + #[error(backtrace)] + source: Inner, + }, +} + +impl FieldAttr { + fn get_source_backtrace(&self) -> &Backtrace { + any::request_ref(match self { + Self::A { source } => &source.source, + Self::B { source } => &source.source, + }) + .unwrap() + } +} + +#[test] +fn field_attr() { + let err_a = FieldAttr::A { + source: Inner { + source: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }, + }; + let err_b = FieldAttr::B { + source: Inner { + source: BacktraceErr { + backtrace: Backtrace::force_capture(), + }, + }, + }; + + assert!(err_a.source().is_some()); + assert!(err_a.source().unwrap().is::()); + assert!(any::request_ref::(&err_a).is_some()); + assert_eq!(any::request_value::(&err_a), Some(42)); + assert_bt!(==, err_a, .get_source_backtrace); + + assert!(err_b.source().is_some()); + assert!(err_b.source().unwrap().is::()); + assert!(any::request_ref::(&err_b).is_some()); + assert_eq!(any::request_value::(&err_b), Some(42)); + assert_bt!(==, err_b, .get_source_backtrace); +} diff --git a/tests/error/nightly/mod.rs b/tests/error/nightly/mod.rs index c9d432ff..936a672e 100644 --- a/tests/error/nightly/mod.rs +++ b/tests/error/nightly/mod.rs @@ -69,6 +69,7 @@ mod derives_for_enums_with_backtrace; mod derives_for_generic_enums_with_backtrace; mod derives_for_generic_structs_with_backtrace; mod derives_for_structs_with_backtrace; +mod derives_forward; derive_display!(BacktraceErr); #[derive(Debug)] From a42e79707fbe07d3d824efef8fb78a5ca36e08b8 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Mon, 14 Aug 2023 18:20:10 +0300 Subject: [PATCH 3/9] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51133030..a6ab60f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add support for container format in `Debug` derive with the same syntax as `Display` derives. ([#279](https://github.com/JelteF/derive_more/pull/279)) - `derive_more::derive` module exporting only macros, without traits. ([#290](https://github.com/JelteF/derive_more/pull/290)) +- Add support for source forwarding in `Error` derive. ([#293](https://github.com/JelteF/derive_more/pull/293)) ### Changed From 936464c9a1ec6cce3615061b677ec812284592b0 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Mon, 14 Aug 2023 17:46:38 +0300 Subject: [PATCH 4/9] Remove field attr --- impl/src/error.rs | 2 +- tests/error/derives_forward.rs | 30 --------------- tests/error/nightly/derives_forward.rs | 53 -------------------------- 3 files changed, 1 insertion(+), 84 deletions(-) diff --git a/impl/src/error.rs b/impl/src/error.rs index 358e527f..5b69fe57 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -167,7 +167,7 @@ fn allowed_attr_params() -> AttrParams { enum_: vec!["ignore", "forward"], struct_: vec!["ignore", "forward"], variant: vec!["ignore", "forward"], - field: vec!["ignore", "source", "backtrace", "forward"], + field: vec!["ignore", "source", "backtrace"], } } diff --git a/tests/error/derives_forward.rs b/tests/error/derives_forward.rs index 61ce18cf..d1d8a2dd 100644 --- a/tests/error/derives_forward.rs +++ b/tests/error/derives_forward.rs @@ -76,33 +76,3 @@ fn variant_attr() { assert!(err_b.source().is_some()); assert!(err_b.source().unwrap().is::()); } - -derive_display!(FieldAttr); -#[derive(Debug, Error)] -enum FieldAttr { - A { - #[error(forward, source)] - field: Inner, - }, - B { - #[error(source)] - field: Inner, - }, -} - -#[test] -fn field_attr() { - let err_a = FieldAttr::A { - field: Inner { source: SimpleErr }, - }; - - let err_b = FieldAttr::B { - field: Inner { source: SimpleErr }, - }; - - assert!(err_a.source().is_some()); - assert!(err_a.source().unwrap().is::()); - - assert!(err_b.source().is_some()); - assert!(err_b.source().unwrap().is::()); -} diff --git a/tests/error/nightly/derives_forward.rs b/tests/error/nightly/derives_forward.rs index 73c3c864..dac3c8b5 100644 --- a/tests/error/nightly/derives_forward.rs +++ b/tests/error/nightly/derives_forward.rs @@ -147,56 +147,3 @@ fn variant_attr() { assert_eq!(any::request_value::(&err_b), Some(42)); assert_bt!(==, err_b, .get_source_backtrace); } - -derive_display!(FieldAttr); -#[derive(Debug, Error)] -enum FieldAttr { - A { - #[error(forward, backtrace)] - source: Inner, - }, - B { - #[error(backtrace)] - source: Inner, - }, -} - -impl FieldAttr { - fn get_source_backtrace(&self) -> &Backtrace { - any::request_ref(match self { - Self::A { source } => &source.source, - Self::B { source } => &source.source, - }) - .unwrap() - } -} - -#[test] -fn field_attr() { - let err_a = FieldAttr::A { - source: Inner { - source: BacktraceErr { - backtrace: Backtrace::force_capture(), - }, - }, - }; - let err_b = FieldAttr::B { - source: Inner { - source: BacktraceErr { - backtrace: Backtrace::force_capture(), - }, - }, - }; - - assert!(err_a.source().is_some()); - assert!(err_a.source().unwrap().is::()); - assert!(any::request_ref::(&err_a).is_some()); - assert_eq!(any::request_value::(&err_a), Some(42)); - assert_bt!(==, err_a, .get_source_backtrace); - - assert!(err_b.source().is_some()); - assert!(err_b.source().unwrap().is::()); - assert!(any::request_ref::(&err_b).is_some()); - assert_eq!(any::request_value::(&err_b), Some(42)); - assert_bt!(==, err_b, .get_source_backtrace); -} From 4408d1074cfc7eec27fe374871a9c3030184395f Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Mon, 14 Aug 2023 18:12:55 +0300 Subject: [PATCH 5/9] Update docs with forwarding details --- impl/doc/error.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/impl/doc/error.md b/impl/doc/error.md index 20ff24cf..f49a335d 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -32,6 +32,11 @@ often [`From` as well](crate::From). 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`. +4. The source field is annotated with `#[error(backtrace)]`. Then it would + forward implementation to the source. + +If the source field is annotated with `#[error(backtrace)]`, and another field +is also annotated/was inferred, both backtraces will be provided. ### Ignoring fields for derives @@ -41,6 +46,11 @@ detecting `backtrace` and `source`. It's also possible to mark a field only ignored for one of these methods by using `#[error(not(backtrace))]` or `#[error(not(source))]`. +### Source Forwarding + +A struct, enum, or enum variant can be annotated with `#[error(forward)]` to forward +the `source()` implementation to the source field (inferred or explicitly annotated), +instead of returning the field itself. ### What works in `no_std`? @@ -126,6 +136,17 @@ enum CompoundError { }, Tuple(WithExplicitSource), WithoutSource(#[error(not(source))] Tuple), + #[error(forward)] + #[from(ignore)] + ForwardedImplicitSource { + source: WithSource, + }, + #[error(forward)] + #[from(ignore)] + ForwardedExplicitSourceWithBacktrace { + #[error(source, backtrace)] + explicit_source: WithSourceAndBacktrace, + } } assert!(Simple.source().is_none()); @@ -146,5 +167,14 @@ assert!(CompoundError::from(Simple).source().is_some()); assert!(CompoundError::from(WithSource::default()).source().is_some()); assert!(CompoundError::from(WithExplicitSource::default()).source().is_some()); assert!(CompoundError::from(Tuple::default()).source().is_none()); + +let forwarded = CompoundError::ForwardedImplicitSource { source: WithSource::default() }; +assert!(forwarded.source().is_some()); +assert!(forwarded.source().unwrap().is::()); + +let forwarded_with_backtrace = CompoundError::ForwardedExplicitSourceWithBacktrace { explicit_source: WithSourceAndBacktrace { source: Simple, backtrace: Backtrace::capture() } }; +assert!(forwarded_with_backtrace.source().is_some()); +assert!(forwarded_with_backtrace.source().unwrap().is::()); +assert!(any::request_ref::(&forwarded_with_backtrace).is_some()); # } ``` From 4b0a08f751c8c7facc21eaae50ab4a56d44a6558 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 15 Aug 2023 12:16:08 +0300 Subject: [PATCH 6/9] Add errors on invalid forward usage and compile_fail tests for them --- impl/src/error.rs | 14 ++++++++++++-- .../error/forward_no_source_enum.rs | 18 ++++++++++++++++++ .../error/forward_no_source_enum.stderr | 7 +++++++ .../error/forward_no_source_struct.rs | 13 +++++++++++++ .../error/forward_no_source_struct.stderr | 7 +++++++ .../error/forward_no_source_variant.rs | 19 +++++++++++++++++++ .../error/forward_no_source_variant.stderr | 7 +++++++ 7 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 tests/compile_fail/error/forward_no_source_enum.rs create mode 100644 tests/compile_fail/error/forward_no_source_enum.stderr create mode 100644 tests/compile_fail/error/forward_no_source_struct.rs create mode 100644 tests/compile_fail/error/forward_no_source_struct.stderr create mode 100644 tests/compile_fail/error/forward_no_source_variant.rs create mode 100644 tests/compile_fail/error/forward_no_source_variant.stderr diff --git a/impl/src/error.rs b/impl/src/error.rs index 5b69fe57..eb288f43 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{spanned::Spanned as _, Error, Result}; @@ -390,7 +390,12 @@ fn parse_fields_impl<'input, 'state, P>( where P: Fn(&str, &syn::Field, usize) -> bool, { - let MultiFieldData { fields, infos, .. } = state.enabled_fields_data(); + let MultiFieldData { + fields, + infos, + variant_info, + .. + } = state.enabled_fields_data(); let iter = fields .iter() @@ -418,6 +423,11 @@ where if let Some((index, _, _)) = source { parsed_fields.source = Some(index); + } else if variant_info.forward { + return Err(syn::Error::new( + Span::call_site(), + "`#[error(forward)]` cannot be used when an error has no source", + )); } if let Some((index, _, _)) = backtrace { diff --git a/tests/compile_fail/error/forward_no_source_enum.rs b/tests/compile_fail/error/forward_no_source_enum.rs new file mode 100644 index 00000000..4d5e81d5 --- /dev/null +++ b/tests/compile_fail/error/forward_no_source_enum.rs @@ -0,0 +1,18 @@ +use derive_more::Error; + +#[derive(Debug, Error)] +#[error(forward)] +enum Foo { + Bar, + Baz { + source: Box, + }, +} + +impl ::core::fmt::Display for Foo { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!(f, "") + } +} + +fn main() {} diff --git a/tests/compile_fail/error/forward_no_source_enum.stderr b/tests/compile_fail/error/forward_no_source_enum.stderr new file mode 100644 index 00000000..2e0d36de --- /dev/null +++ b/tests/compile_fail/error/forward_no_source_enum.stderr @@ -0,0 +1,7 @@ +error: `#[error(forward)]` cannot be used when an error has no source + --> tests/compile_fail/error/forward_no_source_enum.rs:3:17 + | +3 | #[derive(Debug, Error)] + | ^^^^^ + | + = note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile_fail/error/forward_no_source_struct.rs b/tests/compile_fail/error/forward_no_source_struct.rs new file mode 100644 index 00000000..f5f359b5 --- /dev/null +++ b/tests/compile_fail/error/forward_no_source_struct.rs @@ -0,0 +1,13 @@ +use derive_more::Error; + +#[derive(Debug, Error)] +#[error(forward)] +struct Foo; + +impl ::core::fmt::Display for Foo { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!(f, "") + } +} + +fn main() {} diff --git a/tests/compile_fail/error/forward_no_source_struct.stderr b/tests/compile_fail/error/forward_no_source_struct.stderr new file mode 100644 index 00000000..9743137d --- /dev/null +++ b/tests/compile_fail/error/forward_no_source_struct.stderr @@ -0,0 +1,7 @@ +error: `#[error(forward)]` cannot be used when an error has no source + --> tests/compile_fail/error/forward_no_source_struct.rs:3:17 + | +3 | #[derive(Debug, Error)] + | ^^^^^ + | + = note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile_fail/error/forward_no_source_variant.rs b/tests/compile_fail/error/forward_no_source_variant.rs new file mode 100644 index 00000000..8830cc2a --- /dev/null +++ b/tests/compile_fail/error/forward_no_source_variant.rs @@ -0,0 +1,19 @@ +use derive_more::Error; + +#[derive(Debug, Error)] +enum Foo { + #[error(forward)] + Bar, + #[error(forward)] + Baz { + source: Box, + }, +} + +impl ::core::fmt::Display for Foo { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!(f, "") + } +} + +fn main() {} diff --git a/tests/compile_fail/error/forward_no_source_variant.stderr b/tests/compile_fail/error/forward_no_source_variant.stderr new file mode 100644 index 00000000..e30f9d03 --- /dev/null +++ b/tests/compile_fail/error/forward_no_source_variant.stderr @@ -0,0 +1,7 @@ +error: `#[error(forward)]` cannot be used when an error has no source + --> tests/compile_fail/error/forward_no_source_variant.rs:3:17 + | +3 | #[derive(Debug, Error)] + | ^^^^^ + | + = note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info) From 9250ccab4701bc87c525e1b3b0a31a214fa9a656 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 15 Aug 2023 12:43:20 +0300 Subject: [PATCH 7/9] Add note about recommended forward usage --- impl/doc/error.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/impl/doc/error.md b/impl/doc/error.md index f49a335d..5632eb2f 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -52,6 +52,9 @@ A struct, enum, or enum variant can be annotated with `#[error(forward)]` to for the `source()` implementation to the source field (inferred or explicitly annotated), instead of returning the field itself. +In general this approach is only recommended if the error is intended to be fully +transparent, and forwards implementation of [`Display`](crate::Display) as well. + ### What works in `no_std`? If you want to use the `Error` derive on `no_std` environments, then you need to From 7688e66bcb420c760f2bd0ebefafaf62e9371166 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 15 Aug 2023 15:15:23 +0300 Subject: [PATCH 8/9] Update forward tests to reflect provide API changes --- impl/doc/error.md | 2 +- tests/error/nightly/derives_forward.rs | 28 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/impl/doc/error.md b/impl/doc/error.md index d82ad823..0c9b90c8 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -179,6 +179,6 @@ assert!(forwarded.source().unwrap().is::()); let forwarded_with_backtrace = CompoundError::ForwardedExplicitSourceWithBacktrace { explicit_source: WithSourceAndBacktrace { source: Simple, backtrace: Backtrace::capture() } }; assert!(forwarded_with_backtrace.source().is_some()); assert!(forwarded_with_backtrace.source().unwrap().is::()); -assert!(any::request_ref::(&forwarded_with_backtrace).is_some()); +assert!(request_ref::(&forwarded_with_backtrace).is_some()); # } ``` diff --git a/tests/error/nightly/derives_forward.rs b/tests/error/nightly/derives_forward.rs index dac3c8b5..591f0fe3 100644 --- a/tests/error/nightly/derives_forward.rs +++ b/tests/error/nightly/derives_forward.rs @@ -1,4 +1,4 @@ -use std::any; +use core::error::{request_ref, request_value}; use super::*; @@ -19,7 +19,7 @@ struct StructAttr { impl StructAttr { fn get_source_backtrace(&self) -> &Backtrace { - any::request_ref(&self.source.source).unwrap() + request_ref(&self.source.source).unwrap() } } @@ -35,8 +35,8 @@ fn struct_attr() { assert!(err.source().is_some()); assert!(err.source().unwrap().is::()); - assert!(any::request_ref::(&err).is_some()); - assert_eq!(any::request_value::(&err), Some(42)); + assert!(request_ref::(&err).is_some()); + assert_eq!(request_value::(&err), Some(42)); assert_bt!(==, err, .get_source_backtrace); } @@ -56,7 +56,7 @@ enum EnumAttr { impl EnumAttr { fn get_source_backtrace(&self) -> &Backtrace { - any::request_ref(match self { + request_ref(match self { Self::A { source } => &source.source, Self::B { source } => &source.source, }) @@ -83,14 +83,14 @@ fn enum_attr() { assert!(err_a.source().is_some()); assert!(err_a.source().unwrap().is::()); - assert!(any::request_ref::(&err_a).is_some()); - assert_eq!(any::request_value::(&err_a), Some(42)); + assert!(request_ref::(&err_a).is_some()); + assert_eq!(request_value::(&err_a), Some(42)); assert_bt!(==, err_a, .get_source_backtrace); assert!(err_b.source().is_some()); assert!(err_b.source().unwrap().is::()); - assert!(any::request_ref::(&err_b).is_some()); - assert_eq!(any::request_value::(&err_b), Some(42)); + assert!(request_ref::(&err_b).is_some()); + assert_eq!(request_value::(&err_b), Some(42)); assert_bt!(==, err_b, .get_source_backtrace); } @@ -110,7 +110,7 @@ enum VariantAttr { impl VariantAttr { fn get_source_backtrace(&self) -> &Backtrace { - any::request_ref(match self { + request_ref(match self { Self::A { source } => &source.source, Self::B { source } => &source.source, }) @@ -137,13 +137,13 @@ fn variant_attr() { assert!(err_a.source().is_some()); assert!(err_a.source().unwrap().is::()); - assert!(any::request_ref::(&err_a).is_some()); - assert_eq!(any::request_value::(&err_a), Some(42)); + assert!(request_ref::(&err_a).is_some()); + assert_eq!(request_value::(&err_a), Some(42)); assert_bt!(==, err_a, .get_source_backtrace); assert!(err_b.source().is_some()); assert!(err_b.source().unwrap().is::()); - assert!(any::request_ref::(&err_b).is_some()); - assert_eq!(any::request_value::(&err_b), Some(42)); + assert!(request_ref::(&err_b).is_some()); + assert_eq!(request_value::(&err_b), Some(42)); assert_bt!(==, err_b, .get_source_backtrace); } From 2c43dd16aeefb63b6259668f058dabc4d3d49c40 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 15 Aug 2023 15:27:56 +0300 Subject: [PATCH 9/9] Add explicit source case to forward tests --- tests/error/derives_forward.rs | 11 ++++++++--- tests/error/nightly/derives_forward.rs | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/error/derives_forward.rs b/tests/error/derives_forward.rs index d1d8a2dd..9e80a44a 100644 --- a/tests/error/derives_forward.rs +++ b/tests/error/derives_forward.rs @@ -27,8 +27,13 @@ derive_display!(EnumAttr); #[derive(Debug, Error)] #[error(forward)] enum EnumAttr { - A { source: Inner }, - B { source: Inner }, + A { + source: Inner, + }, + B { + #[error(source)] + explicit_source: Inner, + }, } #[test] @@ -38,7 +43,7 @@ fn enum_attr() { }; let err_b = EnumAttr::B { - source: Inner { source: SimpleErr }, + explicit_source: Inner { source: SimpleErr }, }; assert!(err_a.source().is_some()); diff --git a/tests/error/nightly/derives_forward.rs b/tests/error/nightly/derives_forward.rs index 591f0fe3..254bb487 100644 --- a/tests/error/nightly/derives_forward.rs +++ b/tests/error/nightly/derives_forward.rs @@ -49,8 +49,8 @@ enum EnumAttr { source: Inner, }, B { - #[error(backtrace)] - source: Inner, + #[error(source, backtrace)] + explicit_source: Inner, }, } @@ -58,7 +58,7 @@ impl EnumAttr { fn get_source_backtrace(&self) -> &Backtrace { request_ref(match self { Self::A { source } => &source.source, - Self::B { source } => &source.source, + Self::B { explicit_source } => &explicit_source.source, }) .unwrap() } @@ -74,7 +74,7 @@ fn enum_attr() { }, }; let err_b = EnumAttr::B { - source: Inner { + explicit_source: Inner { source: BacktraceErr { backtrace: Backtrace::force_capture(), },