Support backtrace in From impl

This commit is contained in:
David Tolnay 2019-10-12 17:37:17 -07:00
parent f40910293b
commit 46de723f20
No known key found for this signature in database
GPG key ID: F9BA143B95FF6D82
3 changed files with 38 additions and 7 deletions

View file

@ -90,12 +90,13 @@ fn impl_struct(input: Struct) -> TokenStream {
});
let from_impl = input.from_field().map(|from_field| {
let member = &from_field.member;
let backtrace_field = input.backtrace_field();
let from = from_field.ty;
let body = from_initializer(from_field, backtrace_field);
quote! {
impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause {
fn from(source: #from) -> Self {
#ty { #member: source }
#ty #body
}
}
}
@ -243,13 +244,14 @@ fn impl_enum(input: Enum) -> TokenStream {
let from_impls = input.variants.iter().filter_map(|variant| {
let from_field = variant.from_field()?;
let backtrace_field = variant.backtrace_field();
let variant = &variant.ident;
let member = &from_field.member;
let from = from_field.ty;
let body = from_initializer(from_field, backtrace_field);
Some(quote! {
impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause {
fn from(source: #from) -> Self {
#ty::#variant { #member: source }
#ty::#variant #body
}
}
})
@ -280,6 +282,26 @@ fn fields_pat(fields: &[Field]) -> TokenStream {
}
}
fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream {
let from_member = &from_field.member;
let backtrace = backtrace_field.map(|backtrace_field| {
let backtrace_member = &backtrace_field.member;
if type_is_option(backtrace_field.ty) {
quote! {
#backtrace_member: std::option::Option::Some(std::backtrace::Backtrace::capture()),
}
} else {
quote! {
#backtrace_member: std::backtrace::Backtrace::capture(),
}
}
});
quote!({
#from_member: source,
#backtrace
})
}
fn type_is_option(ty: &Type) -> bool {
let path = match ty {
Type::Path(ty) => &ty.path,

View file

@ -51,6 +51,12 @@ impl Variant<'_> {
}
}
impl Field<'_> {
pub(crate) fn is_backtrace(&self) -> bool {
type_is_backtrace(self.ty)
}
}
fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
for field in fields {
if field.attrs.from.is_some() {
@ -82,7 +88,7 @@ fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
}
}
for field in fields {
if type_is_backtrace(field.ty) {
if field.is_backtrace() {
return Some(&field);
}
}

View file

@ -104,6 +104,7 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> {
let mut from_field = None;
let mut source_field = None;
let mut backtrace_field = None;
let mut has_backtrace = false;
for field in fields {
if let Some(from) = field.attrs.from {
if from_field.is_some() {
@ -125,7 +126,9 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> {
));
}
backtrace_field = Some(field);
has_backtrace = true;
}
has_backtrace |= field.is_backtrace();
}
if let (Some(from_field), Some(source_field)) = (from_field, source_field) {
if !same_member(from_field, source_field) {
@ -136,10 +139,10 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> {
}
}
if let Some(from_field) = from_field {
if fields.len() > 1 {
if fields.len() > 1 + has_backtrace as usize {
return Err(Error::new_spanned(
from_field.attrs.from,
"deriving From requires no fields other than source",
"deriving From requires no fields other than source and backtrace",
));
}
}