From c86452cc68c95c28ddba296b270d60c66a91c287 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 9 Oct 2019 07:12:42 -0700 Subject: [PATCH] Add backtrace() method for structs --- impl/src/expand.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index ac79304..e89299e 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -3,7 +3,7 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{ Data, DataEnum, DataStruct, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed, Index, - Member, Result, + Member, Result, Type, }; pub fn derive(input: &DeriveInput) -> Result { @@ -27,6 +27,12 @@ fn struct_error(input: &DeriveInput, data: &DataStruct) -> Result { Fields::Unit => None, }; + let backtrace = match &data.fields { + Fields::Named(fields) => braced_struct_backtrace(fields)?, + Fields::Unnamed(fields) => tuple_struct_backtrace(fields)?, + Fields::Unit => None, + }; + let source_method = source.map(|source| { quote! { fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { @@ -35,9 +41,18 @@ fn struct_error(input: &DeriveInput, data: &DataStruct) -> Result { } }); + let backtrace_method = backtrace.map(|backtrace| { + quote! { + fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { + std::option::Option::Some(&self.#backtrace) + } + } + }); + Ok(quote! { impl #impl_generics std::error::Error for #ident #ty_generics #where_clause { #source_method + #backtrace_method } }) } @@ -60,6 +75,34 @@ fn tuple_struct_source(fields: &FieldsUnnamed) -> Result> { Ok(None) } +fn braced_struct_backtrace(fields: &FieldsNamed) -> Result> { + for field in &fields.named { + if type_is_backtrace(&field.ty) { + return Ok(Some(Member::Named(field.ident.as_ref().unwrap().clone()))); + } + } + Ok(None) +} + +fn tuple_struct_backtrace(fields: &FieldsUnnamed) -> Result> { + for (i, field) in fields.unnamed.iter().enumerate() { + if type_is_backtrace(&field.ty) { + return Ok(Some(Member::Unnamed(Index::from(i)))); + } + } + Ok(None) +} + +fn type_is_backtrace(ty: &Type) -> bool { + let path = match ty { + Type::Path(ty) => &ty.path, + _ => return false, + }; + + let last = path.segments.last().unwrap(); + last.ident == "Backtrace" && last.arguments.is_empty() +} + fn enum_error(input: &DeriveInput, data: &DataEnum) -> Result { let _ = input; let _ = data;