mirror of
https://github.com/dtolnay/thiserror.git
synced 2025-04-04 13:27:38 +03:00
Move more tree analysis logic to prop.rs
This commit is contained in:
parent
3b279475b2
commit
68c18d6ecf
2 changed files with 89 additions and 67 deletions
|
@ -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() {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue