diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/derivative/src/debug.rs | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/vendor/derivative/src/debug.rs b/vendor/derivative/src/debug.rs new file mode 100644 index 000000000..ed667c1d7 --- /dev/null +++ b/vendor/derivative/src/debug.rs @@ -0,0 +1,225 @@ +use proc_macro2; + +use ast; +use attr; +use matcher; +use syn; +use syn::spanned::Spanned; +use utils; + +pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream { + let debug_trait_path = debug_trait_path(); + let fmt_path = fmt_path(); + + let formatter = quote_spanned! {input.span=> __f}; + + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed) + .with_field_filter(|f: &ast::Field| !f.attrs.ignore_debug()) + .build_arms(input, "__arg", |_, _, arm_name, style, attrs, bis| { + let field_prints = bis.iter().filter_map(|bi| { + if bi.field.attrs.ignore_debug() { + return None; + } + + if attrs.debug_transparent() { + return Some(quote_spanned! {arm_name.span()=> + #debug_trait_path::fmt(__arg_0, #formatter) + }); + } + + let arg_expr = &bi.expr; + let arg_ident = &bi.ident; + + let dummy_debug = bi.field.attrs.debug_format_with().map(|format_fn| { + format_with( + bi.field, + &input.attrs.debug_bound(), + &arg_expr, + &arg_ident, + format_fn, + input.generics.clone(), + ) + }); + let expr = if bi.field.attrs.debug_format_with().is_some() { + quote_spanned! {arm_name.span()=> + &#arg_ident + } + } else { + quote_spanned! {arm_name.span()=> + &&#arg_expr + } + }; + + let builder = if let Some(ref name) = bi.field.ident { + let name = name.to_string(); + quote_spanned! {arm_name.span()=> + #dummy_debug + let _ = __debug_trait_builder.field(#name, #expr); + } + } else { + quote_spanned! {arm_name.span()=> + #dummy_debug + let _ = __debug_trait_builder.field(#expr); + } + }; + + Some(builder) + }); + + let method = match style { + ast::Style::Struct => "debug_struct", + ast::Style::Tuple | ast::Style::Unit => "debug_tuple", + }; + let method = syn::Ident::new(method, proc_macro2::Span::call_site()); + + if attrs.debug_transparent() { + quote_spanned! {arm_name.span()=> + #(#field_prints)* + } + } else { + let name = arm_name.to_string(); + quote_spanned! {arm_name.span()=> + let mut __debug_trait_builder = #formatter.#method(#name); + #(#field_prints)* + __debug_trait_builder.finish() + } + } + }); + + let name = &input.ident; + + let generics = utils::build_impl_generics( + input, + &debug_trait_path, + needs_debug_bound, + |field| field.debug_bound(), + |input| input.debug_bound(), + ); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + // don't attach a span to prevent issue #58 + let match_self = quote!(match *self); + quote_spanned! {input.span=> + #[allow(unused_qualifications)] + #[allow(clippy::unneeded_field_pattern)] + impl #impl_generics #debug_trait_path for #name #ty_generics #where_clause { + fn fmt(&self, #formatter: &mut #fmt_path::Formatter) -> #fmt_path::Result { + #match_self { + #body + } + } + } + } +} + +fn needs_debug_bound(attrs: &attr::Field) -> bool { + !attrs.ignore_debug() && attrs.debug_bound().is_none() +} + +/// Return the path of the `Debug` trait, that is `::std::fmt::Debug`. +fn debug_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::fmt::Debug) + } else { + parse_quote!(::std::fmt::Debug) + } +} + +/// Return the path of the `fmt` module, that is `::std::fmt`. +fn fmt_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::fmt) + } else { + parse_quote!(::std::fmt) + } +} + +/// Return the path of the `PhantomData` type, that is `::std::marker::PhantomData`. +fn phantom_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::marker::PhantomData) + } else { + parse_quote!(::std::marker::PhantomData) + } +} + +fn format_with( + f: &ast::Field, + bounds: &Option<&[syn::WherePredicate]>, + arg_expr: &proc_macro2::TokenStream, + arg_ident: &syn::Ident, + format_fn: &syn::Path, + mut generics: syn::Generics, +) -> proc_macro2::TokenStream { + let debug_trait_path = debug_trait_path(); + let fmt_path = fmt_path(); + let phantom_path = phantom_path(); + + generics + .make_where_clause() + .predicates + .extend(f.attrs.debug_bound().unwrap_or(&[]).iter().cloned()); + + generics + .params + .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new( + parse_quote!('_derivative), + ))); + let where_predicates = generics + .type_params() + .map(|ty| { + let mut bounds = syn::punctuated::Punctuated::new(); + bounds.push(syn::TypeParamBound::Lifetime(syn::Lifetime::new( + "'_derivative", + proc_macro2::Span::call_site(), + ))); + + let path = syn::Path::from(syn::PathSegment::from(ty.ident.clone())); + + syn::WherePredicate::Type(syn::PredicateType { + lifetimes: None, + bounded_ty: syn::Type::Path(syn::TypePath { qself: None, path }), + colon_token: Default::default(), + bounds, + }) + }) + .chain(bounds.iter().flat_map(|b| b.iter().cloned())) + .collect::<Vec<_>>(); + generics + .make_where_clause() + .predicates + .extend(where_predicates); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let ty = f.ty; + + // Leave off the type parameter bounds, defaults, and attributes + let phantom = generics.type_params().map(|tp| &tp.ident); + + let mut ctor_generics = generics.clone(); + *ctor_generics + .lifetimes_mut() + .last() + .expect("There must be a '_derivative lifetime") = syn::LifetimeDef::new(parse_quote!('_)); + let (_, ctor_ty_generics, _) = ctor_generics.split_for_impl(); + let ctor_ty_generics = ctor_ty_generics.as_turbofish(); + + // don't attach a span to prevent issue #58 + let match_self = quote!(match self.0); + quote_spanned!(format_fn.span()=> + let #arg_ident = { + struct Dummy #impl_generics (&'_derivative #ty, #phantom_path <(#(#phantom,)*)>) #where_clause; + + impl #impl_generics #debug_trait_path for Dummy #ty_generics #where_clause { + fn fmt(&self, __f: &mut #fmt_path::Formatter) -> #fmt_path::Result { + #match_self { + this => #format_fn(this, __f) + } + } + } + + Dummy #ctor_ty_generics (&&#arg_expr, #phantom_path) + }; + ) +} |