From 5786d69dca9ed4d1706d00eb5fab4d00d415dea4 Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Thu, 21 Nov 2019 11:48:17 -0500 Subject: [PATCH 1/8] Add backtrace support to derive-Error macro --- Cargo.toml | 2 +- src/error.rs | 321 +++-- src/utils.rs | 11 +- tests/error.rs | 1094 ----------------- .../error/derives_for_enums_with_backtrace.rs | 271 ++++ tests/error/derives_for_enums_with_source.rs | 249 ++++ ...or_enums_with_source_backtrace_chaining.rs | 240 ++++ ...erives_for_generic_enums_with_backtrace.rs | 271 ++++ .../derives_for_generic_enums_with_source.rs | 248 ++++ ...ic_enums_with_source_backtrace_chaining.rs | 239 ++++ ...ives_for_generic_structs_with_backtrace.rs | 275 +++++ ...derives_for_generic_structs_with_source.rs | 245 ++++ ..._structs_with_source_backtrace_chaining.rs | 210 ++++ .../derives_for_structs_with_backtrace.rs | 280 +++++ .../error/derives_for_structs_with_source.rs | 249 ++++ ..._structs_with_source_backtrace_chaining.rs | 206 ++++ tests/error/mod.rs | 141 +++ tests/error_tests.rs | 6 + 18 files changed, 3392 insertions(+), 1166 deletions(-) delete mode 100644 tests/error.rs create mode 100644 tests/error/derives_for_enums_with_backtrace.rs create mode 100644 tests/error/derives_for_enums_with_source.rs create mode 100644 tests/error/derives_for_enums_with_source_backtrace_chaining.rs create mode 100644 tests/error/derives_for_generic_enums_with_backtrace.rs create mode 100644 tests/error/derives_for_generic_enums_with_source.rs create mode 100644 tests/error/derives_for_generic_enums_with_source_backtrace_chaining.rs create mode 100644 tests/error/derives_for_generic_structs_with_backtrace.rs create mode 100644 tests/error/derives_for_generic_structs_with_source.rs create mode 100644 tests/error/derives_for_generic_structs_with_source_backtrace_chaining.rs create mode 100644 tests/error/derives_for_structs_with_backtrace.rs create mode 100644 tests/error/derives_for_structs_with_source.rs create mode 100644 tests/error/derives_for_structs_with_source_backtrace_chaining.rs create mode 100644 tests/error/mod.rs create mode 100644 tests/error_tests.rs diff --git a/Cargo.toml b/Cargo.toml index 5a18080e..519cf7cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,7 +133,7 @@ required-features = ["display"] [[test]] name = "error" -path = "tests/error.rs" +path = "tests/error_tests.rs" required-features = ["error"] [[test]] diff --git a/src/error.rs b/src/error.rs index bf58071a..c767fc6d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,11 +33,27 @@ pub fn expand( }) .collect(); - let (bounds, body) = match state.derive_type { + let (bounds, source, backtrace) = match state.derive_type { DeriveType::Named | DeriveType::Unnamed => render_struct(&type_params, &state)?, DeriveType::Enum => render_enum(&type_params, &state)?, }; + let source = source.map(|source| { + quote! { + fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { + #source + } + } + }); + + let backtrace = backtrace.map(|backtrace| { + quote! { + fn backtrace(&self) -> Option<&::std::backtrace::Backtrace> { + #backtrace + } + } + }); + let mut generics = generics.clone(); if !type_params.is_empty() { @@ -66,9 +82,8 @@ pub fn expand( let render = quote! { impl#impl_generics ::std::error::Error for #ident#ty_generics #where_clause { - fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { - #body - } + #source + #backtrace } }; @@ -78,22 +93,22 @@ pub fn expand( fn render_struct( type_params: &HashSet, state: &State, -) -> Result<(HashSet, TokenStream)> { +) -> Result<(HashSet, Option, Option)> { let parsed_fields = parse_fields(&type_params, &state)?; - let render = parsed_fields.render_as_struct(); - let bounds = parsed_fields.bounds; + let source = parsed_fields.render_source_as_struct(); + let backtrace = parsed_fields.render_backtrace_as_struct(); - Ok((bounds, render)) + Ok((parsed_fields.bounds, source, backtrace)) } fn render_enum( type_params: &HashSet, state: &State, -) -> Result<(HashSet, TokenStream)> { +) -> Result<(HashSet, Option, Option)> { let mut bounds = HashSet::new(); - let mut match_arms = Vec::new(); - let mut render_default_wildcard = false; + let mut source_match_arms = Vec::new(); + let mut backtrace_match_arms = Vec::new(); for variant in state.enabled_variant_data().variants { let mut default_info = FullMetaInfo::default(); @@ -111,34 +126,39 @@ fn render_enum( let parsed_fields = parse_fields(&type_params, &state)?; - match parsed_fields.render_as_enum_variant_match_arm() { - Some(match_arm) => { - match_arms.push(match_arm); - } + if let Some(expr) = parsed_fields.render_source_as_enum_variant_match_arm() { + source_match_arms.push(expr); + } - None => { - render_default_wildcard = true; - } + if let Some(expr) = parsed_fields.render_backtrace_as_enum_variant_match_arm() { + backtrace_match_arms.push(expr); } bounds.extend(parsed_fields.bounds.into_iter()); } - if !match_arms.is_empty() && render_default_wildcard { - match_arms.push(quote!(_ => None)); - } + let render = |match_arms: &mut Vec| { + if !match_arms.is_empty() && match_arms.len() < state.variants.len() { + match_arms.push(quote!(_ => None)); + } - let render = if !match_arms.is_empty() { - quote! { - match self { - #(#match_arms),* - } + if !match_arms.is_empty() { + let expr = quote! { + match self { + #(#match_arms),* + } + }; + + Some(expr) + } else { + None } - } else { - quote!(None) }; - Ok((bounds, render)) + let source = render(&mut source_match_arms); + let backtrace = render(&mut backtrace_match_arms); + + Ok((bounds, source, backtrace)) } fn allowed_attr_params() -> AttrParams { @@ -146,13 +166,14 @@ fn allowed_attr_params() -> AttrParams { enum_: vec!["ignore"], struct_: vec!["ignore"], variant: vec!["ignore"], - field: vec!["ignore", "source"], + field: vec!["ignore", "source", "backtrace"], } } struct ParsedFields<'input, 'state> { data: MultiFieldData<'input, 'state>, source: Option, + backtrace: Option, bounds: HashSet, } @@ -161,30 +182,116 @@ impl<'input, 'state> ParsedFields<'input, 'state> { Self { data, source: None, + backtrace: None, bounds: HashSet::new(), } } } impl<'input, 'state> ParsedFields<'input, 'state> { - fn render_as_struct(&self) -> TokenStream { - match self.source { - Some(source) => { - let ident = &self.data.members[source]; - render_some(quote!(&#ident)) - } - None => quote!(None), - } + fn render_source_as_struct(&self) -> Option { + self.source.map(|source| { + let ident = &self.data.members[source]; + render_some(quote!(&#ident)) + }) } - fn render_as_enum_variant_match_arm(&self) -> Option { + fn render_source_as_enum_variant_match_arm(&self) -> Option { self.source.map(|source| { let pattern = self.data.matcher(&[source], &[quote!(source)]); let expr = render_some(quote!(source)); - quote!(#pattern => #expr) }) } + + fn render_backtrace_as_struct(&self) -> Option { + self.render_backtrace_impl( + |fields, index| { + let expr = &fields.data.members[index]; + quote!(#expr) + }, + |fields, index| { + let expr = &fields.data.members[index]; + quote!(&#expr) + }, + false, + ) + .map(|(_, expr)| expr) + } + + fn render_backtrace_as_enum_variant_match_arm(&self) -> Option { + self.render_backtrace_impl( + |_, _| quote!(source), + |_, _| quote!(backtrace), + true, + ) + .map(|(pattern, expr)| quote!(#pattern => #expr)) + } + + fn render_backtrace_impl( + &self, + source_expr: S, + backtrace_expr: B, + is_rendering_enum_match_arm: bool, + ) -> Option<(TokenStream, TokenStream)> + where + S: Fn(&ParsedFields, usize) -> TokenStream, + B: Fn(&ParsedFields, usize) -> TokenStream, + { + // Disable proxying call to `backtrace` to `source` if + // `source` explicitly marked with `#[error(not(backtrace))]`. + let source = self.source.and_then(|source| { + match self.data.infos[source].info.backtrace { + Some(false) => None, + _ => Some(source), + } + }); + + let mut indices = Vec::new(); + let mut bindings = Vec::new(); + + let mut bind = |index: usize, binding: &TokenStream| { + if is_rendering_enum_match_arm { + indices.push(index); + bindings.push(binding.clone()); + } + }; + + let expr = match (source, self.backtrace) { + (Some(source), backtrace) => { + let source_expr = source_expr(self, source); + bind(source, &source_expr); + + let backtrace_expr = backtrace + .filter(|backtrace| source != *backtrace) + .map(|backtrace| { + let backtrace_expr = backtrace_expr(self, backtrace); + bind(backtrace, &backtrace_expr); + + quote!(.or_else(|| Some(#backtrace_expr))) + }); + + quote!(#source_expr.backtrace()#backtrace_expr) + } + + (None, Some(backtrace)) => { + let backtrace_expr = backtrace_expr(self, backtrace); + bind(backtrace, &backtrace_expr); + + quote!(Some(#backtrace_expr)) + } + + _ => return None, + }; + + let pattern = if is_rendering_enum_match_arm { + self.data.matcher(&indices, &bindings) + } else { + TokenStream::new() + }; + + Some((pattern, expr)) + } } fn render_some(expr: T) -> TokenStream @@ -200,29 +307,59 @@ fn parse_fields<'input, 'state>( ) -> Result> { match state.derive_type { DeriveType::Named => { - parse_fields_impl(type_params, state, |field, _| { - let ident = field - .ident - .as_ref() - .expect("Internal error in macro implementation"); // TODO - - ident == "source" + parse_fields_impl(type_params, state, |attr, field, _| { + // Unwrapping is safe, cause fields in named struct + // always have an ident + let ident = field.ident.as_ref().unwrap(); + + match attr { + "source" => ident == "source", + "backtrace" => { + ident == "backtrace" + || is_type_ends_with(&field.ty, "Backtrace") + } + _ => unreachable!(), + } }) } - DeriveType::Unnamed => parse_fields_impl(type_params, state, |_, len| len == 1), + DeriveType::Unnamed => { + parse_fields_impl(type_params, state, |attr, field, len| match attr { + "source" => len == 1, + "backtrace" => is_type_ends_with(&field.ty, "Backtrace"), + _ => unreachable!(), + }) + } _ => unreachable!(), // TODO } } +fn is_type_ends_with(ty: &syn::Type, tail: &str) -> bool { + let ty = match ty { + syn::Type::Path(ty) => ty, + _ => return false, + }; + + // Unwrapping is safe, cause 'syn::TypePath.path.segments' + // have to have at least one segment + let ty = ty.path.segments.last().unwrap(); + + match ty.arguments { + syn::PathArguments::None => (), + _ => return false, + }; + + ty.ident == tail +} + fn parse_fields_impl<'input, 'state, P>( type_params: &HashSet, state: &'state State<'input>, is_valid_default_field_for_attr: P, ) -> Result> where - P: Fn(&syn::Field, usize) -> bool, + P: Fn(&str, &syn::Field, usize) -> bool, { let MultiFieldData { fields, infos, .. } = state.enabled_fields_data(); @@ -232,43 +369,87 @@ where .enumerate() .map(|(index, (field, info))| (index, *field, info)); - let explicit_sources = iter.clone().filter(|(_, _, info)| match info.source { + let mut source = None; + parse_field_impl( + &is_valid_default_field_for_attr, + state.fields.len(), + iter.clone(), + "source", + |info| info.source, + &mut source, + )?; + + let mut backtrace = None; + parse_field_impl( + &is_valid_default_field_for_attr, + state.fields.len(), + iter.clone(), + "backtrace", + |info| info.backtrace, + &mut backtrace, + )?; + + let mut parsed_fields = ParsedFields::new(state.enabled_fields_data()); + + if let Some((index, field, _)) = source { + parsed_fields.source = Some(index); + add_bound_if_type_parameter_used_in_type( + &mut parsed_fields.bounds, + type_params, + &field.ty, + ); + } + + if let Some((index, _, _)) = backtrace { + parsed_fields.backtrace = Some(index); + } + + Ok(parsed_fields) +} + +fn parse_field_impl<'a, P, V>( + is_valid_default_field_for_attr: &P, + len: usize, + iter: impl Iterator + Clone, + attr: &str, + value: V, + out: &mut Option<(usize, &'a syn::Field, &'a MetaInfo)>, +) -> Result<()> +where + P: Fn(&str, &syn::Field, usize) -> bool, + V: Fn(&MetaInfo) -> Option, +{ + let explicit_fields = iter.clone().filter(|(_, _, info)| match value(info) { Some(true) => true, _ => false, }); - let inferred_sources = iter.filter(|(_, field, info)| match info.source { - None => is_valid_default_field_for_attr(field, fields.len()), + let inferred_fields = iter.filter(|(_, field, info)| match value(info) { + None => is_valid_default_field_for_attr(attr, field, len), _ => false, }); - let source = assert_iter_contains_zero_or_one_item( - explicit_sources, - "Multiple `source` attributes specified. \ - Single attribute per struct/enum variant allowed.", + let field = assert_iter_contains_zero_or_one_item( + explicit_fields, + &format!( + "Multiple `{}` attributes specified. \ + Single attribute per struct/enum variant allowed.", + attr + ), )?; - let source = match source { - source @ Some(_) => source, + let field = match field { + field @ Some(_) => field, None => assert_iter_contains_zero_or_one_item( - inferred_sources, + inferred_fields, "Conflicting fields found. Consider specifying some \ `#[error(...)]` attributes to resolve conflict.", )?, }; - let mut parsed_fields = ParsedFields::new(state.enabled_fields_data()); + *out = field; - if let Some((index, field, _)) = source { - parsed_fields.source = Some(index); - add_bound_if_type_parameter_used_in_type( - &mut parsed_fields.bounds, - type_params, - &field.ty, - ); - } - - Ok(parsed_fields) + Ok(()) } fn assert_iter_contains_zero_or_one_item<'a>( diff --git a/src/utils.rs b/src/utils.rs index ba2413f3..08344fc9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -381,10 +381,16 @@ impl<'input> State<'input> { .filter_map(|info| info.enabled.map(|_| info)) .next(); + let default_enabled = if trait_name == "Error" { + true + } else { + first_match.map_or(true, |info| !info.enabled.unwrap()) + }; + let defaults = struct_meta_info.to_full(FullMetaInfo { // Default to enabled true, except when first attribute has explicit // enabling - enabled: first_match.map_or(true, |info| !info.enabled.unwrap()), + enabled: default_enabled, forward: false, // Default to owned true, except when first attribute has one of owned, // ref or ref_mut @@ -888,6 +894,8 @@ fn parse_punctuated_nested_meta( info.ref_mut = Some(value); } else if path.is_ident("source") { info.source = Some(value); + } else if path.is_ident("backtrace") { + info.backtrace = Some(value); } else { return Err(Error::new( meta.span(), @@ -923,6 +931,7 @@ pub struct MetaInfo { pub ref_: Option, pub ref_mut: Option, pub source: Option, + pub backtrace: Option, } impl MetaInfo { diff --git a/tests/error.rs b/tests/error.rs deleted file mode 100644 index 891ea5ed..00000000 --- a/tests/error.rs +++ /dev/null @@ -1,1094 +0,0 @@ -#[macro_use] -extern crate derive_more; - -use std::error::Error as _; - -macro_rules! derive_display { - (@fmt) => { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "") - } - }; - ($type:ident) => { - impl ::std::fmt::Display for $type { - derive_display! {@fmt} - } - }; - ($type:ident, $($type_parameters:ident),*) => { - impl<$($type_parameters),*> ::std::fmt::Display for $type<$($type_parameters),*> { - derive_display! {@fmt} - } - }; -} - -mod derives_for_struct { - use super::*; - - #[derive(Default, Debug, Error)] - struct SimpleErr; - - derive_display! {SimpleErr} - - #[test] - fn unit() { - assert!(SimpleErr.source().is_none()); - } - - #[test] - fn named_implicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn named_implicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - source: SimpleErr, - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_some()); - assert!(TestErr::default().source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(not(source))] - source: SimpleErr, - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn named_explicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(source)] - explicit_source: SimpleErr, - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_some()); - assert!(TestErr::default().source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(not(source))] - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn named_explicit_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(source)] - source: SimpleErr, - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_some()); - assert!(TestErr::default().source().unwrap().is::()); - } - - #[test] - fn named_explicit_suppresses_implicit() { - #[derive(Default, Debug, Error)] - struct TestErr { - source: i32, - #[error(source)] - field: SimpleErr, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_some()); - assert!(TestErr::default().source().unwrap().is::()); - } - - #[test] - fn unnamed_implicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr(i32, i32); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn unnamed_implicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr(SimpleErr); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_some()); - assert!(TestErr::default().source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(not(source))] SimpleErr); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn unnamed_explicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] SimpleErr, i32); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_some()); - assert!(TestErr::default().source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(not(source))] i32, #[error(not(source))] i32); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn unnamed_explicit_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] SimpleErr); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_some()); - assert!(TestErr::default().source().unwrap().is::()); - } - - #[test] - fn named_ignore() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(ignore)] - source: SimpleErr, - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn unnamed_ignore() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(ignore)] SimpleErr); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn named_ignore_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(ignore)] - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn unnamed_ignore_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(ignore)] i32, #[error(ignore)] i32); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()); - } - - #[test] - fn named_struct_ignore() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr { - source: SimpleErr, - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()) - } - - #[test] - fn unnamed_struct_ignore() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr(SimpleErr); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()) - } - - #[test] - fn named_struct_ignore_redundant() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr { - field: i32, - } - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()) - } - - #[test] - fn unnamed_struct_ignore_redundant() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr(i32, i32); - - derive_display! {TestErr} - - assert!(TestErr::default().source().is_none()) - } -} - -mod derives_for_enum { - use super::*; - - #[derive(Default, Debug, Error)] - struct SimpleErr; - - derive_display! {SimpleErr} - - #[derive(Debug, Error)] - enum TestErr { - Unit, - NamedImplicitNoSource { - field: i32, - }, - NamedImplicitSource { - source: SimpleErr, - field: i32, - }, - NamedExplicitNoSource { - #[error(not(source))] - source: SimpleErr, - field: i32, - }, - NamedExplicitSource { - #[error(source)] - explicit_source: SimpleErr, - field: i32, - }, - NamedExplicitNoSourceRedundant { - #[error(not(source))] - field: i32, - }, - NamedExplicitSourceRedundant { - #[error(source)] - source: SimpleErr, - field: i32, - }, - NamedExplicitSuppressesImplicit { - source: i32, - #[error(source)] - field: SimpleErr, - }, - UnnamedImplicitNoSource(i32, i32), - UnnamedImplicitSource(SimpleErr), - UnnamedExplicitNoSource(#[error(not(source))] SimpleErr), - UnnamedExplicitSource(#[error(source)] SimpleErr, i32), - UnnamedExplicitNoSourceRedundant( - #[error(not(source))] i32, - #[error(not(source))] i32, - ), - UnnamedExplicitSourceRedundant(#[error(source)] SimpleErr), - NamedIgnore { - #[error(ignore)] - source: SimpleErr, - field: i32, - }, - UnnamedIgnore(#[error(ignore)] SimpleErr), - NamedIgnoreRedundant { - #[error(ignore)] - field: i32, - }, - UnnamedIgnoreRedundant(#[error(ignore)] i32, #[error(ignore)] i32), - #[error(ignore)] - NamedVariantIgnore { - source: SimpleErr, - field: i32, - }, - #[error(ignore)] - UnnamedVariantIgnore(SimpleErr), - #[error(ignore)] - NamedVariantIgnoreRedundant { - field: i32, - }, - #[error(ignore)] - UnnamedVariantIgnoreRedundant(i32, i32), - } - - derive_display! {TestErr} - - #[test] - fn unit() { - assert!(TestErr::Unit.source().is_none()); - } - - #[test] - fn named_implicit_no_source() { - let err = TestErr::NamedImplicitNoSource { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn named_implicit_source() { - let err = TestErr::NamedImplicitSource { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source() { - let err = TestErr::NamedExplicitNoSource { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_none()); - } - - #[test] - fn named_explicit_source() { - let err = TestErr::NamedExplicitSource { - explicit_source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source_redundant() { - let err = TestErr::NamedExplicitNoSourceRedundant { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn named_explicit_source_redundant() { - let err = TestErr::NamedExplicitSourceRedundant { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_suppresses_implicit() { - let err = TestErr::NamedExplicitSuppressesImplicit { - source: 0, - field: SimpleErr::default(), - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_implicit_no_source() { - assert!(TestErr::UnnamedImplicitNoSource(0, 0).source().is_none()); - } - - #[test] - fn unnamed_implicit_source() { - let err = TestErr::UnnamedImplicitSource(SimpleErr::default()); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source() { - let err = TestErr::UnnamedExplicitNoSource(SimpleErr::default()); - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_explicit_source() { - let err = TestErr::UnnamedExplicitSource(SimpleErr::default(), 0); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source_redundant() { - let err = TestErr::UnnamedExplicitNoSourceRedundant(0, 0); - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_explicit_source_redundant() { - let err = TestErr::UnnamedExplicitSourceRedundant(SimpleErr::default()); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_ignore() { - let err = TestErr::NamedIgnore { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_ignore() { - let err = TestErr::UnnamedIgnore(SimpleErr::default()); - - assert!(err.source().is_none()); - } - - #[test] - fn named_ignore_redundant() { - let err = TestErr::NamedIgnoreRedundant { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_ignore_redundant() { - let err = TestErr::UnnamedIgnoreRedundant(0, 0); - - assert!(err.source().is_none()); - } - - #[test] - fn named_variant_ignore() { - let err = TestErr::NamedVariantIgnore { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_variant_ignore() { - let err = TestErr::UnnamedVariantIgnore(SimpleErr::default()); - - assert!(err.source().is_none()) - } - - #[test] - fn named_variant_ignore_redundant() { - let err = TestErr::NamedVariantIgnoreRedundant { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_variant_ignore_redundant() { - let err = TestErr::UnnamedVariantIgnoreRedundant(0, 0); - - assert!(err.source().is_none()) - } -} - -mod derives_for_generic_struct { - use super::*; - - #[derive(Default, Debug, Error)] - struct SimpleErr; - - derive_display! {SimpleErr} - - #[test] - fn named_implicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - field: T, - } - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn named_implicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - source: E, - field: T, - } - - derive_display! {TestErr, E, T} - - let err = TestErr::::default(); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(not(source))] - source: E, - field: T, - } - - derive_display! {TestErr, E, T} - - let err = TestErr::::default(); - - assert!(err.source().is_none()); - } - - #[test] - fn named_explicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(source)] - explicit_source: E, - field: T, - } - - derive_display! {TestErr, E, T} - - let err = TestErr::::default(); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(not(source))] - field: T, - } - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn named_explicit_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(source)] - source: E, - field: T, - } - - derive_display! {TestErr, E, T} - - let err = TestErr::::default(); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_suppresses_implicit() { - #[derive(Default, Debug, Error)] - struct TestErr { - source: E, - #[error(source)] - field: T, - } - - derive_display! {TestErr, E, T} - - let err = TestErr::::default(); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_implicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr(T, T); - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn unnamed_implicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr(E); - - derive_display! {TestErr, E} - - let err = TestErr::::default(); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(not(source))] E); - - derive_display! {TestErr, E} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn unnamed_explicit_source() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] E, T); - - derive_display! {TestErr, E, T} - - let err = TestErr::::default(); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(not(source))] T, #[error(not(source))] T); - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn unnamed_explicit_source_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] E); - - derive_display! {TestErr, E} - - let err = TestErr::::default(); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_ignore() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(ignore)] - source: E, - field: T, - } - - derive_display! {TestErr, E, T} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn unnamed_ignore() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(ignore)] E); - - derive_display! {TestErr, E} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn named_ignore_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(ignore)] - field: T, - } - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn unnamed_ignore_redundant() { - #[derive(Default, Debug, Error)] - struct TestErr(#[error(ignore)] T, #[error(ignore)] T); - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()); - } - - #[test] - fn named_struct_ignore() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr { - source: E, - field: T, - } - - derive_display! {TestErr, E, T} - - assert!(TestErr::::default().source().is_none()) - } - - #[test] - fn unnamed_struct_ignore() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr(E); - - derive_display! {TestErr, E} - - assert!(TestErr::::default().source().is_none()) - } - - #[test] - fn named_struct_ignore_redundant() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr { - field: T, - } - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()) - } - - #[test] - fn unnamed_struct_ignore_redundant() { - #[derive(Default, Debug, Error)] - #[error(ignore)] - struct TestErr(T, T); - - derive_display! {TestErr, T} - - assert!(TestErr::::default().source().is_none()) - } -} - -mod derives_for_generic_enum { - use super::*; - - #[derive(Default, Debug, Error)] - struct SimpleErr; - - derive_display! {SimpleErr} - - #[derive(Debug, Error)] - enum TestErr { - Unit, - NamedImplicitNoSource { - field: T, - }, - NamedImplicitSource { - source: E, - field: T, - }, - NamedExplicitNoSource { - #[error(not(source))] - source: E, - field: T, - }, - NamedExplicitSource { - #[error(source)] - explicit_source: E, - field: T, - }, - NamedExplicitNoSourceRedundant { - #[error(not(source))] - field: T, - }, - NamedExplicitSourceRedundant { - #[error(source)] - source: E, - field: T, - }, - NamedExplicitSuppressesImplicit { - source: T, - #[error(source)] - field: E, - }, - UnnamedImplicitNoSource(T, T), - UnnamedImplicitSource(E), - UnnamedExplicitNoSource(#[error(not(source))] E), - UnnamedExplicitSource(#[error(source)] E, T), - UnnamedExplicitNoSourceRedundant( - #[error(not(source))] T, - #[error(not(source))] T, - ), - UnnamedExplicitSourceRedundant(#[error(source)] E), - NamedIgnore { - #[error(ignore)] - source: E, - field: T, - }, - UnnamedIgnore(#[error(ignore)] E), - NamedIgnoreRedundant { - #[error(ignore)] - field: T, - }, - UnnamedIgnoreRedundant(#[error(ignore)] T, #[error(ignore)] T), - #[error(ignore)] - NamedVariantIgnore { - source: E, - field: T, - }, - #[error(ignore)] - UnnamedVariantIgnore(E), - #[error(ignore)] - NamedVariantIgnoreRedundant { - field: T, - }, - #[error(ignore)] - UnnamedVariantIgnoreRedundant(T, T), - } - - derive_display! {TestErr, T, E} - - #[test] - fn unit() { - assert!(TestErr::::Unit.source().is_none()); - } - - #[test] - fn named_implicit_no_source() { - let err = TestErr::::NamedImplicitNoSource { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn named_implicit_source() { - let err = TestErr::NamedImplicitSource { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source() { - let err = TestErr::NamedExplicitNoSource { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_none()); - } - - #[test] - fn named_explicit_source() { - let err = TestErr::NamedExplicitSource { - explicit_source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_no_source_redundant() { - let err = TestErr::::NamedExplicitNoSourceRedundant { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn named_explicit_source_redundant() { - let err = TestErr::NamedExplicitSourceRedundant { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_explicit_suppresses_implicit() { - let err = TestErr::NamedExplicitSuppressesImplicit { - source: 0, - field: SimpleErr::default(), - }; - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_implicit_no_source() { - let err = TestErr::::UnnamedImplicitNoSource(0, 0); - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_implicit_source() { - let err = TestErr::<_, i32>::UnnamedImplicitSource(SimpleErr::default()); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source() { - let err = TestErr::<_, i32>::UnnamedExplicitNoSource(SimpleErr::default()); - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_explicit_source() { - let err = TestErr::UnnamedExplicitSource(SimpleErr::default(), 0); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn unnamed_explicit_no_source_redundant() { - let err = TestErr::::UnnamedExplicitNoSourceRedundant(0, 0); - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_explicit_source_redundant() { - let err = - TestErr::<_, i32>::UnnamedExplicitSourceRedundant(SimpleErr::default()); - - assert!(err.source().is_some()); - assert!(err.source().unwrap().is::()); - } - - #[test] - fn named_ignore() { - let err = TestErr::NamedIgnore { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_ignore() { - let err = TestErr::<_, i32>::UnnamedIgnore(SimpleErr::default()); - - assert!(err.source().is_none()); - } - - #[test] - fn named_ignore_redundant() { - let err = TestErr::::NamedIgnoreRedundant { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_ignore_redundant() { - let err = TestErr::::UnnamedIgnoreRedundant(0, 0); - - assert!(err.source().is_none()); - } - - #[test] - fn named_variant_ignore() { - let err = TestErr::NamedVariantIgnore { - source: SimpleErr::default(), - field: 0, - }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_variant_ignore() { - let err = TestErr::<_, i32>::UnnamedVariantIgnore(SimpleErr::default()); - - assert!(err.source().is_none()) - } - - #[test] - fn named_variant_ignore_redundant() { - let err = TestErr::::NamedVariantIgnoreRedundant { field: 0 }; - - assert!(err.source().is_none()); - } - - #[test] - fn unnamed_variant_ignore_redundant() { - let err = TestErr::::UnnamedVariantIgnoreRedundant(0, 0); - - assert!(err.source().is_none()) - } -} diff --git a/tests/error/derives_for_enums_with_backtrace.rs b/tests/error/derives_for_enums_with_backtrace.rs new file mode 100644 index 00000000..89155d02 --- /dev/null +++ b/tests/error/derives_for_enums_with_backtrace.rs @@ -0,0 +1,271 @@ +use super::*; + +derive_display!(TestErr); +#[derive(Debug, Error)] +enum TestErr { + Unit, + NamedImplicitNoBacktrace { + field: i32, + }, + NamedImplicitBacktraceByFieldName { + backtrace: MyBacktrace, + field: i32, + }, + NamedImplicitBacktraceByFieldType { + implicit_backtrace: Backtrace, + field: i32, + }, + NamedExplicitNoBacktraceByFieldName { + #[error(not(backtrace))] + backtrace: MyBacktrace, + field: i32, + }, + NamedExplicitNoBacktraceByFieldType { + #[error(not(backtrace))] + implicit_backtrace: Backtrace, + field: i32, + }, + NamedExplicitBacktrace { + #[error(backtrace)] + explicit_backtrace: MyBacktrace, + field: i32, + }, + NamedExplicitNoBacktraceRedundant { + #[error(not(backtrace))] + not_backtrace: MyBacktrace, + #[error(not(backtrace))] + field: i32, + }, + NamedExplicitBacktraceByFieldNameRedundant { + #[error(backtrace)] + backtrace: MyBacktrace, + field: i32, + }, + NamedExplicitBacktraceByFieldTypeRedundant { + #[error(backtrace)] + implicit_backtrace: Backtrace, + field: i32, + }, + NamedExplicitSupressesImplicit { + #[error(backtrace)] + not_backtrace: MyBacktrace, + backtrace: Backtrace, + field: i32, + }, + UnnamedImplicitNoBacktrace(i32, i32), + UnnamedImplicitBacktrace(Backtrace, i32), + UnnamedExplicitNoBacktrace(#[error(not(backtrace))] Backtrace, i32), + UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, i32), + UnnamedExplicitNoBacktraceRedundant( + #[error(not(backtrace))] MyBacktrace, + #[error(not(backtrace))] i32, + ), + UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, i32), + UnnamedExplicitSupressesImplicit(#[error(backtrace)] MyBacktrace, Backtrace, i32), +} + +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::NamedExplicitBacktraceByFieldTypeRedundant { + implicit_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, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + } + } + + fn get_unused_backtrace(&self) -> &Backtrace { + match self { + Self::NamedExplicitSupressesImplicit { backtrace, .. } => backtrace, + Self::UnnamedExplicitSupressesImplicit(_, backtrace, _) => backtrace, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + } + } +} + +type MyBacktrace = Backtrace; + +#[test] +fn unit() { + assert!(TestErr::Unit.backtrace().is_none()); +} + +#[test] +fn named_implicit_no_backtrace() { + let err = TestErr::NamedImplicitNoBacktrace { field: 0 }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_implicit_backtrace_by_field_name() { + let err = TestErr::NamedImplicitBacktraceByFieldName { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_implicit_backtrace_by_field_type() { + let err = TestErr::NamedImplicitBacktraceByFieldType { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_by_field_name() { + let err = TestErr::NamedExplicitNoBacktraceByFieldName { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_explicit_no_backtrace_by_field_type() { + let err = TestErr::NamedExplicitNoBacktraceByFieldType { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_explicit_backtrace() { + let err = TestErr::NamedExplicitBacktrace { + explicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_redundant() { + let err = TestErr::NamedExplicitNoBacktraceRedundant { + not_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_explicit_backtrace_by_field_name_redundant() { + let err = TestErr::NamedExplicitBacktraceByFieldNameRedundant { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_backtrace_by_field_type_redundant() { + let err = TestErr::NamedExplicitBacktraceByFieldTypeRedundant { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_supresses_implicit() { + let err = TestErr::NamedExplicitSupressesImplicit { + not_backtrace: Backtrace::force_capture(), + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_unused_backtrace); +} + +#[test] +fn unnamed_implicit_no_backtrace() { + let err = TestErr::UnnamedImplicitNoBacktrace(0, 0); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_implicit_backtrace() { + let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_explicit_no_backtrace() { + let err = TestErr::UnnamedExplicitNoBacktrace(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace() { + let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_explicit_no_backtrace_redundant() { + let err = + TestErr::UnnamedExplicitNoBacktraceRedundant(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_redundant() { + let err = TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_explicit_supresses_implicit() { + let err = TestErr::UnnamedExplicitSupressesImplicit( + Backtrace::force_capture(), + (|| Backtrace::force_capture())(), // ensure backtraces are different + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_unused_backtrace); +} diff --git a/tests/error/derives_for_enums_with_source.rs b/tests/error/derives_for_enums_with_source.rs new file mode 100644 index 00000000..2513ab60 --- /dev/null +++ b/tests/error/derives_for_enums_with_source.rs @@ -0,0 +1,249 @@ +use super::*; + +derive_display!(TestErr); +#[derive(Debug, Error)] +enum TestErr { + Unit, + NamedImplicitNoSource { + field: i32, + }, + NamedImplicitSource { + source: SimpleErr, + field: i32, + }, + NamedExplicitNoSource { + #[error(not(source))] + source: SimpleErr, + field: i32, + }, + NamedExplicitSource { + #[error(source)] + explicit_source: SimpleErr, + field: i32, + }, + NamedExplicitNoSourceRedundant { + #[error(not(source))] + field: i32, + }, + NamedExplicitSourceRedundant { + #[error(source)] + source: SimpleErr, + field: i32, + }, + NamedExplicitSuppressesImplicit { + source: i32, + #[error(source)] + field: SimpleErr, + }, + UnnamedImplicitNoSource(i32, i32), + UnnamedImplicitSource(SimpleErr), + UnnamedExplicitNoSource(#[error(not(source))] SimpleErr), + UnnamedExplicitSource(#[error(source)] SimpleErr, i32), + UnnamedExplicitNoSourceRedundant( + #[error(not(source))] i32, + #[error(not(source))] i32, + ), + UnnamedExplicitSourceRedundant(#[error(source)] SimpleErr), + NamedIgnore { + #[error(ignore)] + source: SimpleErr, + field: i32, + }, + UnnamedIgnore(#[error(ignore)] SimpleErr), + NamedIgnoreRedundant { + #[error(ignore)] + field: i32, + }, + UnnamedIgnoreRedundant(#[error(ignore)] i32, #[error(ignore)] i32), + #[error(ignore)] + NamedVariantIgnore { + source: SimpleErr, + field: i32, + }, + #[error(ignore)] + UnnamedVariantIgnore(SimpleErr), + #[error(ignore)] + NamedVariantIgnoreRedundant { + field: i32, + }, + #[error(ignore)] + UnnamedVariantIgnoreRedundant(i32, i32), +} + +#[test] +fn unit() { + assert!(TestErr::Unit.source().is_none()); +} + +#[test] +fn named_implicit_no_source() { + let err = TestErr::NamedImplicitNoSource { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn named_implicit_source() { + let err = TestErr::NamedImplicitSource { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source() { + let err = TestErr::NamedExplicitNoSource { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_none()); +} + +#[test] +fn named_explicit_source() { + let err = TestErr::NamedExplicitSource { + explicit_source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source_redundant() { + let err = TestErr::NamedExplicitNoSourceRedundant { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn named_explicit_source_redundant() { + let err = TestErr::NamedExplicitSourceRedundant { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_suppresses_implicit() { + let err = TestErr::NamedExplicitSuppressesImplicit { + source: 0, + field: SimpleErr, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_implicit_no_source() { + assert!(TestErr::UnnamedImplicitNoSource(0, 0).source().is_none()); +} + +#[test] +fn unnamed_implicit_source() { + let err = TestErr::UnnamedImplicitSource(SimpleErr); + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source() { + let err = TestErr::UnnamedExplicitNoSource(SimpleErr); + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_explicit_source() { + let err = TestErr::UnnamedExplicitSource(SimpleErr, 0); + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source_redundant() { + let err = TestErr::UnnamedExplicitNoSourceRedundant(0, 0); + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_explicit_source_redundant() { + let err = TestErr::UnnamedExplicitSourceRedundant(SimpleErr); + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_ignore() { + let err = TestErr::NamedIgnore { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_ignore() { + let err = TestErr::UnnamedIgnore(SimpleErr); + + assert!(err.source().is_none()); +} + +#[test] +fn named_ignore_redundant() { + let err = TestErr::NamedIgnoreRedundant { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_ignore_redundant() { + let err = TestErr::UnnamedIgnoreRedundant(0, 0); + + assert!(err.source().is_none()); +} + +#[test] +fn named_variant_ignore() { + let err = TestErr::NamedVariantIgnore { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_variant_ignore() { + let err = TestErr::UnnamedVariantIgnore(SimpleErr); + + assert!(err.source().is_none()) +} + +#[test] +fn named_variant_ignore_redundant() { + let err = TestErr::NamedVariantIgnoreRedundant { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_variant_ignore_redundant() { + let err = TestErr::UnnamedVariantIgnoreRedundant(0, 0); + + assert!(err.source().is_none()) +} diff --git a/tests/error/derives_for_enums_with_source_backtrace_chaining.rs b/tests/error/derives_for_enums_with_source_backtrace_chaining.rs new file mode 100644 index 00000000..394a61b7 --- /dev/null +++ b/tests/error/derives_for_enums_with_source_backtrace_chaining.rs @@ -0,0 +1,240 @@ +use super::*; + +derive_display!(TestErr); +#[derive(Debug, Error)] +enum TestErr { + NamedNoBacktraceNoSource { + field: i32, + }, + NamedNoBacktraceSourceWithoutBacktrace { + source: SimpleErr, + field: i32, + }, + NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { + #[error(not(backtrace))] + source: BacktraceErr, + field: i32, + }, + NamedNoBacktraceSourceWithBacktrace { + source: BacktraceErr, + field: i32, + }, + NamedBacktraceNoSource { + backtrace: Backtrace, + field: i32, + }, + NamedBacktraceSourceWithoutBacktrace { + source: SimpleErr, + backtrace: Backtrace, + field: i32, + }, + NamedBacktraceSourceWithBacktraceExplicitlyDisabled { + #[error(not(backtrace))] + source: BacktraceErr, + backtrace: Backtrace, + field: i32, + }, + NamedBacktraceSourceWithBacktrace { + source: BacktraceErr, + backtrace: Backtrace, + field: i32, + }, + UnnamedNoBacktraceNoSource(i32, i32), + UnnamedNoBacktraceSourceWithoutBacktrace(#[error(source)] SimpleErr, i32), + UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( + #[error(source, not(backtrace))] BacktraceErr, + i32, + ), + UnnamedNoBacktraceSourceWithBacktrace(#[error(source)] BacktraceErr, i32), + UnnamedBacktraceNoSource(Backtrace, i32), + UnnamedBacktraceSourceWithoutBacktrace(#[error(source)] SimpleErr, Backtrace, i32), + UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + #[error(source, not(backtrace))] BacktraceErr, + Backtrace, + i32, + ), + UnnamedBacktraceSourceWithBacktrace(#[error(source)] BacktraceErr, Backtrace, i32), +} + +impl TestErr { + fn get_stored_backtrace(&self) -> &Backtrace { + match self { + TestErr::NamedBacktraceSourceWithoutBacktrace { backtrace, .. } => { + backtrace + } + TestErr::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { + backtrace, + .. + } => backtrace, + TestErr::NamedBacktraceSourceWithBacktrace { backtrace, .. } => backtrace, + TestErr::UnnamedBacktraceSourceWithoutBacktrace(_, backtrace, _) => { + backtrace + } + TestErr::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + _, + backtrace, + _, + ) => backtrace, + TestErr::UnnamedBacktraceSourceWithBacktrace(_, backtrace, _) => backtrace, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + } + } +} + +#[test] +fn named_no_backtrace_no_source() { + let err = TestErr::NamedNoBacktraceNoSource { field: 0 }; + + assert!(err.backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_without_backtrace() { + let err = TestErr::NamedNoBacktraceSourceWithoutBacktrace { + source: SimpleErr, + field: 0, + }; + + assert!(err.backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { + let err = TestErr::NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { + source: BacktraceErr::default(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_no_backtrace_source_with_backtrace() { + let err = TestErr::NamedNoBacktraceSourceWithBacktrace { + source: BacktraceErr::default(), + field: 0, + }; + + assert!(err.backtrace().is_some()); +} + +#[test] +fn named_backtrace_no_source() { + let err = TestErr::NamedBacktraceNoSource { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); +} + +#[test] +fn named_backtrace_source_without_backtrace() { + let err = TestErr::NamedBacktraceSourceWithoutBacktrace { + source: SimpleErr, + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_backtrace_source_with_backtrace_explicitly_disabled() { + let err = TestErr::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_backtrace_source_with_backtrace() { + let err = TestErr::NamedBacktraceSourceWithBacktrace { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_no_backtrace_no_source() { + let err = TestErr::UnnamedNoBacktraceNoSource(0, 0); + + assert!(err.backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_without_backtrace() { + let err = TestErr::UnnamedNoBacktraceSourceWithoutBacktrace(SimpleErr, 0); + + assert!(err.backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { + let err = TestErr::UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( + BacktraceErr::default(), + 0, + ); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace() { + let err = + TestErr::UnnamedNoBacktraceSourceWithBacktrace(BacktraceErr::default(), 0); + + assert!(err.backtrace().is_some()); +} + +#[test] +fn unnamed_backtrace_no_source() { + let err = TestErr::UnnamedBacktraceNoSource(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_some()); +} + +#[test] +fn unnamed_backtrace_source_without_backtrace() { + let err = TestErr::UnnamedBacktraceSourceWithoutBacktrace( + SimpleErr, + Backtrace::force_capture(), + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace_explictily_disabled() { + let err = TestErr::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + BacktraceErr::default(), + Backtrace::force_capture(), + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace() { + let err = TestErr::UnnamedBacktraceSourceWithBacktrace( + BacktraceErr::default(), + Backtrace::force_capture(), + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, .get_stored_backtrace); +} diff --git a/tests/error/derives_for_generic_enums_with_backtrace.rs b/tests/error/derives_for_generic_enums_with_backtrace.rs new file mode 100644 index 00000000..1eec54c5 --- /dev/null +++ b/tests/error/derives_for_generic_enums_with_backtrace.rs @@ -0,0 +1,271 @@ +use super::*; + +derive_display!(TestErr, T); +#[derive(Debug, Error)] +enum TestErr { + Unit, + NamedImplicitNoBacktrace { + field: T, + }, + NamedImplicitBacktraceByFieldName { + backtrace: MyBacktrace, + field: T, + }, + NamedImplicitBacktraceByFieldType { + implicit_backtrace: Backtrace, + field: T, + }, + NamedExplicitNoBacktraceByFieldName { + #[error(not(backtrace))] + backtrace: MyBacktrace, + field: T, + }, + NamedExplicitNoBacktraceByFieldType { + #[error(not(backtrace))] + implicit_backtrace: Backtrace, + field: T, + }, + NamedExplicitBacktrace { + #[error(backtrace)] + explicit_backtrace: MyBacktrace, + field: T, + }, + NamedExplicitNoBacktraceRedundant { + #[error(not(backtrace))] + not_backtrace: MyBacktrace, + #[error(not(backtrace))] + field: T, + }, + NamedExplicitBacktraceByFieldNameRedundant { + #[error(backtrace)] + backtrace: MyBacktrace, + field: T, + }, + NamedExplicitBacktraceByFieldTypeRedundant { + #[error(backtrace)] + implicit_backtrace: Backtrace, + field: T, + }, + NamedExplicitSupressesImplicit { + #[error(backtrace)] + not_backtrace: MyBacktrace, + backtrace: Backtrace, + field: T, + }, + UnnamedImplicitNoBacktrace(T, T), + UnnamedImplicitBacktrace(Backtrace, T), + UnnamedExplicitNoBacktrace(#[error(not(backtrace))] Backtrace, T), + UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, T), + UnnamedExplicitNoBacktraceRedundant( + #[error(not(backtrace))] MyBacktrace, + #[error(not(backtrace))] T, + ), + UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, T), + UnnamedExplicitSupressesImplicit(#[error(backtrace)] MyBacktrace, Backtrace, T), +} + +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::NamedExplicitBacktraceByFieldTypeRedundant { + implicit_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, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + } + } + + fn get_unused_backtrace(&self) -> &Backtrace { + match self { + Self::NamedExplicitSupressesImplicit { backtrace, .. } => backtrace, + Self::UnnamedExplicitSupressesImplicit(_, backtrace, _) => backtrace, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + } + } +} + +type MyBacktrace = Backtrace; + +#[test] +fn unit() { + assert!(TestErr::::Unit.backtrace().is_none()); +} + +#[test] +fn named_implicit_no_backtrace() { + let err = TestErr::NamedImplicitNoBacktrace { field: 0 }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_implicit_backtrace_by_field_name() { + let err = TestErr::NamedImplicitBacktraceByFieldName { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_implicit_backtrace_by_field_type() { + let err = TestErr::NamedImplicitBacktraceByFieldType { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_by_field_name() { + let err = TestErr::NamedExplicitNoBacktraceByFieldName { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_explicit_no_backtrace_by_field_type() { + let err = TestErr::NamedExplicitNoBacktraceByFieldType { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_explicit_backtrace() { + let err = TestErr::NamedExplicitBacktrace { + explicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_redundant() { + let err = TestErr::NamedExplicitNoBacktraceRedundant { + not_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_explicit_backtrace_by_field_name_redundant() { + let err = TestErr::NamedExplicitBacktraceByFieldNameRedundant { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_backtrace_by_field_type_redundant() { + let err = TestErr::NamedExplicitBacktraceByFieldTypeRedundant { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_explicit_supresses_implicit() { + let err = TestErr::NamedExplicitSupressesImplicit { + not_backtrace: Backtrace::force_capture(), + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_unused_backtrace); +} + +#[test] +fn unnamed_implicit_no_backtrace() { + let err = TestErr::UnnamedImplicitNoBacktrace(0, 0); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_implicit_backtrace() { + let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_explicit_no_backtrace() { + let err = TestErr::UnnamedExplicitNoBacktrace(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace() { + let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_explicit_no_backtrace_redundant() { + let err = + TestErr::UnnamedExplicitNoBacktraceRedundant(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_redundant() { + let err = TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_explicit_supresses_implicit() { + let err = TestErr::UnnamedExplicitSupressesImplicit( + Backtrace::force_capture(), + (|| Backtrace::force_capture())(), // ensure backtraces are different + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); + assert_bt!(!=, err, .get_unused_backtrace); +} diff --git a/tests/error/derives_for_generic_enums_with_source.rs b/tests/error/derives_for_generic_enums_with_source.rs new file mode 100644 index 00000000..92f3b3ae --- /dev/null +++ b/tests/error/derives_for_generic_enums_with_source.rs @@ -0,0 +1,248 @@ +use super::*; + +derive_display!(TestErr, T, E); +#[derive(Debug, Error)] +enum TestErr { + Unit, + NamedImplicitNoSource { + field: T, + }, + NamedImplicitSource { + source: E, + field: T, + }, + NamedExplicitNoSource { + #[error(not(source))] + source: E, + field: T, + }, + NamedExplicitSource { + #[error(source)] + explicit_source: E, + field: T, + }, + NamedExplicitNoSourceRedundant { + #[error(not(source))] + field: T, + }, + NamedExplicitSourceRedundant { + #[error(source)] + source: E, + field: T, + }, + NamedExplicitSuppressesImplicit { + source: T, + #[error(source)] + field: E, + }, + UnnamedImplicitNoSource(T, T), + UnnamedImplicitSource(E), + UnnamedExplicitNoSource(#[error(not(source))] E), + UnnamedExplicitSource(#[error(source)] E, T), + UnnamedExplicitNoSourceRedundant(#[error(not(source))] T, #[error(not(source))] T), + UnnamedExplicitSourceRedundant(#[error(source)] E), + NamedIgnore { + #[error(ignore)] + source: E, + field: T, + }, + UnnamedIgnore(#[error(ignore)] E), + NamedIgnoreRedundant { + #[error(ignore)] + field: T, + }, + UnnamedIgnoreRedundant(#[error(ignore)] T, #[error(ignore)] T), + #[error(ignore)] + NamedVariantIgnore { + source: E, + field: T, + }, + #[error(ignore)] + UnnamedVariantIgnore(E), + #[error(ignore)] + NamedVariantIgnoreRedundant { + field: T, + }, + #[error(ignore)] + UnnamedVariantIgnoreRedundant(T, T), +} + +#[test] +fn unit() { + assert!(TestErr::::Unit.source().is_none()); +} + +#[test] +fn named_implicit_no_source() { + let err = TestErr::::NamedImplicitNoSource { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn named_implicit_source() { + let err = TestErr::NamedImplicitSource { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source() { + let err = TestErr::NamedExplicitNoSource { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_none()); +} + +#[test] +fn named_explicit_source() { + let err = TestErr::NamedExplicitSource { + explicit_source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source_redundant() { + let err = TestErr::::NamedExplicitNoSourceRedundant { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn named_explicit_source_redundant() { + let err = TestErr::NamedExplicitSourceRedundant { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_suppresses_implicit() { + let err = TestErr::NamedExplicitSuppressesImplicit { + source: 0, + field: SimpleErr, + }; + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_implicit_no_source() { + let err = TestErr::::UnnamedImplicitNoSource(0, 0); + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_implicit_source() { + let err = TestErr::<_, i32>::UnnamedImplicitSource(SimpleErr); + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source() { + let err = TestErr::<_, i32>::UnnamedExplicitNoSource(SimpleErr); + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_explicit_source() { + let err = TestErr::UnnamedExplicitSource(SimpleErr, 0); + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source_redundant() { + let err = TestErr::::UnnamedExplicitNoSourceRedundant(0, 0); + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_explicit_source_redundant() { + let err = TestErr::<_, i32>::UnnamedExplicitSourceRedundant(SimpleErr); + + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_ignore() { + let err = TestErr::NamedIgnore { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_ignore() { + let err = TestErr::<_, i32>::UnnamedIgnore(SimpleErr); + + assert!(err.source().is_none()); +} + +#[test] +fn named_ignore_redundant() { + let err = TestErr::::NamedIgnoreRedundant { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_ignore_redundant() { + let err = TestErr::::UnnamedIgnoreRedundant(0, 0); + + assert!(err.source().is_none()); +} + +#[test] +fn named_variant_ignore() { + let err = TestErr::NamedVariantIgnore { + source: SimpleErr, + field: 0, + }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_variant_ignore() { + let err = TestErr::<_, i32>::UnnamedVariantIgnore(SimpleErr); + + assert!(err.source().is_none()) +} + +#[test] +fn named_variant_ignore_redundant() { + let err = TestErr::::NamedVariantIgnoreRedundant { field: 0 }; + + assert!(err.source().is_none()); +} + +#[test] +fn unnamed_variant_ignore_redundant() { + let err = TestErr::::UnnamedVariantIgnoreRedundant(0, 0); + + assert!(err.source().is_none()) +} diff --git a/tests/error/derives_for_generic_enums_with_source_backtrace_chaining.rs b/tests/error/derives_for_generic_enums_with_source_backtrace_chaining.rs new file mode 100644 index 00000000..881195ef --- /dev/null +++ b/tests/error/derives_for_generic_enums_with_source_backtrace_chaining.rs @@ -0,0 +1,239 @@ +use super::*; + +derive_display!(TestErr, E, T); +#[derive(Debug, Error)] +enum TestErr { + NamedNoBacktraceNoSource { + field: T, + }, + NamedNoBacktraceSourceWithoutBacktrace { + source: E, + field: T, + }, + NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { + #[error(not(backtrace))] + source: E, + field: T, + }, + NamedNoBacktraceSourceWithBacktrace { + source: E, + field: T, + }, + NamedBacktraceNoSource { + backtrace: Backtrace, + field: T, + }, + NamedBacktraceSourceWithoutBacktrace { + source: E, + backtrace: Backtrace, + field: T, + }, + NamedBacktraceSourceWithBacktraceExplicitlyDisabled { + #[error(not(backtrace))] + source: E, + backtrace: Backtrace, + field: T, + }, + NamedBacktraceSourceWithBacktrace { + source: E, + backtrace: Backtrace, + field: T, + }, + UnnamedNoBacktraceNoSource(T, T), + UnnamedNoBacktraceSourceWithoutBacktrace(#[error(source)] E, T), + UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( + #[error(source, not(backtrace))] E, + T, + ), + UnnamedNoBacktraceSourceWithBacktrace(#[error(source)] E, T), + UnnamedBacktraceNoSource(Backtrace, T), + UnnamedBacktraceSourceWithoutBacktrace(#[error(source)] E, Backtrace, T), + UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + #[error(source, not(backtrace))] E, + Backtrace, + T, + ), + UnnamedBacktraceSourceWithBacktrace(#[error(source)] E, Backtrace, T), +} + +impl TestErr { + fn get_stored_backtrace(&self) -> &Backtrace { + match self { + Self::NamedBacktraceSourceWithoutBacktrace { backtrace, .. } => backtrace, + Self::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { + backtrace, + .. + } => backtrace, + Self::NamedBacktraceSourceWithBacktrace { backtrace, .. } => backtrace, + Self::UnnamedBacktraceSourceWithoutBacktrace(_, backtrace, _) => backtrace, + Self::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + _, + backtrace, + _, + ) => backtrace, + Self::UnnamedBacktraceSourceWithBacktrace(_, backtrace, _) => backtrace, + _ => panic!("ERROR IN TEST IMPLEMENTATION"), + } + } +} + +#[test] +fn named_no_backtrace_no_source() { + let err = TestErr::::NamedNoBacktraceNoSource { field: 0 }; + + assert!(err.backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_without_backtrace() { + let err = TestErr::NamedNoBacktraceSourceWithoutBacktrace { + source: SimpleErr, + field: 0, + }; + + assert!(err.backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { + let err = TestErr::NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { + source: BacktraceErr::default(), + field: 0, + }; + + assert!(err.backtrace().is_none()); +} + +#[test] +fn named_no_backtrace_source_with_backtrace() { + let err = TestErr::NamedNoBacktraceSourceWithBacktrace { + source: BacktraceErr::default(), + field: 0, + }; + + assert!(err.backtrace().is_some()); +} + +#[test] +fn named_backtrace_no_source() { + let err = TestErr::::NamedBacktraceNoSource { + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); +} + +#[test] +fn named_backtrace_source_without_backtrace() { + let err = TestErr::NamedBacktraceSourceWithoutBacktrace { + source: SimpleErr, + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_backtrace_source_with_backtrace_explicitly_disabled() { + let err = TestErr::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn named_backtrace_source_with_backtrace() { + let err = TestErr::NamedBacktraceSourceWithBacktrace { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_no_backtrace_no_source() { + let err = TestErr::::UnnamedNoBacktraceNoSource(0, 0); + + assert!(err.backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_without_backtrace() { + let err = TestErr::UnnamedNoBacktraceSourceWithoutBacktrace(SimpleErr, 0); + + assert!(err.backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { + let err = TestErr::UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( + BacktraceErr::default(), + 0, + ); + + assert!(err.backtrace().is_none()); +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace() { + let err = + TestErr::UnnamedNoBacktraceSourceWithBacktrace(BacktraceErr::default(), 0); + + assert!(err.backtrace().is_some()); +} + +#[test] +fn unnamed_backtrace_no_source() { + let err = TestErr::::UnnamedBacktraceNoSource( + Backtrace::force_capture(), + 0, + ); + + assert!(err.backtrace().is_some()); +} + +#[test] +fn unnamed_backtrace_source_without_backtrace() { + let err = TestErr::UnnamedBacktraceSourceWithoutBacktrace( + SimpleErr, + Backtrace::force_capture(), + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace_explictily_disabled() { + let err = TestErr::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + BacktraceErr::default(), + Backtrace::force_capture(), + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace() { + let err = TestErr::UnnamedBacktraceSourceWithBacktrace( + BacktraceErr::default(), + Backtrace::force_capture(), + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, .get_stored_backtrace); +} diff --git a/tests/error/derives_for_generic_structs_with_backtrace.rs b/tests/error/derives_for_generic_structs_with_backtrace.rs new file mode 100644 index 00000000..2de93b9a --- /dev/null +++ b/tests/error/derives_for_generic_structs_with_backtrace.rs @@ -0,0 +1,275 @@ +use super::*; + +#[test] +fn named_implicit_no_backtrace() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr { + field: T, + } + + assert!(TestErr::::default().backtrace().is_none()); +} + +#[test] +fn named_implicit_backtrace_by_field_name() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + backtrace: MyBacktrace, + field: T, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_implicit_backtrace_by_field_type() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + implicit_backtrace: Backtrace, + field: T, + } + + let err = TestErr { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err, implicit_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_by_field_name() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + backtrace: MyBacktrace, + field: T, + } + + type MyBacktrace = Backtrace; + + assert!(TestErr { + backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_none()); +} + +#[test] +fn named_explicit_no_backtrace_by_field_type() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + implicit_backtrace: Backtrace, + field: T, + } + + assert!(TestErr { + implicit_backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_none()); +} + +#[test] +fn named_explicit_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + explicit_backtrace: MyBacktrace, + field: T, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + explicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err, explicit_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_redundant() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + not_backtrace: MyBacktrace, + #[error(not(backtrace))] + field: T, + } + + type MyBacktrace = Backtrace; + + assert!(TestErr { + not_backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_none()); +} + +#[test] +fn named_explicit_backtrace_by_field_name_redundant() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + backtrace: MyBacktrace, + field: T, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_explicit_backtrace_by_field_type_redundant() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + implicit_backtrace: Backtrace, + field: T, + } + + let err = TestErr { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err, implicit_backtrace); +} + +#[test] +fn named_explicit_supresses_implicit() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + not_backtrace: MyBacktrace, + backtrace: Backtrace, + field: T, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + not_backtrace: Backtrace::force_capture(), + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, not_backtrace); + assert_bt!(!=, err); +} + +#[test] +fn unnamed_implicit_no_backtrace() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr(T, T); + + assert!(TestErr::::default().backtrace().is_none()); +} + +#[test] +fn unnamed_implicit_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(Backtrace, T); + + let err = TestErr(Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); +} + +#[test] +fn unnamed_explicit_no_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(not(backtrace))] Backtrace, T); + + assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] MyBacktrace, T); + + type MyBacktrace = Backtrace; + + let err = TestErr(Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); +} + +#[test] +fn unnamed_explicit_no_backtrace_redundant() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr( + #[error(not(backtrace))] MyBacktrace, + #[error(not(backtrace))] T, + ); + + type MyBacktrace = Backtrace; + + assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_redundant() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] Backtrace, T); + + let err = TestErr(Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); +} + +#[test] +fn unnamed_explicit_supresses_implicit() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] MyBacktrace, Backtrace, T); + + type MyBacktrace = Backtrace; + + let err = TestErr( + Backtrace::force_capture(), + (|| Backtrace::force_capture())(), // ensure backtraces are different + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); + assert_bt!(!=, err, 1); +} diff --git a/tests/error/derives_for_generic_structs_with_source.rs b/tests/error/derives_for_generic_structs_with_source.rs new file mode 100644 index 00000000..248e3c97 --- /dev/null +++ b/tests/error/derives_for_generic_structs_with_source.rs @@ -0,0 +1,245 @@ +use super::*; + +#[test] +fn named_implicit_no_source() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr { + field: T, + } + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn named_implicit_source() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + source: E, + field: T, + } + + let err = TestErr::::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(not(source))] + source: E, + field: T, + } + + let err = TestErr::::default(); + assert!(err.source().is_none()); +} + +#[test] +fn named_explicit_source() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(source)] + explicit_source: E, + field: T, + } + + let err = TestErr::::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source_redundant() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(not(source))] + field: T, + } + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn named_explicit_source_redundant() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(source)] + source: E, + field: T, + } + + let err = TestErr::::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_suppresses_implicit() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + source: E, + #[error(source)] + field: T, + } + + let err = TestErr::::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_implicit_no_source() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr(T, T); + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn unnamed_implicit_source() { + derive_display!(TestErr, E); + #[derive(Default, Debug, Error)] + struct TestErr(E); + + let err = TestErr::::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source() { + derive_display!(TestErr, E); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(not(source))] E); + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn unnamed_explicit_source() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] E, T); + + let err = TestErr::::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source_redundant() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(not(source))] T, #[error(not(source))] T); + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn unnamed_explicit_source_redundant() { + derive_display!(TestErr, E); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] E); + + let err = TestErr::::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_ignore() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(ignore)] + source: E, + field: T, + } + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn unnamed_ignore() { + derive_display!(TestErr, E); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(ignore)] E); + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn named_ignore_redundant() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(ignore)] + field: T, + } + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn unnamed_ignore_redundant() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(ignore)] T, #[error(ignore)] T); + + assert!(TestErr::::default().source().is_none()); +} + +#[test] +fn named_struct_ignore() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr { + source: E, + field: T, + } + + assert!(TestErr::::default().source().is_none()) +} + +#[test] +fn unnamed_struct_ignore() { + derive_display!(TestErr, E); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr(E); + + assert!(TestErr::::default().source().is_none()) +} + +#[test] +fn named_struct_ignore_redundant() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr { + field: T, + } + + assert!(TestErr::::default().source().is_none()) +} + +#[test] +fn unnamed_struct_ignore_redundant() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr(T, T); + + assert!(TestErr::::default().source().is_none()) +} diff --git a/tests/error/derives_for_generic_structs_with_source_backtrace_chaining.rs b/tests/error/derives_for_generic_structs_with_source_backtrace_chaining.rs new file mode 100644 index 00000000..67722b1c --- /dev/null +++ b/tests/error/derives_for_generic_structs_with_source_backtrace_chaining.rs @@ -0,0 +1,210 @@ +use super::*; + +#[test] +fn named_no_backtrace_no_source() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr { + field: T, + } + + assert!(TestErr::::default().backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_without_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + source: E, + field: T, + } + + assert!(TestErr::::default().backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + source: E, + field: T, + } + + assert!(TestErr::::default() + .backtrace() + .is_none()); +} + +#[test] +fn named_no_backtrace_source_with_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr { + source: E, + field: T, + } + + assert!(TestErr::::default() + .backtrace() + .is_some()); +} + +#[test] +fn named_backtrace_no_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr { + backtrace: Backtrace, + field: T, + } + + assert!(TestErr { + backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_some()); +} + +#[test] +fn named_backtrace_source_without_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Debug, Error)] + struct TestErr { + source: E, + backtrace: Backtrace, + field: T, + } + + let err = TestErr { + source: SimpleErr, + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_backtrace_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr, E, T); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + source: E, + backtrace: Backtrace, + field: T, + } + + let err = TestErr { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_backtrace_source_with_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Debug, Error)] + struct TestErr { + source: E, + backtrace: Backtrace, + field: T, + } + + let err = TestErr { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(!=, err); +} + +#[test] +fn unnamed_no_backtrace_no_source() { + derive_display!(TestErr, T); + #[derive(Default, Debug, Error)] + struct TestErr(T, T); + + assert!(TestErr::::default().backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_without_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] E, T); + + assert!(TestErr::::default().backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source, not(backtrace))] E, T); + + assert!(TestErr::::default() + .backtrace() + .is_none()); +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] E, T); + + assert!(TestErr::::default() + .backtrace() + .is_some()); +} + +#[test] +fn unnamed_backtrace_no_source() { + derive_display!(TestErr, T); + #[derive(Debug, Error)] + struct TestErr(Backtrace, T); + + assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_some()); +} + +#[test] +fn unnamed_backtrace_source_without_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] E, Backtrace, T); + + let err = TestErr(SimpleErr, Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace_explictitly_disabled() { + derive_display!(TestErr, E, T); + #[derive(Debug, Error)] + struct TestErr(#[error(source, not(backtrace))] E, Backtrace, T); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace() { + derive_display!(TestErr, E, T); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] E, Backtrace, T); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, 1); +} diff --git a/tests/error/derives_for_structs_with_backtrace.rs b/tests/error/derives_for_structs_with_backtrace.rs new file mode 100644 index 00000000..6e0b13f8 --- /dev/null +++ b/tests/error/derives_for_structs_with_backtrace.rs @@ -0,0 +1,280 @@ +use super::*; + +#[test] +fn unit() { + assert!(SimpleErr.backtrace().is_none()); +} + +#[test] +fn named_implicit_no_backtrace() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + field: i32, + } + + assert!(TestErr::default().backtrace().is_none()); +} + +#[test] +fn named_implicit_backtrace_by_field_name() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + backtrace: MyBacktrace, + field: i32, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_implicit_backtrace_by_field_type() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + implicit_backtrace: Backtrace, + field: i32, + } + + let err = TestErr { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err, implicit_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_by_field_name() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + backtrace: MyBacktrace, + field: i32, + } + + type MyBacktrace = Backtrace; + + assert!(TestErr { + backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_none()); +} + +#[test] +fn named_explicit_no_backtrace_by_field_type() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + implicit_backtrace: Backtrace, + field: i32, + } + + assert!(TestErr { + implicit_backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_none()); +} + +#[test] +fn named_explicit_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + explicit_backtrace: MyBacktrace, + field: i32, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + explicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err, explicit_backtrace); +} + +#[test] +fn named_explicit_no_backtrace_redundant() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + not_backtrace: MyBacktrace, + #[error(not(backtrace))] + field: i32, + } + + type MyBacktrace = Backtrace; + + assert!(TestErr { + not_backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_none()); +} + +#[test] +fn named_explicit_backtrace_by_field_name_redundant() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + backtrace: MyBacktrace, + field: i32, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_explicit_backtrace_by_field_type_redundant() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + implicit_backtrace: Backtrace, + field: i32, + } + + let err = TestErr { + implicit_backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err, implicit_backtrace); +} + +#[test] +fn named_explicit_supresses_implicit() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(backtrace)] + not_backtrace: MyBacktrace, + backtrace: Backtrace, + field: i32, + } + + type MyBacktrace = Backtrace; + + let err = TestErr { + not_backtrace: Backtrace::force_capture(), + backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different + field: 0, + }; + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, not_backtrace); + assert_bt!(!=, err); +} + +#[test] +fn unnamed_implicit_no_backtrace() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(i32, i32); + + assert!(TestErr::default().backtrace().is_none()); +} + +#[test] +fn unnamed_implicit_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(Backtrace, i32); + + let err = TestErr(Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); +} + +#[test] +fn unnamed_explicit_no_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(not(backtrace))] Backtrace, i32); + + assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] MyBacktrace, i32); + + type MyBacktrace = Backtrace; + + let err = TestErr(Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); +} + +#[test] +fn unnamed_explicit_no_backtrace_redundant() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr( + #[error(not(backtrace))] MyBacktrace, + #[error(not(backtrace))] i32, + ); + + type MyBacktrace = Backtrace; + + assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none()); +} + +#[test] +fn unnamed_explicit_backtrace_redundant() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] Backtrace, i32); + + let err = TestErr(Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); +} + +#[test] +fn unnamed_explicit_supresses_implicit() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(backtrace)] MyBacktrace, Backtrace, i32); + + type MyBacktrace = Backtrace; + + let err = TestErr( + Backtrace::force_capture(), + (|| Backtrace::force_capture())(), // ensure backtraces are different + 0, + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 0); + assert_bt!(!=, err, 1); +} diff --git a/tests/error/derives_for_structs_with_source.rs b/tests/error/derives_for_structs_with_source.rs new file mode 100644 index 00000000..d46bd5ad --- /dev/null +++ b/tests/error/derives_for_structs_with_source.rs @@ -0,0 +1,249 @@ +use super::*; + +#[test] +fn unit() { + assert!(SimpleErr.source().is_none()); +} + +#[test] +fn named_implicit_no_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + field: i32, + } + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn named_implicit_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + source: SimpleErr, + field: i32, + } + + let err = TestErr::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(not(source))] + source: SimpleErr, + field: i32, + } + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn named_explicit_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(source)] + explicit_source: SimpleErr, + field: i32, + } + + let err = TestErr::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_no_source_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(not(source))] + field: i32, + } + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn named_explicit_source_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(source)] + source: SimpleErr, + field: i32, + } + + let err = TestErr::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_explicit_suppresses_implicit() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + source: i32, + #[error(source)] + field: SimpleErr, + } + + let err = TestErr::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_implicit_no_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(i32, i32); + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn unnamed_implicit_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(SimpleErr); + + let err = TestErr::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(not(source))] SimpleErr); + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn unnamed_explicit_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] SimpleErr, i32); + + let err = TestErr::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn unnamed_explicit_no_source_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(not(source))] i32, #[error(not(source))] i32); + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn unnamed_explicit_source_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] SimpleErr); + + let err = TestErr::default(); + assert!(err.source().is_some()); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn named_ignore() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(ignore)] + source: SimpleErr, + field: i32, + } + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn unnamed_ignore() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(ignore)] SimpleErr); + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn named_ignore_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(ignore)] + field: i32, + } + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn unnamed_ignore_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(ignore)] i32, #[error(ignore)] i32); + + assert!(TestErr::default().source().is_none()); +} + +#[test] +fn named_struct_ignore() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr { + source: SimpleErr, + field: i32, + } + + assert!(TestErr::default().source().is_none()) +} + +#[test] +fn unnamed_struct_ignore() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr(SimpleErr); + + assert!(TestErr::default().source().is_none()) +} + +#[test] +fn named_struct_ignore_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr { + field: i32, + } + + assert!(TestErr::default().source().is_none()) +} + +#[test] +fn unnamed_struct_ignore_redundant() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + #[error(ignore)] + struct TestErr(i32, i32); + + assert!(TestErr::default().source().is_none()) +} diff --git a/tests/error/derives_for_structs_with_source_backtrace_chaining.rs b/tests/error/derives_for_structs_with_source_backtrace_chaining.rs new file mode 100644 index 00000000..24a6086a --- /dev/null +++ b/tests/error/derives_for_structs_with_source_backtrace_chaining.rs @@ -0,0 +1,206 @@ +use super::*; + +#[test] +fn named_no_backtrace_no_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + field: i32, + } + + assert!(TestErr::default().backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_without_backtrace() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + source: SimpleErr, + field: i32, + } + + assert!(TestErr::default().backtrace().is_none()) +} + +#[test] +fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + source: BacktraceErr, + field: i32, + } + + assert!(TestErr::default().backtrace().is_none()); +} + +#[test] +fn named_no_backtrace_source_with_backtrace() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr { + source: BacktraceErr, + field: i32, + } + + assert!(TestErr::default().backtrace().is_some()); +} + +#[test] +fn named_backtrace_no_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + backtrace: Backtrace, + field: i32, + } + + assert!(TestErr { + backtrace: Backtrace::force_capture(), + field: 0 + } + .backtrace() + .is_some()); +} + +#[test] +fn named_backtrace_source_without_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + source: SimpleErr, + backtrace: Backtrace, + field: i32, + } + + let err = TestErr { + source: SimpleErr, + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_backtrace_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + #[error(not(backtrace))] + source: BacktraceErr, + backtrace: Backtrace, + field: i32, + } + + let err = TestErr { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(==, err); +} + +#[test] +fn named_backtrace_source_with_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr { + source: BacktraceErr, + backtrace: Backtrace, + field: i32, + } + + let err = TestErr { + source: BacktraceErr::default(), + backtrace: Backtrace::force_capture(), + field: 0, + }; + assert!(err.backtrace().is_some()); + assert_bt!(!=, err); +} + +#[test] +fn unnamed_no_backtrace_no_source() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(i32, i32); + + assert!(TestErr::default().backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_without_backtrace() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] SimpleErr, i32); + + assert!(TestErr::default().backtrace().is_none()) +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source, not(backtrace))] BacktraceErr, i32); + + assert!(TestErr::default().backtrace().is_none()); +} + +#[test] +fn unnamed_no_backtrace_source_with_backtrace() { + derive_display!(TestErr); + #[derive(Default, Debug, Error)] + struct TestErr(#[error(source)] BacktraceErr, i32); + + assert!(TestErr::default().backtrace().is_some()); +} + +#[test] +fn unnamed_backtrace_no_source() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(Backtrace, i32); + + assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_some()); +} + +#[test] +fn unnamed_backtrace_source_without_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] SimpleErr, Backtrace, i32); + + let err = TestErr(SimpleErr, Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace_explictily_disabled() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr( + #[error(source, not(backtrace))] BacktraceErr, + Backtrace, + i32, + ); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_source_with_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(source)] BacktraceErr, Backtrace, i32); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, 1); +} diff --git a/tests/error/mod.rs b/tests/error/mod.rs new file mode 100644 index 00000000..d23ce243 --- /dev/null +++ b/tests/error/mod.rs @@ -0,0 +1,141 @@ +use std::{backtrace::Backtrace, error::Error}; + +/// Derives `std::fmt::Display` for structs/enums. +/// Derived implementation outputs empty string. +/// Useful, as a way to formally satisfy `Display` trait bound. +/// +/// ## Syntax: +/// +/// For regular structs/enums: +/// +/// ``` +/// enum MyEnum { +/// ... +/// } +/// +/// derive_display!(MyEnum); +/// ``` +/// +/// For generic structs/enums: +/// +/// ``` +/// struct MyGenericStruct { +/// ... +/// } +/// +/// derive_display!(MyGenericStruct, T, U); +/// ``` +macro_rules! derive_display { + (@fmt) => { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "") + } + }; + ($type:ident) => { + impl ::std::fmt::Display for $type { + derive_display!(@fmt); + } + }; + ($type:ident, $($type_parameters:ident),*) => { + impl<$($type_parameters),*> ::std::fmt::Display for $type<$($type_parameters),*> { + derive_display!(@fmt); + } + }; +} + +/// Asserts that backtrace returned by `Error::backtrace` method equals/not-equals +/// backtrace stored in object itself. +/// +/// Comparison is done by converting backtraces to strings +/// and then comparing these strings. +/// +/// ## Syntax +/// +/// * Equals: `assert_bt!(==, ...)` +/// * Not-equals: `assert_bt!(!=, ...)` +/// +/// ### Backtrace Access +/// +/// Shortcut for named-structs with `backtrace` field. +/// Access backtrace as `error.backtrace`. +/// +/// ``` +/// assert_bt!(==, error); +/// ``` +/// +/// Full form for named- and tuple-structs. +/// Access backtrace as `error.some_other_field` and `error.1` respectively. +/// +/// ``` +/// assert_bt!(!=, error, some_other_field); +/// assert_bt!(==, error, 1); +/// ``` +/// +/// Access as a method call. +/// Useful for enums (i.e., you can define a method that will match on enum variants +/// and return backtrace for each variant). +/// Access backtrace as `error.get_stored_backtrace_method()`. +/// +/// ``` +/// assert_bt!(!=, error, .get_stored_backtrace_method); +/// ``` +macro_rules! assert_bt { + (@impl $macro:ident, $error:expr, $backtrace:expr) => { + $macro!($error.backtrace().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) => { + assert_bt!(@impl $macro, $error, $error.$backtrace) + }; + (@expand $macro:ident, $error:expr) => { + assert_bt!(@expand $macro, $error, backtrace) + }; + (==, $($args:tt)*) => { + assert_bt!(@expand assert_eq, $($args)*) + }; + (!=, $($args:tt)*) => { + assert_bt!(@expand assert_ne, $($args)*) + }; +} + +mod derives_for_structs_with_backtrace; +mod derives_for_structs_with_source; +mod derives_for_structs_with_source_backtrace_chaining; + +mod derives_for_enums_with_backtrace; +mod derives_for_enums_with_source; +mod derives_for_enums_with_source_backtrace_chaining; + +mod derives_for_generic_structs_with_backtrace; +mod derives_for_generic_structs_with_source; +mod derives_for_generic_structs_with_source_backtrace_chaining; + +mod derives_for_generic_enums_with_backtrace; +mod derives_for_generic_enums_with_source; +mod derives_for_generic_enums_with_source_backtrace_chaining; + +derive_display!(SimpleErr); +#[derive(Default, Debug, Error)] +struct SimpleErr; + +derive_display!(BacktraceErr); +#[derive(Debug)] +struct BacktraceErr { + backtrace: Backtrace, +} + +impl Default for BacktraceErr { + fn default() -> Self { + Self { + backtrace: Backtrace::force_capture(), + } + } +} + +impl Error for BacktraceErr { + fn backtrace(&self) -> Option<&Backtrace> { + Some(&self.backtrace) + } +} diff --git a/tests/error_tests.rs b/tests/error_tests.rs new file mode 100644 index 00000000..12955cee --- /dev/null +++ b/tests/error_tests.rs @@ -0,0 +1,6 @@ +#![feature(backtrace)] + +#[macro_use] +extern crate derive_more; + +mod error; From b5bd37821b17bf8e90880ba637562279a8f29ca8 Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Thu, 21 Nov 2019 12:41:40 -0500 Subject: [PATCH 2/8] Add simple feature-gating --- src/error.rs | 6 +- tests/error/mod.rs | 95 +------------------ .../derives_for_enums_with_backtrace.rs | 0 ...or_enums_with_source_backtrace_chaining.rs | 0 ...erives_for_generic_enums_with_backtrace.rs | 0 ...ic_enums_with_source_backtrace_chaining.rs | 0 ...ives_for_generic_structs_with_backtrace.rs | 0 ..._structs_with_source_backtrace_chaining.rs | 0 .../derives_for_structs_with_backtrace.rs | 0 ..._structs_with_source_backtrace_chaining.rs | 0 tests/error/nightly/mod.rs | 89 +++++++++++++++++ tests/error_tests.rs | 2 +- 12 files changed, 100 insertions(+), 92 deletions(-) rename tests/error/{ => nightly}/derives_for_enums_with_backtrace.rs (100%) rename tests/error/{ => nightly}/derives_for_enums_with_source_backtrace_chaining.rs (100%) rename tests/error/{ => nightly}/derives_for_generic_enums_with_backtrace.rs (100%) rename tests/error/{ => nightly}/derives_for_generic_enums_with_source_backtrace_chaining.rs (100%) rename tests/error/{ => nightly}/derives_for_generic_structs_with_backtrace.rs (100%) rename tests/error/{ => nightly}/derives_for_generic_structs_with_source_backtrace_chaining.rs (100%) rename tests/error/{ => nightly}/derives_for_structs_with_backtrace.rs (100%) rename tests/error/{ => nightly}/derives_for_structs_with_source_backtrace_chaining.rs (100%) create mode 100644 tests/error/nightly/mod.rs diff --git a/src/error.rs b/src/error.rs index c767fc6d..d66a2bbc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -238,6 +238,10 @@ impl<'input, 'state> ParsedFields<'input, 'state> { S: Fn(&ParsedFields, usize) -> TokenStream, B: Fn(&ParsedFields, usize) -> TokenStream, { + if !cfg!(feature = "nightly") { + return None; + } + // Disable proxying call to `backtrace` to `source` if // `source` explicitly marked with `#[error(not(backtrace))]`. let source = self.source.and_then(|source| { @@ -331,7 +335,7 @@ fn parse_fields<'input, 'state>( }) } - _ => unreachable!(), // TODO + _ => unreachable!(), } } diff --git a/tests/error/mod.rs b/tests/error/mod.rs index d23ce243..5d7190d8 100644 --- a/tests/error/mod.rs +++ b/tests/error/mod.rs @@ -1,4 +1,4 @@ -use std::{backtrace::Backtrace, error::Error}; +use std::error::Error; /// Derives `std::fmt::Display` for structs/enums. /// Derived implementation outputs empty string. @@ -43,99 +43,14 @@ macro_rules! derive_display { }; } -/// Asserts that backtrace returned by `Error::backtrace` method equals/not-equals -/// backtrace stored in object itself. -/// -/// Comparison is done by converting backtraces to strings -/// and then comparing these strings. -/// -/// ## Syntax -/// -/// * Equals: `assert_bt!(==, ...)` -/// * Not-equals: `assert_bt!(!=, ...)` -/// -/// ### Backtrace Access -/// -/// Shortcut for named-structs with `backtrace` field. -/// Access backtrace as `error.backtrace`. -/// -/// ``` -/// assert_bt!(==, error); -/// ``` -/// -/// Full form for named- and tuple-structs. -/// Access backtrace as `error.some_other_field` and `error.1` respectively. -/// -/// ``` -/// assert_bt!(!=, error, some_other_field); -/// assert_bt!(==, error, 1); -/// ``` -/// -/// Access as a method call. -/// Useful for enums (i.e., you can define a method that will match on enum variants -/// and return backtrace for each variant). -/// Access backtrace as `error.get_stored_backtrace_method()`. -/// -/// ``` -/// assert_bt!(!=, error, .get_stored_backtrace_method); -/// ``` -macro_rules! assert_bt { - (@impl $macro:ident, $error:expr, $backtrace:expr) => { - $macro!($error.backtrace().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) => { - assert_bt!(@impl $macro, $error, $error.$backtrace) - }; - (@expand $macro:ident, $error:expr) => { - assert_bt!(@expand $macro, $error, backtrace) - }; - (==, $($args:tt)*) => { - assert_bt!(@expand assert_eq, $($args)*) - }; - (!=, $($args:tt)*) => { - assert_bt!(@expand assert_ne, $($args)*) - }; -} - -mod derives_for_structs_with_backtrace; -mod derives_for_structs_with_source; -mod derives_for_structs_with_source_backtrace_chaining; - -mod derives_for_enums_with_backtrace; mod derives_for_enums_with_source; -mod derives_for_enums_with_source_backtrace_chaining; - -mod derives_for_generic_structs_with_backtrace; +mod derives_for_generic_enums_with_source; mod derives_for_generic_structs_with_source; -mod derives_for_generic_structs_with_source_backtrace_chaining; +mod derives_for_structs_with_source; -mod derives_for_generic_enums_with_backtrace; -mod derives_for_generic_enums_with_source; -mod derives_for_generic_enums_with_source_backtrace_chaining; +#[cfg(feature = "nightly")] +mod nightly; derive_display!(SimpleErr); #[derive(Default, Debug, Error)] struct SimpleErr; - -derive_display!(BacktraceErr); -#[derive(Debug)] -struct BacktraceErr { - backtrace: Backtrace, -} - -impl Default for BacktraceErr { - fn default() -> Self { - Self { - backtrace: Backtrace::force_capture(), - } - } -} - -impl Error for BacktraceErr { - fn backtrace(&self) -> Option<&Backtrace> { - Some(&self.backtrace) - } -} diff --git a/tests/error/derives_for_enums_with_backtrace.rs b/tests/error/nightly/derives_for_enums_with_backtrace.rs similarity index 100% rename from tests/error/derives_for_enums_with_backtrace.rs rename to tests/error/nightly/derives_for_enums_with_backtrace.rs diff --git a/tests/error/derives_for_enums_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs similarity index 100% rename from tests/error/derives_for_enums_with_source_backtrace_chaining.rs rename to tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs diff --git a/tests/error/derives_for_generic_enums_with_backtrace.rs b/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs similarity index 100% rename from tests/error/derives_for_generic_enums_with_backtrace.rs rename to tests/error/nightly/derives_for_generic_enums_with_backtrace.rs diff --git a/tests/error/derives_for_generic_enums_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs similarity index 100% rename from tests/error/derives_for_generic_enums_with_source_backtrace_chaining.rs rename to tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs diff --git a/tests/error/derives_for_generic_structs_with_backtrace.rs b/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs similarity index 100% rename from tests/error/derives_for_generic_structs_with_backtrace.rs rename to tests/error/nightly/derives_for_generic_structs_with_backtrace.rs diff --git a/tests/error/derives_for_generic_structs_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs similarity index 100% rename from tests/error/derives_for_generic_structs_with_source_backtrace_chaining.rs rename to tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs diff --git a/tests/error/derives_for_structs_with_backtrace.rs b/tests/error/nightly/derives_for_structs_with_backtrace.rs similarity index 100% rename from tests/error/derives_for_structs_with_backtrace.rs rename to tests/error/nightly/derives_for_structs_with_backtrace.rs diff --git a/tests/error/derives_for_structs_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs similarity index 100% rename from tests/error/derives_for_structs_with_source_backtrace_chaining.rs rename to tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs diff --git a/tests/error/nightly/mod.rs b/tests/error/nightly/mod.rs new file mode 100644 index 00000000..edb55271 --- /dev/null +++ b/tests/error/nightly/mod.rs @@ -0,0 +1,89 @@ +use std::backtrace::Backtrace; + +use super::*; + +/// Asserts that backtrace returned by `Error::backtrace` method equals/not-equals +/// backtrace stored in object itself. +/// +/// Comparison is done by converting backtraces to strings +/// and then comparing these strings. +/// +/// ## Syntax +/// +/// * Equals: `assert_bt!(==, ...)` +/// * Not-equals: `assert_bt!(!=, ...)` +/// +/// ### Backtrace Access +/// +/// Shortcut for named-structs with `backtrace` field. +/// Access backtrace as `error.backtrace`. +/// +/// ``` +/// assert_bt!(==, error); +/// ``` +/// +/// Full form for named- and tuple-structs. +/// Access backtrace as `error.some_other_field` and `error.1` respectively. +/// +/// ``` +/// assert_bt!(!=, error, some_other_field); +/// assert_bt!(==, error, 1); +/// ``` +/// +/// Access as a method call. +/// Useful for enums (i.e., you can define a method that will match on enum variants +/// and return backtrace for each variant). +/// Access backtrace as `error.get_stored_backtrace_method()`. +/// +/// ``` +/// assert_bt!(!=, error, .get_stored_backtrace_method); +/// ``` +macro_rules! assert_bt { + (@impl $macro:ident, $error:expr, $backtrace:expr) => { + $macro!($error.backtrace().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) => { + assert_bt!(@impl $macro, $error, $error.$backtrace) + }; + (@expand $macro:ident, $error:expr) => { + assert_bt!(@expand $macro, $error, backtrace) + }; + (==, $($args:tt)*) => { + assert_bt!(@expand assert_eq, $($args)*) + }; + (!=, $($args:tt)*) => { + assert_bt!(@expand assert_ne, $($args)*) + }; +} + +mod derives_for_enums_with_backtrace; +mod derives_for_enums_with_source_backtrace_chaining; +mod derives_for_generic_enums_with_backtrace; +mod derives_for_generic_enums_with_source_backtrace_chaining; +mod derives_for_generic_structs_with_backtrace; +mod derives_for_generic_structs_with_source_backtrace_chaining; +mod derives_for_structs_with_backtrace; +mod derives_for_structs_with_source_backtrace_chaining; + +derive_display!(BacktraceErr); +#[derive(Debug)] +struct BacktraceErr { + backtrace: Backtrace, +} + +impl Default for BacktraceErr { + fn default() -> Self { + Self { + backtrace: Backtrace::force_capture(), + } + } +} + +impl Error for BacktraceErr { + fn backtrace(&self) -> Option<&Backtrace> { + Some(&self.backtrace) + } +} diff --git a/tests/error_tests.rs b/tests/error_tests.rs index 12955cee..c2798bd1 100644 --- a/tests/error_tests.rs +++ b/tests/error_tests.rs @@ -1,4 +1,4 @@ -#![feature(backtrace)] +#![cfg_attr(feature = "nightly", feature(backtrace))] #[macro_use] extern crate derive_more; From 5b8fcde3c75758552fa89b0ce79afe36b90d6078 Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Fri, 22 Nov 2019 11:47:51 -0500 Subject: [PATCH 3/8] Improve source/backtrace inferring for tuple-structs/tuple-enum-variants --- src/error.rs | 47 +++++++++---- .../derives_for_enums_with_backtrace.rs | 19 +++--- ...or_enums_with_source_backtrace_chaining.rs | 67 ++++++++++++++++--- ...erives_for_generic_enums_with_backtrace.rs | 19 +++--- ...ic_enums_with_source_backtrace_chaining.rs | 52 +++++++++++++- ...ives_for_generic_structs_with_backtrace.rs | 12 ++-- ..._structs_with_source_backtrace_chaining.rs | 39 ++++++++++- .../derives_for_structs_with_backtrace.rs | 12 ++-- ..._structs_with_source_backtrace_chaining.rs | 39 ++++++++++- 9 files changed, 248 insertions(+), 58 deletions(-) diff --git a/src/error.rs b/src/error.rs index d66a2bbc..24a33163 100644 --- a/src/error.rs +++ b/src/error.rs @@ -309,9 +309,9 @@ fn parse_fields<'input, 'state>( type_params: &HashSet, state: &'state State<'input>, ) -> Result> { - match state.derive_type { + let parsed_fields = match state.derive_type { DeriveType::Named => { - parse_fields_impl(type_params, state, |attr, field, _| { + parse_fields_impl(state, |attr, field, _| { // Unwrapping is safe, cause fields in named struct // always have an ident let ident = field.ident.as_ref().unwrap(); @@ -328,15 +328,44 @@ fn parse_fields<'input, 'state>( } DeriveType::Unnamed => { - parse_fields_impl(type_params, state, |attr, field, len| match attr { - "source" => len == 1, + let result = parse_fields_impl(state, |attr, field, len| match attr { + "source" => len == 1 && !is_type_ends_with(&field.ty, "Backtrace"), "backtrace" => is_type_ends_with(&field.ty, "Backtrace"), _ => unreachable!(), + }); + + result.map(|mut parsed_fields| { + let len_source_backtrace = ( + state.fields.len(), + parsed_fields.source, + parsed_fields.backtrace, + ); + + if let (2, None, Some(backtrace)) = len_source_backtrace { + let source = (backtrace + 1) % 2; + if parsed_fields.data.infos[source].info.source != Some(false) { + parsed_fields.source = Some(source); + } + } + + parsed_fields }) } _ => unreachable!(), - } + }; + + parsed_fields.map(|mut parsed_fields| { + if let Some(source) = parsed_fields.source { + add_bound_if_type_parameter_used_in_type( + &mut parsed_fields.bounds, + type_params, + &state.fields[source].ty, + ); + } + + parsed_fields + }) } fn is_type_ends_with(ty: &syn::Type, tail: &str) -> bool { @@ -358,7 +387,6 @@ fn is_type_ends_with(ty: &syn::Type, tail: &str) -> bool { } fn parse_fields_impl<'input, 'state, P>( - type_params: &HashSet, state: &'state State<'input>, is_valid_default_field_for_attr: P, ) -> Result> @@ -395,13 +423,8 @@ where let mut parsed_fields = ParsedFields::new(state.enabled_fields_data()); - if let Some((index, field, _)) = source { + if let Some((index, _, _)) = source { parsed_fields.source = Some(index); - add_bound_if_type_parameter_used_in_type( - &mut parsed_fields.bounds, - type_params, - &field.ty, - ); } if let Some((index, _, _)) = backtrace { diff --git a/tests/error/nightly/derives_for_enums_with_backtrace.rs b/tests/error/nightly/derives_for_enums_with_backtrace.rs index 89155d02..c4ff4817 100644 --- a/tests/error/nightly/derives_for_enums_with_backtrace.rs +++ b/tests/error/nightly/derives_for_enums_with_backtrace.rs @@ -53,14 +53,14 @@ enum TestErr { field: i32, }, UnnamedImplicitNoBacktrace(i32, i32), - UnnamedImplicitBacktrace(Backtrace, i32), + UnnamedImplicitBacktrace(Backtrace, i32, i32), UnnamedExplicitNoBacktrace(#[error(not(backtrace))] Backtrace, i32), - UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, i32), + UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, i32, i32), UnnamedExplicitNoBacktraceRedundant( #[error(not(backtrace))] MyBacktrace, #[error(not(backtrace))] i32, ), - UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, i32), + UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, i32, i32), UnnamedExplicitSupressesImplicit(#[error(backtrace)] MyBacktrace, Backtrace, i32), } @@ -82,9 +82,9 @@ impl TestErr { .. } => implicit_backtrace, Self::NamedExplicitSupressesImplicit { not_backtrace, .. } => not_backtrace, - Self::UnnamedImplicitBacktrace(backtrace, _) => backtrace, - Self::UnnamedExplicitBacktrace(backtrace, _) => backtrace, - Self::UnnamedExplicitBacktraceRedundant(backtrace, _) => backtrace, + Self::UnnamedImplicitBacktrace(backtrace, _, _) => backtrace, + Self::UnnamedExplicitBacktrace(backtrace, _, _) => backtrace, + Self::UnnamedExplicitBacktraceRedundant(backtrace, _, _) => backtrace, Self::UnnamedExplicitSupressesImplicit(backtrace, _, _) => backtrace, _ => panic!("ERROR IN TEST IMPLEMENTATION"), } @@ -220,7 +220,7 @@ fn unnamed_implicit_no_backtrace() { #[test] fn unnamed_implicit_backtrace() { - let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0); + let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, .get_stored_backtrace); @@ -235,7 +235,7 @@ fn unnamed_explicit_no_backtrace() { #[test] fn unnamed_explicit_backtrace() { - let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0); + let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, .get_stored_backtrace); @@ -251,7 +251,8 @@ fn unnamed_explicit_no_backtrace_redundant() { #[test] fn unnamed_explicit_backtrace_redundant() { - let err = TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0); + let err = + TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, .get_stored_backtrace); diff --git a/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs index 394a61b7..df18e49f 100644 --- a/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs +++ b/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs @@ -46,7 +46,7 @@ enum TestErr { i32, ), UnnamedNoBacktraceSourceWithBacktrace(#[error(source)] BacktraceErr, i32), - UnnamedBacktraceNoSource(Backtrace, i32), + UnnamedBacktraceNoSource(Backtrace, i32, i32), UnnamedBacktraceSourceWithoutBacktrace(#[error(source)] SimpleErr, Backtrace, i32), UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( #[error(source, not(backtrace))] BacktraceErr, @@ -54,28 +54,40 @@ enum TestErr { i32, ), UnnamedBacktraceSourceWithBacktrace(#[error(source)] BacktraceErr, Backtrace, i32), + UnnamedBacktraceImplicitSourceWithoutBacktrace(SimpleErr, Backtrace), + UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( + #[error(not(backtrace))] BacktraceErr, + Backtrace, + ), + UnnamedBacktraceImplicitSourceWithBacktrace(BacktraceErr, Backtrace), } impl TestErr { fn get_stored_backtrace(&self) -> &Backtrace { match self { - TestErr::NamedBacktraceSourceWithoutBacktrace { backtrace, .. } => { - backtrace - } - TestErr::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { + Self::NamedBacktraceSourceWithoutBacktrace { backtrace, .. } => backtrace, + Self::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { backtrace, .. } => backtrace, - TestErr::NamedBacktraceSourceWithBacktrace { backtrace, .. } => backtrace, - TestErr::UnnamedBacktraceSourceWithoutBacktrace(_, backtrace, _) => { + Self::NamedBacktraceSourceWithBacktrace { backtrace, .. } => backtrace, + Self::UnnamedBacktraceSourceWithoutBacktrace(_, backtrace, _) => backtrace, + Self::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + _, + backtrace, + _, + ) => backtrace, + Self::UnnamedBacktraceSourceWithBacktrace(_, backtrace, _) => backtrace, + Self::UnnamedBacktraceImplicitSourceWithoutBacktrace(_, backtrace) => { backtrace } - TestErr::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( + Self::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( _, backtrace, - _, ) => backtrace, - TestErr::UnnamedBacktraceSourceWithBacktrace(_, backtrace, _) => backtrace, + Self::UnnamedBacktraceImplicitSourceWithBacktrace(_, backtrace) => { + backtrace + } _ => panic!("ERROR IN TEST IMPLEMENTATION"), } } @@ -198,7 +210,7 @@ fn unnamed_no_backtrace_source_with_backtrace() { #[test] fn unnamed_backtrace_no_source() { - let err = TestErr::UnnamedBacktraceNoSource(Backtrace::force_capture(), 0); + let err = TestErr::UnnamedBacktraceNoSource(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); } @@ -238,3 +250,36 @@ fn unnamed_backtrace_source_with_backtrace() { assert!(err.backtrace().is_some()); assert_bt!(!=, err, .get_stored_backtrace); } + +#[test] +fn unnamed_backtrace_implicit_source_without_backtrace() { + let err = TestErr::UnnamedBacktraceImplicitSourceWithoutBacktrace( + SimpleErr, + Backtrace::force_capture(), + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { + let err = TestErr::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( + BacktraceErr::default(), + Backtrace::force_capture(), + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace() { + let err = TestErr::UnnamedBacktraceImplicitSourceWithBacktrace( + BacktraceErr::default(), + Backtrace::force_capture(), + ); + + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, .get_stored_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 1eec54c5..8574751d 100644 --- a/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs +++ b/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs @@ -53,14 +53,14 @@ enum TestErr { field: T, }, UnnamedImplicitNoBacktrace(T, T), - UnnamedImplicitBacktrace(Backtrace, T), + UnnamedImplicitBacktrace(Backtrace, T, T), UnnamedExplicitNoBacktrace(#[error(not(backtrace))] Backtrace, T), - UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, T), + UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, T, T), UnnamedExplicitNoBacktraceRedundant( #[error(not(backtrace))] MyBacktrace, #[error(not(backtrace))] T, ), - UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, T), + UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, T, T), UnnamedExplicitSupressesImplicit(#[error(backtrace)] MyBacktrace, Backtrace, T), } @@ -82,9 +82,9 @@ impl TestErr { .. } => implicit_backtrace, Self::NamedExplicitSupressesImplicit { not_backtrace, .. } => not_backtrace, - Self::UnnamedImplicitBacktrace(backtrace, _) => backtrace, - Self::UnnamedExplicitBacktrace(backtrace, _) => backtrace, - Self::UnnamedExplicitBacktraceRedundant(backtrace, _) => backtrace, + Self::UnnamedImplicitBacktrace(backtrace, _, _) => backtrace, + Self::UnnamedExplicitBacktrace(backtrace, _, _) => backtrace, + Self::UnnamedExplicitBacktraceRedundant(backtrace, _, _) => backtrace, Self::UnnamedExplicitSupressesImplicit(backtrace, _, _) => backtrace, _ => panic!("ERROR IN TEST IMPLEMENTATION"), } @@ -220,7 +220,7 @@ fn unnamed_implicit_no_backtrace() { #[test] fn unnamed_implicit_backtrace() { - let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0); + let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, .get_stored_backtrace); @@ -235,7 +235,7 @@ fn unnamed_explicit_no_backtrace() { #[test] fn unnamed_explicit_backtrace() { - let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0); + let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, .get_stored_backtrace); @@ -251,7 +251,8 @@ fn unnamed_explicit_no_backtrace_redundant() { #[test] fn unnamed_explicit_backtrace_redundant() { - let err = TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0); + let err = + TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, .get_stored_backtrace); diff --git a/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs index 881195ef..e7e7bd04 100644 --- a/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs +++ b/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs @@ -46,7 +46,7 @@ enum TestErr { T, ), UnnamedNoBacktraceSourceWithBacktrace(#[error(source)] E, T), - UnnamedBacktraceNoSource(Backtrace, T), + UnnamedBacktraceNoSource(Backtrace, T, T), UnnamedBacktraceSourceWithoutBacktrace(#[error(source)] E, Backtrace, T), UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( #[error(source, not(backtrace))] E, @@ -54,6 +54,12 @@ enum TestErr { T, ), UnnamedBacktraceSourceWithBacktrace(#[error(source)] E, Backtrace, T), + UnnamedBacktraceImplicitSourceWithoutBacktrace(E, Backtrace), + UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( + #[error(not(backtrace))] E, + Backtrace, + ), + UnnamedBacktraceImplicitSourceWithBacktrace(E, Backtrace), } impl TestErr { @@ -72,6 +78,16 @@ impl TestErr { _, ) => backtrace, Self::UnnamedBacktraceSourceWithBacktrace(_, backtrace, _) => backtrace, + Self::UnnamedBacktraceImplicitSourceWithoutBacktrace(_, backtrace) => { + backtrace + } + Self::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( + _, + backtrace, + ) => backtrace, + Self::UnnamedBacktraceImplicitSourceWithBacktrace(_, backtrace) => { + backtrace + } _ => panic!("ERROR IN TEST IMPLEMENTATION"), } } @@ -197,6 +213,7 @@ fn unnamed_backtrace_no_source() { let err = TestErr::::UnnamedBacktraceNoSource( Backtrace::force_capture(), 0, + 0, ); assert!(err.backtrace().is_some()); @@ -237,3 +254,36 @@ fn unnamed_backtrace_source_with_backtrace() { assert!(err.backtrace().is_some()); assert_bt!(!=, err, .get_stored_backtrace); } + +#[test] +fn unnamed_backtrace_implicit_source_without_backtrace() { + let err = TestErr::<_, i32>::UnnamedBacktraceImplicitSourceWithoutBacktrace( + SimpleErr, + Backtrace::force_capture(), + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { + let err = TestErr::<_, i32>::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( + BacktraceErr::default(), + Backtrace::force_capture(), + ); + + assert!(err.backtrace().is_some()); + assert_bt!(==, err, .get_stored_backtrace); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace() { + let err = TestErr::<_, i32>::UnnamedBacktraceImplicitSourceWithBacktrace( + BacktraceErr::default(), + Backtrace::force_capture(), + ); + + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, .get_stored_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 2de93b9a..91df87c8 100644 --- a/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs +++ b/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs @@ -201,9 +201,9 @@ fn unnamed_implicit_no_backtrace() { fn unnamed_implicit_backtrace() { derive_display!(TestErr, T); #[derive(Debug, Error)] - struct TestErr(Backtrace, T); + struct TestErr(Backtrace, T, T); - let err = TestErr(Backtrace::force_capture(), 0); + let err = TestErr(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, 0); } @@ -221,11 +221,11 @@ fn unnamed_explicit_no_backtrace() { fn unnamed_explicit_backtrace() { derive_display!(TestErr, T); #[derive(Debug, Error)] - struct TestErr(#[error(backtrace)] MyBacktrace, T); + struct TestErr(#[error(backtrace)] MyBacktrace, T, T); type MyBacktrace = Backtrace; - let err = TestErr(Backtrace::force_capture(), 0); + let err = TestErr(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, 0); } @@ -248,9 +248,9 @@ fn unnamed_explicit_no_backtrace_redundant() { fn unnamed_explicit_backtrace_redundant() { derive_display!(TestErr, T); #[derive(Debug, Error)] - struct TestErr(#[error(backtrace)] Backtrace, T); + struct TestErr(#[error(backtrace)] Backtrace, T, T); - let err = TestErr(Backtrace::force_capture(), 0); + let err = TestErr(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, 0); } diff --git a/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs index 67722b1c..19fd9acb 100644 --- a/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs +++ b/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs @@ -171,9 +171,11 @@ fn unnamed_no_backtrace_source_with_backtrace() { fn unnamed_backtrace_no_source() { derive_display!(TestErr, T); #[derive(Debug, Error)] - struct TestErr(Backtrace, T); + struct TestErr(Backtrace, T, T); - assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_some()); + assert!(TestErr(Backtrace::force_capture(), 0, 0) + .backtrace() + .is_some()); } #[test] @@ -208,3 +210,36 @@ fn unnamed_backtrace_source_with_backtrace() { assert!(err.backtrace().is_some()); assert_bt!(!=, err, 1); } + +#[test] +fn unnamed_backtrace_implicit_source_without_backtrace() { + derive_display!(TestErr, E); + #[derive(Debug, Error)] + struct TestErr(E, Backtrace); + + let err = TestErr(SimpleErr, Backtrace::force_capture()); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr, E); + #[derive(Debug, Error)] + struct TestErr(#[error(not(backtrace))] E, Backtrace); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace() { + derive_display!(TestErr, E); + #[derive(Debug, Error)] + struct TestErr(E, Backtrace); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, 1); +} diff --git a/tests/error/nightly/derives_for_structs_with_backtrace.rs b/tests/error/nightly/derives_for_structs_with_backtrace.rs index 6e0b13f8..18e268f8 100644 --- a/tests/error/nightly/derives_for_structs_with_backtrace.rs +++ b/tests/error/nightly/derives_for_structs_with_backtrace.rs @@ -206,9 +206,9 @@ fn unnamed_implicit_no_backtrace() { fn unnamed_implicit_backtrace() { derive_display!(TestErr); #[derive(Debug, Error)] - struct TestErr(Backtrace, i32); + struct TestErr(Backtrace, i32, i32); - let err = TestErr(Backtrace::force_capture(), 0); + let err = TestErr(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, 0); } @@ -226,11 +226,11 @@ fn unnamed_explicit_no_backtrace() { fn unnamed_explicit_backtrace() { derive_display!(TestErr); #[derive(Debug, Error)] - struct TestErr(#[error(backtrace)] MyBacktrace, i32); + struct TestErr(#[error(backtrace)] MyBacktrace, i32, i32); type MyBacktrace = Backtrace; - let err = TestErr(Backtrace::force_capture(), 0); + let err = TestErr(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, 0); } @@ -253,9 +253,9 @@ fn unnamed_explicit_no_backtrace_redundant() { fn unnamed_explicit_backtrace_redundant() { derive_display!(TestErr); #[derive(Debug, Error)] - struct TestErr(#[error(backtrace)] Backtrace, i32); + struct TestErr(#[error(backtrace)] Backtrace, i32, i32); - let err = TestErr(Backtrace::force_capture(), 0); + let err = TestErr(Backtrace::force_capture(), 0, 0); assert!(err.backtrace().is_some()); assert_bt!(==, err, 0); } diff --git a/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs index 24a6086a..489224c9 100644 --- a/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs +++ b/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs @@ -163,9 +163,11 @@ fn unnamed_no_backtrace_source_with_backtrace() { fn unnamed_backtrace_no_source() { derive_display!(TestErr); #[derive(Debug, Error)] - struct TestErr(Backtrace, i32); + struct TestErr(Backtrace, i32, i32); - assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_some()); + assert!(TestErr(Backtrace::force_capture(), 0, 0) + .backtrace() + .is_some()); } #[test] @@ -204,3 +206,36 @@ fn unnamed_backtrace_source_with_backtrace() { assert!(err.backtrace().is_some()); assert_bt!(!=, err, 1); } + +#[test] +fn unnamed_backtrace_implicit_source_without_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(SimpleErr, Backtrace); + + let err = TestErr(SimpleErr, Backtrace::force_capture()); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(#[error(not(backtrace))] BacktraceErr, Backtrace); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); + assert!(err.backtrace().is_some()); + assert_bt!(==, err, 1); +} + +#[test] +fn unnamed_backtrace_implicit_source_with_backtrace() { + derive_display!(TestErr); + #[derive(Debug, Error)] + struct TestErr(BacktraceErr, Backtrace); + + let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); + assert!(err.backtrace().is_some()); + assert_bt!(!=, err, 1); +} From 319bf301d4dec98fce07a07c238c50ca4651d596 Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Sat, 29 Feb 2020 20:32:08 +0100 Subject: [PATCH 4/8] Remove backtrace forwarding support --- src/error.rs | 95 +----- ...or_enums_with_source_backtrace_chaining.rs | 285 ----------------- ...ic_enums_with_source_backtrace_chaining.rs | 289 ------------------ ..._structs_with_source_backtrace_chaining.rs | 245 --------------- ..._structs_with_source_backtrace_chaining.rs | 241 --------------- tests/error/nightly/mod.rs | 4 - 6 files changed, 8 insertions(+), 1151 deletions(-) delete mode 100644 tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs delete mode 100644 tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs delete mode 100644 tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs delete mode 100644 tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs diff --git a/src/error.rs b/src/error.rs index 24a33163..911b3265 100644 --- a/src/error.rs +++ b/src/error.rs @@ -205,96 +205,17 @@ impl<'input, 'state> ParsedFields<'input, 'state> { } fn render_backtrace_as_struct(&self) -> Option { - self.render_backtrace_impl( - |fields, index| { - let expr = &fields.data.members[index]; - quote!(#expr) - }, - |fields, index| { - let expr = &fields.data.members[index]; - quote!(&#expr) - }, - false, - ) - .map(|(_, expr)| expr) + self.backtrace.map(|backtrace| { + let backtrace_expr = &self.data.members[backtrace]; + quote!(Some(&#backtrace_expr)) + }) } fn render_backtrace_as_enum_variant_match_arm(&self) -> Option { - self.render_backtrace_impl( - |_, _| quote!(source), - |_, _| quote!(backtrace), - true, - ) - .map(|(pattern, expr)| quote!(#pattern => #expr)) - } - - fn render_backtrace_impl( - &self, - source_expr: S, - backtrace_expr: B, - is_rendering_enum_match_arm: bool, - ) -> Option<(TokenStream, TokenStream)> - where - S: Fn(&ParsedFields, usize) -> TokenStream, - B: Fn(&ParsedFields, usize) -> TokenStream, - { - if !cfg!(feature = "nightly") { - return None; - } - - // Disable proxying call to `backtrace` to `source` if - // `source` explicitly marked with `#[error(not(backtrace))]`. - let source = self.source.and_then(|source| { - match self.data.infos[source].info.backtrace { - Some(false) => None, - _ => Some(source), - } - }); - - let mut indices = Vec::new(); - let mut bindings = Vec::new(); - - let mut bind = |index: usize, binding: &TokenStream| { - if is_rendering_enum_match_arm { - indices.push(index); - bindings.push(binding.clone()); - } - }; - - let expr = match (source, self.backtrace) { - (Some(source), backtrace) => { - let source_expr = source_expr(self, source); - bind(source, &source_expr); - - let backtrace_expr = backtrace - .filter(|backtrace| source != *backtrace) - .map(|backtrace| { - let backtrace_expr = backtrace_expr(self, backtrace); - bind(backtrace, &backtrace_expr); - - quote!(.or_else(|| Some(#backtrace_expr))) - }); - - quote!(#source_expr.backtrace()#backtrace_expr) - } - - (None, Some(backtrace)) => { - let backtrace_expr = backtrace_expr(self, backtrace); - bind(backtrace, &backtrace_expr); - - quote!(Some(#backtrace_expr)) - } - - _ => return None, - }; - - let pattern = if is_rendering_enum_match_arm { - self.data.matcher(&indices, &bindings) - } else { - TokenStream::new() - }; - - Some((pattern, expr)) + self.backtrace.map(|backtrace| { + let pattern = self.data.matcher(&[backtrace], &[quote!(backtrace)]); + quote!(#pattern => Some(backtrace)) + }) } } diff --git a/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs deleted file mode 100644 index df18e49f..00000000 --- a/tests/error/nightly/derives_for_enums_with_source_backtrace_chaining.rs +++ /dev/null @@ -1,285 +0,0 @@ -use super::*; - -derive_display!(TestErr); -#[derive(Debug, Error)] -enum TestErr { - NamedNoBacktraceNoSource { - field: i32, - }, - NamedNoBacktraceSourceWithoutBacktrace { - source: SimpleErr, - field: i32, - }, - NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { - #[error(not(backtrace))] - source: BacktraceErr, - field: i32, - }, - NamedNoBacktraceSourceWithBacktrace { - source: BacktraceErr, - field: i32, - }, - NamedBacktraceNoSource { - backtrace: Backtrace, - field: i32, - }, - NamedBacktraceSourceWithoutBacktrace { - source: SimpleErr, - backtrace: Backtrace, - field: i32, - }, - NamedBacktraceSourceWithBacktraceExplicitlyDisabled { - #[error(not(backtrace))] - source: BacktraceErr, - backtrace: Backtrace, - field: i32, - }, - NamedBacktraceSourceWithBacktrace { - source: BacktraceErr, - backtrace: Backtrace, - field: i32, - }, - UnnamedNoBacktraceNoSource(i32, i32), - UnnamedNoBacktraceSourceWithoutBacktrace(#[error(source)] SimpleErr, i32), - UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( - #[error(source, not(backtrace))] BacktraceErr, - i32, - ), - UnnamedNoBacktraceSourceWithBacktrace(#[error(source)] BacktraceErr, i32), - UnnamedBacktraceNoSource(Backtrace, i32, i32), - UnnamedBacktraceSourceWithoutBacktrace(#[error(source)] SimpleErr, Backtrace, i32), - UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( - #[error(source, not(backtrace))] BacktraceErr, - Backtrace, - i32, - ), - UnnamedBacktraceSourceWithBacktrace(#[error(source)] BacktraceErr, Backtrace, i32), - UnnamedBacktraceImplicitSourceWithoutBacktrace(SimpleErr, Backtrace), - UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( - #[error(not(backtrace))] BacktraceErr, - Backtrace, - ), - UnnamedBacktraceImplicitSourceWithBacktrace(BacktraceErr, Backtrace), -} - -impl TestErr { - fn get_stored_backtrace(&self) -> &Backtrace { - match self { - Self::NamedBacktraceSourceWithoutBacktrace { backtrace, .. } => backtrace, - Self::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { - backtrace, - .. - } => backtrace, - Self::NamedBacktraceSourceWithBacktrace { backtrace, .. } => backtrace, - Self::UnnamedBacktraceSourceWithoutBacktrace(_, backtrace, _) => backtrace, - Self::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( - _, - backtrace, - _, - ) => backtrace, - Self::UnnamedBacktraceSourceWithBacktrace(_, backtrace, _) => backtrace, - Self::UnnamedBacktraceImplicitSourceWithoutBacktrace(_, backtrace) => { - backtrace - } - Self::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( - _, - backtrace, - ) => backtrace, - Self::UnnamedBacktraceImplicitSourceWithBacktrace(_, backtrace) => { - backtrace - } - _ => panic!("ERROR IN TEST IMPLEMENTATION"), - } - } -} - -#[test] -fn named_no_backtrace_no_source() { - let err = TestErr::NamedNoBacktraceNoSource { field: 0 }; - - assert!(err.backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_without_backtrace() { - let err = TestErr::NamedNoBacktraceSourceWithoutBacktrace { - source: SimpleErr, - field: 0, - }; - - assert!(err.backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { - let err = TestErr::NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { - source: BacktraceErr::default(), - field: 0, - }; - - assert!(err.backtrace().is_none()); -} - -#[test] -fn named_no_backtrace_source_with_backtrace() { - let err = TestErr::NamedNoBacktraceSourceWithBacktrace { - source: BacktraceErr::default(), - field: 0, - }; - - assert!(err.backtrace().is_some()); -} - -#[test] -fn named_backtrace_no_source() { - let err = TestErr::NamedBacktraceNoSource { - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); -} - -#[test] -fn named_backtrace_source_without_backtrace() { - let err = TestErr::NamedBacktraceSourceWithoutBacktrace { - source: SimpleErr, - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn named_backtrace_source_with_backtrace_explicitly_disabled() { - let err = TestErr::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn named_backtrace_source_with_backtrace() { - let err = TestErr::NamedBacktraceSourceWithBacktrace { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_no_backtrace_no_source() { - let err = TestErr::UnnamedNoBacktraceNoSource(0, 0); - - assert!(err.backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_without_backtrace() { - let err = TestErr::UnnamedNoBacktraceSourceWithoutBacktrace(SimpleErr, 0); - - assert!(err.backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { - let err = TestErr::UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( - BacktraceErr::default(), - 0, - ); - - assert!(err.backtrace().is_none()); -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace() { - let err = - TestErr::UnnamedNoBacktraceSourceWithBacktrace(BacktraceErr::default(), 0); - - assert!(err.backtrace().is_some()); -} - -#[test] -fn unnamed_backtrace_no_source() { - let err = TestErr::UnnamedBacktraceNoSource(Backtrace::force_capture(), 0, 0); - - assert!(err.backtrace().is_some()); -} - -#[test] -fn unnamed_backtrace_source_without_backtrace() { - let err = TestErr::UnnamedBacktraceSourceWithoutBacktrace( - SimpleErr, - Backtrace::force_capture(), - 0, - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace_explictily_disabled() { - let err = TestErr::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( - BacktraceErr::default(), - Backtrace::force_capture(), - 0, - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace() { - let err = TestErr::UnnamedBacktraceSourceWithBacktrace( - BacktraceErr::default(), - Backtrace::force_capture(), - 0, - ); - - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_implicit_source_without_backtrace() { - let err = TestErr::UnnamedBacktraceImplicitSourceWithoutBacktrace( - SimpleErr, - Backtrace::force_capture(), - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { - let err = TestErr::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( - BacktraceErr::default(), - Backtrace::force_capture(), - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace() { - let err = TestErr::UnnamedBacktraceImplicitSourceWithBacktrace( - BacktraceErr::default(), - Backtrace::force_capture(), - ); - - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, .get_stored_backtrace); -} diff --git a/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs deleted file mode 100644 index e7e7bd04..00000000 --- a/tests/error/nightly/derives_for_generic_enums_with_source_backtrace_chaining.rs +++ /dev/null @@ -1,289 +0,0 @@ -use super::*; - -derive_display!(TestErr, E, T); -#[derive(Debug, Error)] -enum TestErr { - NamedNoBacktraceNoSource { - field: T, - }, - NamedNoBacktraceSourceWithoutBacktrace { - source: E, - field: T, - }, - NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { - #[error(not(backtrace))] - source: E, - field: T, - }, - NamedNoBacktraceSourceWithBacktrace { - source: E, - field: T, - }, - NamedBacktraceNoSource { - backtrace: Backtrace, - field: T, - }, - NamedBacktraceSourceWithoutBacktrace { - source: E, - backtrace: Backtrace, - field: T, - }, - NamedBacktraceSourceWithBacktraceExplicitlyDisabled { - #[error(not(backtrace))] - source: E, - backtrace: Backtrace, - field: T, - }, - NamedBacktraceSourceWithBacktrace { - source: E, - backtrace: Backtrace, - field: T, - }, - UnnamedNoBacktraceNoSource(T, T), - UnnamedNoBacktraceSourceWithoutBacktrace(#[error(source)] E, T), - UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( - #[error(source, not(backtrace))] E, - T, - ), - UnnamedNoBacktraceSourceWithBacktrace(#[error(source)] E, T), - UnnamedBacktraceNoSource(Backtrace, T, T), - UnnamedBacktraceSourceWithoutBacktrace(#[error(source)] E, Backtrace, T), - UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( - #[error(source, not(backtrace))] E, - Backtrace, - T, - ), - UnnamedBacktraceSourceWithBacktrace(#[error(source)] E, Backtrace, T), - UnnamedBacktraceImplicitSourceWithoutBacktrace(E, Backtrace), - UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( - #[error(not(backtrace))] E, - Backtrace, - ), - UnnamedBacktraceImplicitSourceWithBacktrace(E, Backtrace), -} - -impl TestErr { - fn get_stored_backtrace(&self) -> &Backtrace { - match self { - Self::NamedBacktraceSourceWithoutBacktrace { backtrace, .. } => backtrace, - Self::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { - backtrace, - .. - } => backtrace, - Self::NamedBacktraceSourceWithBacktrace { backtrace, .. } => backtrace, - Self::UnnamedBacktraceSourceWithoutBacktrace(_, backtrace, _) => backtrace, - Self::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( - _, - backtrace, - _, - ) => backtrace, - Self::UnnamedBacktraceSourceWithBacktrace(_, backtrace, _) => backtrace, - Self::UnnamedBacktraceImplicitSourceWithoutBacktrace(_, backtrace) => { - backtrace - } - Self::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( - _, - backtrace, - ) => backtrace, - Self::UnnamedBacktraceImplicitSourceWithBacktrace(_, backtrace) => { - backtrace - } - _ => panic!("ERROR IN TEST IMPLEMENTATION"), - } - } -} - -#[test] -fn named_no_backtrace_no_source() { - let err = TestErr::::NamedNoBacktraceNoSource { field: 0 }; - - assert!(err.backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_without_backtrace() { - let err = TestErr::NamedNoBacktraceSourceWithoutBacktrace { - source: SimpleErr, - field: 0, - }; - - assert!(err.backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { - let err = TestErr::NamedNoBacktraceSourceWithBacktraceExplicitlyDisabled { - source: BacktraceErr::default(), - field: 0, - }; - - assert!(err.backtrace().is_none()); -} - -#[test] -fn named_no_backtrace_source_with_backtrace() { - let err = TestErr::NamedNoBacktraceSourceWithBacktrace { - source: BacktraceErr::default(), - field: 0, - }; - - assert!(err.backtrace().is_some()); -} - -#[test] -fn named_backtrace_no_source() { - let err = TestErr::::NamedBacktraceNoSource { - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); -} - -#[test] -fn named_backtrace_source_without_backtrace() { - let err = TestErr::NamedBacktraceSourceWithoutBacktrace { - source: SimpleErr, - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn named_backtrace_source_with_backtrace_explicitly_disabled() { - let err = TestErr::NamedBacktraceSourceWithBacktraceExplicitlyDisabled { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn named_backtrace_source_with_backtrace() { - let err = TestErr::NamedBacktraceSourceWithBacktrace { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_no_backtrace_no_source() { - let err = TestErr::::UnnamedNoBacktraceNoSource(0, 0); - - assert!(err.backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_without_backtrace() { - let err = TestErr::UnnamedNoBacktraceSourceWithoutBacktrace(SimpleErr, 0); - - assert!(err.backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { - let err = TestErr::UnnamedNoBacktraceSourceWithBacktraceExplicitlyDisabled( - BacktraceErr::default(), - 0, - ); - - assert!(err.backtrace().is_none()); -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace() { - let err = - TestErr::UnnamedNoBacktraceSourceWithBacktrace(BacktraceErr::default(), 0); - - assert!(err.backtrace().is_some()); -} - -#[test] -fn unnamed_backtrace_no_source() { - let err = TestErr::::UnnamedBacktraceNoSource( - Backtrace::force_capture(), - 0, - 0, - ); - - assert!(err.backtrace().is_some()); -} - -#[test] -fn unnamed_backtrace_source_without_backtrace() { - let err = TestErr::UnnamedBacktraceSourceWithoutBacktrace( - SimpleErr, - Backtrace::force_capture(), - 0, - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace_explictily_disabled() { - let err = TestErr::UnnamedBacktraceSourceWithBacktraceExplictilyDisabled( - BacktraceErr::default(), - Backtrace::force_capture(), - 0, - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace() { - let err = TestErr::UnnamedBacktraceSourceWithBacktrace( - BacktraceErr::default(), - Backtrace::force_capture(), - 0, - ); - - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_implicit_source_without_backtrace() { - let err = TestErr::<_, i32>::UnnamedBacktraceImplicitSourceWithoutBacktrace( - SimpleErr, - Backtrace::force_capture(), - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { - let err = TestErr::<_, i32>::UnnamedBacktraceImplicitSourceWithBacktraceExplicitlyDisabled( - BacktraceErr::default(), - Backtrace::force_capture(), - ); - - assert!(err.backtrace().is_some()); - assert_bt!(==, err, .get_stored_backtrace); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace() { - let err = TestErr::<_, i32>::UnnamedBacktraceImplicitSourceWithBacktrace( - BacktraceErr::default(), - Backtrace::force_capture(), - ); - - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, .get_stored_backtrace); -} diff --git a/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs deleted file mode 100644 index 19fd9acb..00000000 --- a/tests/error/nightly/derives_for_generic_structs_with_source_backtrace_chaining.rs +++ /dev/null @@ -1,245 +0,0 @@ -use super::*; - -#[test] -fn named_no_backtrace_no_source() { - derive_display!(TestErr, T); - #[derive(Default, Debug, Error)] - struct TestErr { - field: T, - } - - assert!(TestErr::::default().backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_without_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Default, Debug, Error)] - struct TestErr { - source: E, - field: T, - } - - assert!(TestErr::::default().backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr, E, T); - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(not(backtrace))] - source: E, - field: T, - } - - assert!(TestErr::::default() - .backtrace() - .is_none()); -} - -#[test] -fn named_no_backtrace_source_with_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Default, Debug, Error)] - struct TestErr { - source: E, - field: T, - } - - assert!(TestErr::::default() - .backtrace() - .is_some()); -} - -#[test] -fn named_backtrace_no_source() { - derive_display!(TestErr, T); - #[derive(Debug, Error)] - struct TestErr { - backtrace: Backtrace, - field: T, - } - - assert!(TestErr { - backtrace: Backtrace::force_capture(), - field: 0 - } - .backtrace() - .is_some()); -} - -#[test] -fn named_backtrace_source_without_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Debug, Error)] - struct TestErr { - source: E, - backtrace: Backtrace, - field: T, - } - - let err = TestErr { - source: SimpleErr, - backtrace: Backtrace::force_capture(), - field: 0, - }; - assert!(err.backtrace().is_some()); - assert_bt!(==, err); -} - -#[test] -fn named_backtrace_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr, E, T); - #[derive(Debug, Error)] - struct TestErr { - #[error(not(backtrace))] - source: E, - backtrace: Backtrace, - field: T, - } - - let err = TestErr { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - assert!(err.backtrace().is_some()); - assert_bt!(==, err); -} - -#[test] -fn named_backtrace_source_with_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Debug, Error)] - struct TestErr { - source: E, - backtrace: Backtrace, - field: T, - } - - let err = TestErr { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - assert!(err.backtrace().is_some()); - assert_bt!(!=, err); -} - -#[test] -fn unnamed_no_backtrace_no_source() { - derive_display!(TestErr, T); - #[derive(Default, Debug, Error)] - struct TestErr(T, T); - - assert!(TestErr::::default().backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_without_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] E, T); - - assert!(TestErr::::default().backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr, E, T); - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source, not(backtrace))] E, T); - - assert!(TestErr::::default() - .backtrace() - .is_none()); -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] E, T); - - assert!(TestErr::::default() - .backtrace() - .is_some()); -} - -#[test] -fn unnamed_backtrace_no_source() { - derive_display!(TestErr, T); - #[derive(Debug, Error)] - struct TestErr(Backtrace, T, T); - - assert!(TestErr(Backtrace::force_capture(), 0, 0) - .backtrace() - .is_some()); -} - -#[test] -fn unnamed_backtrace_source_without_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Debug, Error)] - struct TestErr(#[error(source)] E, Backtrace, T); - - let err = TestErr(SimpleErr, Backtrace::force_capture(), 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace_explictitly_disabled() { - derive_display!(TestErr, E, T); - #[derive(Debug, Error)] - struct TestErr(#[error(source, not(backtrace))] E, Backtrace, T); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace() { - derive_display!(TestErr, E, T); - #[derive(Debug, Error)] - struct TestErr(#[error(source)] E, Backtrace, T); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, 1); -} - -#[test] -fn unnamed_backtrace_implicit_source_without_backtrace() { - derive_display!(TestErr, E); - #[derive(Debug, Error)] - struct TestErr(E, Backtrace); - - let err = TestErr(SimpleErr, Backtrace::force_capture()); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr, E); - #[derive(Debug, Error)] - struct TestErr(#[error(not(backtrace))] E, Backtrace); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace() { - derive_display!(TestErr, E); - #[derive(Debug, Error)] - struct TestErr(E, Backtrace); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, 1); -} diff --git a/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs b/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs deleted file mode 100644 index 489224c9..00000000 --- a/tests/error/nightly/derives_for_structs_with_source_backtrace_chaining.rs +++ /dev/null @@ -1,241 +0,0 @@ -use super::*; - -#[test] -fn named_no_backtrace_no_source() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr { - field: i32, - } - - assert!(TestErr::default().backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_without_backtrace() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr { - source: SimpleErr, - field: i32, - } - - assert!(TestErr::default().backtrace().is_none()) -} - -#[test] -fn named_no_backtrace_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr { - #[error(not(backtrace))] - source: BacktraceErr, - field: i32, - } - - assert!(TestErr::default().backtrace().is_none()); -} - -#[test] -fn named_no_backtrace_source_with_backtrace() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr { - source: BacktraceErr, - field: i32, - } - - assert!(TestErr::default().backtrace().is_some()); -} - -#[test] -fn named_backtrace_no_source() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr { - backtrace: Backtrace, - field: i32, - } - - assert!(TestErr { - backtrace: Backtrace::force_capture(), - field: 0 - } - .backtrace() - .is_some()); -} - -#[test] -fn named_backtrace_source_without_backtrace() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr { - source: SimpleErr, - backtrace: Backtrace, - field: i32, - } - - let err = TestErr { - source: SimpleErr, - backtrace: Backtrace::force_capture(), - field: 0, - }; - assert!(err.backtrace().is_some()); - assert_bt!(==, err); -} - -#[test] -fn named_backtrace_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr { - #[error(not(backtrace))] - source: BacktraceErr, - backtrace: Backtrace, - field: i32, - } - - let err = TestErr { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - assert!(err.backtrace().is_some()); - assert_bt!(==, err); -} - -#[test] -fn named_backtrace_source_with_backtrace() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr { - source: BacktraceErr, - backtrace: Backtrace, - field: i32, - } - - let err = TestErr { - source: BacktraceErr::default(), - backtrace: Backtrace::force_capture(), - field: 0, - }; - assert!(err.backtrace().is_some()); - assert_bt!(!=, err); -} - -#[test] -fn unnamed_no_backtrace_no_source() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr(i32, i32); - - assert!(TestErr::default().backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_without_backtrace() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] SimpleErr, i32); - - assert!(TestErr::default().backtrace().is_none()) -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source, not(backtrace))] BacktraceErr, i32); - - assert!(TestErr::default().backtrace().is_none()); -} - -#[test] -fn unnamed_no_backtrace_source_with_backtrace() { - derive_display!(TestErr); - #[derive(Default, Debug, Error)] - struct TestErr(#[error(source)] BacktraceErr, i32); - - assert!(TestErr::default().backtrace().is_some()); -} - -#[test] -fn unnamed_backtrace_no_source() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr(Backtrace, i32, i32); - - assert!(TestErr(Backtrace::force_capture(), 0, 0) - .backtrace() - .is_some()); -} - -#[test] -fn unnamed_backtrace_source_without_backtrace() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr(#[error(source)] SimpleErr, Backtrace, i32); - - let err = TestErr(SimpleErr, Backtrace::force_capture(), 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace_explictily_disabled() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr( - #[error(source, not(backtrace))] BacktraceErr, - Backtrace, - i32, - ); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_source_with_backtrace() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr(#[error(source)] BacktraceErr, Backtrace, i32); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture(), 0); - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, 1); -} - -#[test] -fn unnamed_backtrace_implicit_source_without_backtrace() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr(SimpleErr, Backtrace); - - let err = TestErr(SimpleErr, Backtrace::force_capture()); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace_explicitly_disabled() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr(#[error(not(backtrace))] BacktraceErr, Backtrace); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); - assert!(err.backtrace().is_some()); - assert_bt!(==, err, 1); -} - -#[test] -fn unnamed_backtrace_implicit_source_with_backtrace() { - derive_display!(TestErr); - #[derive(Debug, Error)] - struct TestErr(BacktraceErr, Backtrace); - - let err = TestErr(BacktraceErr::default(), Backtrace::force_capture()); - assert!(err.backtrace().is_some()); - assert_bt!(!=, err, 1); -} diff --git a/tests/error/nightly/mod.rs b/tests/error/nightly/mod.rs index edb55271..57de7ba2 100644 --- a/tests/error/nightly/mod.rs +++ b/tests/error/nightly/mod.rs @@ -60,13 +60,9 @@ macro_rules! assert_bt { } mod derives_for_enums_with_backtrace; -mod derives_for_enums_with_source_backtrace_chaining; mod derives_for_generic_enums_with_backtrace; -mod derives_for_generic_enums_with_source_backtrace_chaining; mod derives_for_generic_structs_with_backtrace; -mod derives_for_generic_structs_with_source_backtrace_chaining; mod derives_for_structs_with_backtrace; -mod derives_for_structs_with_source_backtrace_chaining; derive_display!(BacktraceErr); #[derive(Debug)] From 8b3e941f68fbad2b94749046f5b1033e20b8576f Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Tue, 10 Mar 2020 08:29:59 +0100 Subject: [PATCH 5/8] Fix linting issues --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5452e731..e96dec10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,8 +183,6 @@ #![recursion_limit = "128"] extern crate proc_macro; -use proc_macro2; -use syn; use proc_macro::TokenStream; use syn::parse::Error as ParseError; From ccf781e45342190cf72f5b346ba577ce7c6a667d Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Tue, 10 Mar 2020 08:30:47 +0100 Subject: [PATCH 6/8] Fix formatting issues --- src/parsing.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/parsing.rs b/src/parsing.rs index c3d09d16..936293f5 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -654,9 +654,11 @@ fn __parse_format_spec<'input>( Matched(__pos, _) => { let __choice_res = { let __seq_res = { - let mut __repeat_pos = + let mut + __repeat_pos = __pos; - let mut __repeat_value = + let mut + __repeat_value = vec![]; loop { let __pos = @@ -717,7 +719,8 @@ fn __parse_format_spec<'input>( let __choice_res = { let mut __repeat_pos = __pos; - let mut __repeat_value = vec![]; + let mut + __repeat_value = vec![]; loop { let __pos = __repeat_pos ; let __step_res = From 9b7b61669cb9c74e9291c3e898e87aff63e41e52 Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Mon, 16 Mar 2020 16:17:16 +0100 Subject: [PATCH 7/8] Review corrections --- src/error.rs | 83 +++++++++++++++++++++++++++++----------------------- src/utils.rs | 16 ++++++++-- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/src/error.rs b/src/error.rs index 911b3265..41f62c55 100644 --- a/src/error.rs +++ b/src/error.rs @@ -241,7 +241,7 @@ fn parse_fields<'input, 'state>( "source" => ident == "source", "backtrace" => { ident == "backtrace" - || is_type_ends_with(&field.ty, "Backtrace") + || is_type_path_ends_with_segment(&field.ty, "Backtrace") } _ => unreachable!(), } @@ -249,28 +249,23 @@ fn parse_fields<'input, 'state>( } DeriveType::Unnamed => { - let result = parse_fields_impl(state, |attr, field, len| match attr { - "source" => len == 1 && !is_type_ends_with(&field.ty, "Backtrace"), - "backtrace" => is_type_ends_with(&field.ty, "Backtrace"), - _ => unreachable!(), - }); - - result.map(|mut parsed_fields| { - let len_source_backtrace = ( - state.fields.len(), - parsed_fields.source, - parsed_fields.backtrace, - ); - - if let (2, None, Some(backtrace)) = len_source_backtrace { - let source = (backtrace + 1) % 2; - if parsed_fields.data.infos[source].info.source != Some(false) { - parsed_fields.source = Some(source); + let mut parsed_fields = + parse_fields_impl(state, |attr, field, len| match attr { + "source" => { + len == 1 + && !is_type_path_ends_with_segment(&field.ty, "Backtrace") } - } + "backtrace" => { + is_type_path_ends_with_segment(&field.ty, "Backtrace") + } + _ => unreachable!(), + })?; - parsed_fields - }) + parsed_fields.source = parsed_fields + .source + .or_else(|| infer_source_field(state, &parsed_fields)); + + Ok(parsed_fields) } _ => unreachable!(), @@ -289,7 +284,9 @@ fn parse_fields<'input, 'state>( }) } -fn is_type_ends_with(ty: &syn::Type, tail: &str) -> bool { +/// Checks if `ty` is [`syn::Type::Path`] and ends with segment matching `tail` +/// and doesn't contain any generic parameters. +fn is_type_path_ends_with_segment(ty: &syn::Type, tail: &str) -> bool { let ty = match ty { syn::Type::Path(ty) => ty, _ => return false, @@ -297,14 +294,35 @@ fn is_type_ends_with(ty: &syn::Type, tail: &str) -> bool { // Unwrapping is safe, cause 'syn::TypePath.path.segments' // have to have at least one segment - let ty = ty.path.segments.last().unwrap(); + let segment = ty.path.segments.last().unwrap(); - match ty.arguments { + match segment.arguments { syn::PathArguments::None => (), _ => return false, }; - ty.ident == tail + segment.ident == tail +} + +fn infer_source_field(state: &State, parsed_fields: &ParsedFields) -> Option { + let len_source_backtrace = ( + state.fields.len(), + parsed_fields.source, + parsed_fields.backtrace, + ); + + // If we have exactly two fields, one of the fields was specified/inferred as backtrace field, + // but no source field was specified/inferred... + if let (2, None, Some(backtrace)) = len_source_backtrace { + // ...then infer other field as source field... + let source = (backtrace + 1) % 2; + // ...unless it was explicitly marked as non-source. + if parsed_fields.data.infos[source].info.source != Some(false) { + return Some(source); + } + } + + None } fn parse_fields_impl<'input, 'state, P>( @@ -322,24 +340,20 @@ where .enumerate() .map(|(index, (field, info))| (index, *field, info)); - let mut source = None; - parse_field_impl( + let source = parse_field_impl( &is_valid_default_field_for_attr, state.fields.len(), iter.clone(), "source", |info| info.source, - &mut source, )?; - let mut backtrace = None; - parse_field_impl( + let backtrace = parse_field_impl( &is_valid_default_field_for_attr, state.fields.len(), iter.clone(), "backtrace", |info| info.backtrace, - &mut backtrace, )?; let mut parsed_fields = ParsedFields::new(state.enabled_fields_data()); @@ -361,8 +375,7 @@ fn parse_field_impl<'a, P, V>( iter: impl Iterator + Clone, attr: &str, value: V, - out: &mut Option<(usize, &'a syn::Field, &'a MetaInfo)>, -) -> Result<()> +) -> Result> where P: Fn(&str, &syn::Field, usize) -> bool, V: Fn(&MetaInfo) -> Option, @@ -395,9 +408,7 @@ where )?, }; - *out = field; - - Ok(()) + Ok(field) } fn assert_iter_contains_zero_or_one_item<'a>( diff --git a/src/utils.rs b/src/utils.rs index 08344fc9..77cae30c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -381,6 +381,20 @@ impl<'input> State<'input> { .filter_map(|info| info.enabled.map(|_| info)) .next(); + // Default to enabled true, except when first attribute has explicit + // enabling. + // + // Except for derive Error. + // + // The way `else` case works is that if any field have any valid + // attribute specified, then all fields without any attributes + // specified are filtered out from `State::enabled_fields`. + // + // However, derive Error *infers* fields and there are cases when + // one of the fields may have an attribute specified, but another field + // would be inferred. So, for derive Error macro we default enabled + // to true unconditionally (i.e., even if some fields have attributes + // specified). let default_enabled = if trait_name == "Error" { true } else { @@ -388,8 +402,6 @@ impl<'input> State<'input> { }; let defaults = struct_meta_info.to_full(FullMetaInfo { - // Default to enabled true, except when first attribute has explicit - // enabling enabled: default_enabled, forward: false, // Default to owned true, except when first attribute has one of owned, From 16655471dad62eb3a674112b2c371353132ebba7 Mon Sep 17 00:00:00 2001 From: Roman Titov Date: Thu, 19 Mar 2020 12:23:33 +0100 Subject: [PATCH 8/8] Corrections --- src/error.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/error.rs b/src/error.rs index 41f62c55..85dbe18e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -263,7 +263,7 @@ fn parse_fields<'input, 'state>( parsed_fields.source = parsed_fields .source - .or_else(|| infer_source_field(state, &parsed_fields)); + .or_else(|| infer_source_field(&state.fields, &parsed_fields)); Ok(parsed_fields) } @@ -304,19 +304,25 @@ fn is_type_path_ends_with_segment(ty: &syn::Type, tail: &str) -> bool { segment.ident == tail } -fn infer_source_field(state: &State, parsed_fields: &ParsedFields) -> Option { - let len_source_backtrace = ( - state.fields.len(), - parsed_fields.source, - parsed_fields.backtrace, - ); - - // If we have exactly two fields, one of the fields was specified/inferred as backtrace field, - // but no source field was specified/inferred... - if let (2, None, Some(backtrace)) = len_source_backtrace { - // ...then infer other field as source field... +fn infer_source_field( + fields: &[&syn::Field], + parsed_fields: &ParsedFields, +) -> Option { + // if we have exactly two fields + if fields.len() != 2 { + return None; + } + + // no source field was specified/inferred + if parsed_fields.source.is_some() { + return None; + } + + // but one of the fields was specified/inferred as backtrace field + if let Some(backtrace) = parsed_fields.backtrace { + // then infer *other field* as source field let source = (backtrace + 1) % 2; - // ...unless it was explicitly marked as non-source. + // unless it was explicitly marked as non-source if parsed_fields.data.infos[source].info.source != Some(false) { return Some(source); }