Add backtrace() method for structs

This commit is contained in:
David Tolnay 2019-10-09 07:12:42 -07:00
parent 4cdeec15e5
commit c86452cc68
No known key found for this signature in database
GPG key ID: F9BA143B95FF6D82

View file

@ -3,7 +3,7 @@ use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use syn::{ use syn::{
Data, DataEnum, DataStruct, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed, Index, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed, Index,
Member, Result, Member, Result, Type,
}; };
pub fn derive(input: &DeriveInput) -> Result<TokenStream> { pub fn derive(input: &DeriveInput) -> Result<TokenStream> {
@ -27,6 +27,12 @@ fn struct_error(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
Fields::Unit => None, 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| { let source_method = source.map(|source| {
quote! { quote! {
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
@ -35,9 +41,18 @@ fn struct_error(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
} }
}); });
let backtrace_method = backtrace.map(|backtrace| {
quote! {
fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
std::option::Option::Some(&self.#backtrace)
}
}
});
Ok(quote! { Ok(quote! {
impl #impl_generics std::error::Error for #ident #ty_generics #where_clause { impl #impl_generics std::error::Error for #ident #ty_generics #where_clause {
#source_method #source_method
#backtrace_method
} }
}) })
} }
@ -60,6 +75,34 @@ fn tuple_struct_source(fields: &FieldsUnnamed) -> Result<Option<Member>> {
Ok(None) Ok(None)
} }
fn braced_struct_backtrace(fields: &FieldsNamed) -> Result<Option<Member>> {
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<Option<Member>> {
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<TokenStream> { fn enum_error(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
let _ = input; let _ = input;
let _ = data; let _ = data;