Move more tree analysis logic to prop.rs

This commit is contained in:
David Tolnay 2019-10-12 12:20:04 -07:00
parent 3b279475b2
commit 68c18d6ecf
No known key found for this signature in database
GPG key ID: F9BA143B95FF6D82
2 changed files with 89 additions and 67 deletions

View file

@ -3,7 +3,7 @@ use crate::valid;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{DeriveInput, Member, Result, Type};
use syn::{DeriveInput, Member, Result};
pub fn derive(node: &DeriveInput) -> Result<TokenStream> {
let input = Input::from_syn(node)?;
@ -18,8 +18,7 @@ fn impl_struct(input: Struct) -> TokenStream {
let ty = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let source = source_member(&input.fields);
let source_method = source.map(|source| {
let source_method = input.source_member().map(|source| {
let member = quote_spanned!(source.span()=> self.#source);
quote! {
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
@ -29,8 +28,7 @@ fn impl_struct(input: Struct) -> TokenStream {
}
});
let backtrace = backtrace_member(&input.fields);
let backtrace_method = backtrace.map(|backtrace| {
let backtrace_method = input.backtrace_member().map(|backtrace| {
quote! {
fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
std::option::Option::Some(&self.#backtrace)
@ -38,7 +36,7 @@ fn impl_struct(input: Struct) -> TokenStream {
}
});
let display = input.attrs.display.as_ref().map(|display| {
let display_impl = input.attrs.display.as_ref().map(|display| {
let pat = fields_pat(&input.fields);
quote! {
impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
@ -56,7 +54,7 @@ fn impl_struct(input: Struct) -> TokenStream {
#source_method
#backtrace_method
}
#display
#display_impl
}
}
@ -64,22 +62,10 @@ fn impl_enum(input: Enum) -> TokenStream {
let ty = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let sources: Vec<Option<&Member>> = input
.variants
.iter()
.map(|variant| source_member(&variant.fields))
.collect();
let backtraces: Vec<Option<&Member>> = input
.variants
.iter()
.map(|variant| backtrace_member(&variant.fields))
.collect();
let source_method = if sources.iter().any(Option::is_some) {
let arms = input.variants.iter().zip(sources).map(|(variant, source)| {
let source_method = if input.has_source() {
let arms = input.variants.iter().map(|variant| {
let ident = &variant.ident;
match source {
match variant.source_member() {
Some(source) => quote! {
#ty::#ident {#source: source, ..} => std::option::Option::Some(source.as_dyn_error()),
},
@ -100,10 +86,10 @@ fn impl_enum(input: Enum) -> TokenStream {
None
};
let backtrace_method = if backtraces.iter().any(Option::is_some) {
let arms = input.variants.iter().zip(backtraces).map(|(variant, backtrace)| {
let backtrace_method = if input.has_backtrace() {
let arms = input.variants.iter().map(|variant| {
let ident = &variant.ident;
match backtrace {
match variant.backtrace_member() {
Some(backtrace) => quote! {
#ty::#ident {#backtrace: backtrace, ..} => std::option::Option::Some(backtrace),
},
@ -123,18 +109,15 @@ fn impl_enum(input: Enum) -> TokenStream {
None
};
let display = if input.has_display() {
let arms = input
.variants
.iter()
.map(|variant| {
let display = variant.attrs.display.as_ref().expect(valid::CHECKED);
let ident = &variant.ident;
let pat = fields_pat(&variant.fields);
quote! {
#ty::#ident #pat => #display
}
});
let display_impl = if input.has_display() {
let arms = input.variants.iter().map(|variant| {
let display = variant.attrs.display.as_ref().expect(valid::CHECKED);
let ident = &variant.ident;
let pat = fields_pat(&variant.fields);
quote! {
#ty::#ident #pat => #display
}
});
Some(quote! {
impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
@ -154,38 +137,10 @@ fn impl_enum(input: Enum) -> TokenStream {
#source_method
#backtrace_method
}
#display
#display_impl
}
}
fn source_member<'a>(fields: &'a [Field]) -> Option<&'a Member> {
for field in fields {
if field.attrs.source {
return Some(&field.member);
}
}
None
}
fn backtrace_member<'a>(fields: &'a [Field]) -> Option<&'a Member> {
for field in fields {
if type_is_backtrace(&field.ty) {
return Some(&field.member);
}
}
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 fields_pat(fields: &[Field]) -> TokenStream {
let mut members = fields.iter().map(|field| &field.member).peekable();
match members.peek() {

View file

@ -1,9 +1,76 @@
use crate::ast::Enum;
use crate::ast::{Enum, Field, Struct, Variant};
use syn::{Member, Type};
impl Struct<'_> {
pub(crate) fn source_member(&self) -> Option<&Member> {
source_member(&self.fields)
}
pub(crate) fn backtrace_member(&self) -> Option<&Member> {
backtrace_member(&self.fields)
}
}
impl Enum<'_> {
pub(crate) fn has_source(&self) -> bool {
self.variants
.iter()
.any(|variant| variant.source_member().is_some())
}
pub(crate) fn has_backtrace(&self) -> bool {
self.variants
.iter()
.any(|variant| variant.backtrace_member().is_some())
}
pub(crate) fn has_display(&self) -> bool {
self.variants
.iter()
.any(|variant| variant.attrs.display.is_some())
}
}
impl Variant<'_> {
pub(crate) fn source_member(&self) -> Option<&Member> {
source_member(&self.fields)
}
pub(crate) fn backtrace_member(&self) -> Option<&Member> {
backtrace_member(&self.fields)
}
}
impl Field<'_> {
fn is_source(&self) -> bool {
self.attrs.source
}
fn is_backtrace(&self) -> bool {
type_is_backtrace(self.ty)
}
}
fn source_member<'a>(fields: &'a [Field]) -> Option<&'a Member> {
fields
.iter()
.find(|field| field.is_source())
.map(|field| &field.member)
}
fn backtrace_member<'a>(fields: &'a [Field]) -> Option<&'a Member> {
fields
.iter()
.find(|field| field.is_backtrace())
.map(|field| &field.member)
}
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()
}