diff options
Diffstat (limited to 'vendor/educe/src/trait_handlers/debug')
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/common.rs | 55 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/debug_enum.rs | 371 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/debug_struct.rs | 185 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/debug_union.rs | 86 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/mod.rs | 38 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/models/field_attribute.rs | 224 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/models/mod.rs | 5 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/models/type_attribute.rs | 264 | ||||
| -rw-r--r-- | vendor/educe/src/trait_handlers/debug/panic.rs | 43 |
9 files changed, 1271 insertions, 0 deletions
diff --git a/vendor/educe/src/trait_handlers/debug/common.rs b/vendor/educe/src/trait_handlers/debug/common.rs new file mode 100644 index 00000000..e2c59a9d --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/common.rs @@ -0,0 +1,55 @@ +use quote::quote; +use syn::{DeriveInput, Path, Type}; + +#[inline] +pub(crate) fn create_debug_map_builder() -> proc_macro2::TokenStream { + quote!( + #[allow(non_camel_case_types)] // We're using __ to help avoid clashes. + struct Educe__RawString(&'static str); + + impl ::core::fmt::Debug for Educe__RawString { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.write_str(self.0) + } + } + + let mut builder = f.debug_map(); + ) +} + +#[inline] +pub(crate) fn create_format_arg( + ast: &DeriveInput, + field_ty: &Type, + format_method: &Path, + field_expr: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let ty_ident = &ast.ident; + + // We use the complete original generics, not filtered by field, + // and include a PhantomData<Self> in our wrapper struct to use the generics. + // + // This avoids having to try to calculate the right *subset* of the generics + // relevant for this field, which is nontrivial and maybe impossible. + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + + quote!( + let arg = { + #[allow(non_camel_case_types)] // We're using __ to help avoid clashes. + struct Educe__DebugField<V, M>(V, ::core::marker::PhantomData<M>); + + impl #impl_generics ::core::fmt::Debug + for Educe__DebugField<&#field_ty, #ty_ident #ty_generics> + #where_clause + { + #[inline] + fn fmt(&self, educe__f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + #format_method(self.0, educe__f) + } + } + + Educe__DebugField(#field_expr, ::core::marker::PhantomData::<Self>) + }; + ) +} diff --git a/vendor/educe/src/trait_handlers/debug/debug_enum.rs b/vendor/educe/src/trait_handlers/debug/debug_enum.rs new file mode 100644 index 00000000..1e423818 --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/debug_enum.rs @@ -0,0 +1,371 @@ +use quote::{format_ident, quote, ToTokens}; +use syn::{Data, DeriveInput, Fields, Meta, Type}; + +use super::models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName}; +use crate::{common::path::path_to_string, supported_traits::Trait, trait_handlers::TraitHandler}; + +pub(crate) struct DebugEnumHandler; + +impl TraitHandler for DebugEnumHandler { + fn trait_meta_handler( + ast: &DeriveInput, + token_stream: &mut proc_macro2::TokenStream, + traits: &[Trait], + meta: &Meta, + ) -> syn::Result<()> { + let type_attribute = TypeAttributeBuilder { + enable_flag: true, + enable_unsafe: false, + enable_name: true, + enable_named_field: false, + enable_bound: true, + name: TypeName::Disable, + named_field: false, + } + .build_from_debug_meta(meta)?; + + let name = type_attribute.name.to_ident_by_ident(&ast.ident); + + let mut debug_types: Vec<&Type> = Vec::new(); + + let mut builder_token_stream = proc_macro2::TokenStream::new(); + + let mut arms_token_stream = proc_macro2::TokenStream::new(); + + if let Data::Enum(data) = &ast.data { + for variant in data.variants.iter() { + let type_attribute = TypeAttributeBuilder { + enable_flag: false, + enable_unsafe: false, + enable_name: true, + enable_named_field: true, + enable_bound: false, + name: TypeName::Default, + named_field: matches!(&variant.fields, Fields::Named(_)), + } + .build_from_attributes(&variant.attrs, traits)?; + + let variant_ident = &variant.ident; + + let variant_name = type_attribute.name.to_ident_by_ident(variant_ident); + + let named_field = type_attribute.named_field; + + let name_string = if let Some(name) = name { + if let Some(variant_name) = variant_name { + Some(path_to_string(&syn::parse2(quote!(#name::#variant_name)).unwrap())) + } else { + Some(name.into_token_stream().to_string()) + } + } else { + variant_name.map(|variant_name| variant_name.into_token_stream().to_string()) + }; + + match &variant.fields { + Fields::Unit => { + if name_string.is_none() { + return Err(super::panic::unit_variant_need_name(variant)); + } + + arms_token_stream + .extend(quote!( Self::#variant_ident => f.write_str(#name_string), )); + }, + Fields::Named(fields) => { + let mut has_fields = false; + + let mut pattern_token_stream = proc_macro2::TokenStream::new(); + let mut block_token_stream = proc_macro2::TokenStream::new(); + + if named_field { + block_token_stream + .extend(create_named_field_builder(name_string.as_deref())); + + for field in fields.named.iter() { + let field_attribute = FieldAttributeBuilder { + enable_name: true, + enable_ignore: true, + enable_method: true, + name: FieldName::Default, + } + .build_from_attributes(&field.attrs, traits)?; + + let field_name_real = field.ident.as_ref().unwrap(); + let field_name_var = format_ident!("_{}", field_name_real); + + if field_attribute.ignore { + pattern_token_stream.extend(quote!(#field_name_real: _,)); + + continue; + } + + let key = match field_attribute.name { + FieldName::Custom(name) => name, + FieldName::Default => field_name_real.clone(), + }; + + pattern_token_stream + .extend(quote!(#field_name_real: #field_name_var,)); + + let ty = &field.ty; + + if let Some(method) = field_attribute.method { + block_token_stream.extend(super::common::create_format_arg( + ast, + ty, + &method, + quote!(#field_name_var), + )); + + block_token_stream.extend(if name_string.is_some() { + quote! (builder.field(stringify!(#key), &arg);) + } else { + quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);) + }); + } else { + debug_types.push(ty); + + block_token_stream.extend(if name_string.is_some() { + quote! (builder.field(stringify!(#key), #field_name_var);) + } else { + quote! (builder.entry(&Educe__RawString(stringify!(#key)), #field_name_var);) + }); + } + + has_fields = true; + } + } else { + block_token_stream + .extend(quote!(let mut builder = f.debug_tuple(#name_string);)); + + for field in fields.named.iter() { + let field_attribute = FieldAttributeBuilder { + enable_name: false, + enable_ignore: true, + enable_method: true, + name: FieldName::Default, + } + .build_from_attributes(&field.attrs, traits)?; + + let field_name_real = field.ident.as_ref().unwrap(); + let field_name_var = format_ident!("_{}", field_name_real); + + if field_attribute.ignore { + pattern_token_stream.extend(quote!(#field_name_real: _,)); + + continue; + } + + pattern_token_stream + .extend(quote!(#field_name_real: #field_name_var,)); + + let ty = &field.ty; + + if let Some(method) = field_attribute.method { + block_token_stream.extend(super::common::create_format_arg( + ast, + ty, + &method, + quote!(#field_name_var), + )); + + block_token_stream.extend(quote! (builder.field(&arg);)); + } else { + debug_types.push(ty); + + block_token_stream + .extend(quote! (builder.field(#field_name_var);)); + } + + has_fields = true; + } + } + + if !has_fields && name_string.is_none() { + return Err(super::panic::unit_struct_need_name(variant_ident)); + } + + arms_token_stream.extend(quote! { + Self::#variant_ident { #pattern_token_stream } => { + #block_token_stream + + builder.finish() + }, + }); + }, + Fields::Unnamed(fields) => { + let mut has_fields = false; + + let mut pattern_token_stream = proc_macro2::TokenStream::new(); + let mut block_token_stream = proc_macro2::TokenStream::new(); + + if named_field { + block_token_stream + .extend(create_named_field_builder(name_string.as_deref())); + + for (index, field) in fields.unnamed.iter().enumerate() { + let field_attribute = FieldAttributeBuilder { + enable_name: true, + enable_ignore: true, + enable_method: true, + name: FieldName::Default, + } + .build_from_attributes(&field.attrs, traits)?; + + if field_attribute.ignore { + pattern_token_stream.extend(quote!(_,)); + + continue; + } + + let field_name_var = format_ident!("_{}", index); + + let key = match field_attribute.name { + FieldName::Custom(name) => name, + FieldName::Default => field_name_var.clone(), + }; + + pattern_token_stream.extend(quote!(#field_name_var,)); + + let ty = &field.ty; + + if let Some(method) = field_attribute.method { + block_token_stream.extend(super::common::create_format_arg( + ast, + ty, + &method, + quote!(#field_name_var), + )); + + block_token_stream.extend(if name_string.is_some() { + quote! (builder.field(stringify!(#key), &arg);) + } else { + quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);) + }); + } else { + debug_types.push(ty); + + block_token_stream.extend(if name_string.is_some() { + quote! (builder.field(stringify!(#key), #field_name_var);) + } else { + quote! (builder.entry(&Educe__RawString(stringify!(#key)), #field_name_var);) + }); + } + + has_fields = true; + } + } else { + block_token_stream + .extend(quote!(let mut builder = f.debug_tuple(#name_string);)); + + for (index, field) in fields.unnamed.iter().enumerate() { + let field_attribute = FieldAttributeBuilder { + enable_name: false, + enable_ignore: true, + enable_method: true, + name: FieldName::Default, + } + .build_from_attributes(&field.attrs, traits)?; + + if field_attribute.ignore { + pattern_token_stream.extend(quote!(_,)); + + continue; + } + + let field_name_var = format_ident!("_{}", index); + + pattern_token_stream.extend(quote!(#field_name_var,)); + + let ty = &field.ty; + + if let Some(method) = field_attribute.method { + block_token_stream.extend(super::common::create_format_arg( + ast, + ty, + &method, + quote!(#field_name_var), + )); + + block_token_stream.extend(quote! (builder.field(&arg);)); + } else { + debug_types.push(ty); + + block_token_stream + .extend(quote! (builder.field(#field_name_var);)); + } + + has_fields = true; + } + } + + if !has_fields && name_string.is_none() { + return Err(super::panic::unit_struct_need_name(variant_ident)); + } + + arms_token_stream.extend(quote! { + Self::#variant_ident ( #pattern_token_stream ) => { + #block_token_stream + + builder.finish() + }, + }); + }, + } + } + } + + let ident = &ast.ident; + + if arms_token_stream.is_empty() { + if let Some(ident) = name { + builder_token_stream.extend(quote! { + f.write_str(stringify!(#ident)) + }); + } else { + return Err(super::panic::unit_enum_need_name(ident)); + } + } else { + builder_token_stream.extend(quote! { + match self { + #arms_token_stream + } + }); + } + + let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types( + &ast.generics.params, + &syn::parse2(quote!(::core::fmt::Debug)).unwrap(), + &debug_types, + &[], + ); + + let mut generics = ast.generics.clone(); + let where_clause = generics.make_where_clause(); + + for where_predicate in bound { + where_clause.predicates.push(where_predicate); + } + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + token_stream.extend(quote! { + impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #builder_token_stream + } + } + }); + + Ok(()) + } +} + +#[inline] +fn create_named_field_builder(name_string: Option<&str>) -> proc_macro2::TokenStream { + if let Some(name_string) = name_string { + quote!(let mut builder = f.debug_struct(#name_string);) + } else { + super::common::create_debug_map_builder() + } +} diff --git a/vendor/educe/src/trait_handlers/debug/debug_struct.rs b/vendor/educe/src/trait_handlers/debug/debug_struct.rs new file mode 100644 index 00000000..6430480b --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/debug_struct.rs @@ -0,0 +1,185 @@ +use quote::{format_ident, quote}; +use syn::{Data, DeriveInput, Fields, Meta, Type}; + +use super::{ + models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName}, + TraitHandler, +}; +use crate::{common::ident_index::IdentOrIndex, Trait}; + +pub struct DebugStructHandler; + +impl TraitHandler for DebugStructHandler { + fn trait_meta_handler( + ast: &DeriveInput, + token_stream: &mut proc_macro2::TokenStream, + traits: &[Trait], + meta: &Meta, + ) -> syn::Result<()> { + let is_tuple = { + if let Data::Struct(data) = &ast.data { + matches!(data.fields, Fields::Unnamed(_)) + } else { + true + } + }; + + let type_attribute = TypeAttributeBuilder { + enable_flag: true, + enable_unsafe: false, + enable_name: true, + enable_named_field: true, + enable_bound: true, + name: TypeName::Default, + named_field: !is_tuple, + } + .build_from_debug_meta(meta)?; + + let name = type_attribute.name.to_ident_by_ident(&ast.ident); + + let mut debug_types: Vec<&Type> = Vec::new(); + + let mut builder_token_stream = proc_macro2::TokenStream::new(); + let mut has_fields = false; + + if type_attribute.named_field { + builder_token_stream.extend(if let Some(name) = name { + quote!(let mut builder = f.debug_struct(stringify!(#name));) + } else { + super::common::create_debug_map_builder() + }); + + if let Data::Struct(data) = &ast.data { + for (index, field) in data.fields.iter().enumerate() { + let field_attribute = FieldAttributeBuilder { + enable_name: true, + enable_ignore: true, + enable_method: true, + name: FieldName::Default, + } + .build_from_attributes(&field.attrs, traits)?; + + if field_attribute.ignore { + continue; + } + + let (key, field_name) = match field_attribute.name { + FieldName::Custom(name) => { + (name, IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index)) + }, + FieldName::Default => { + if let Some(ident) = field.ident.as_ref() { + (ident.clone(), IdentOrIndex::from(ident)) + } else { + (format_ident!("_{}", index), IdentOrIndex::from(index)) + } + }, + }; + + let ty = &field.ty; + + if let Some(method) = field_attribute.method { + builder_token_stream.extend(super::common::create_format_arg( + ast, + ty, + &method, + quote!(&self.#field_name), + )); + + builder_token_stream.extend(if name.is_some() { + quote! (builder.field(stringify!(#key), &arg);) + } else { + quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);) + }); + } else { + debug_types.push(ty); + + builder_token_stream.extend(if name.is_some() { + quote! (builder.field(stringify!(#key), &self.#field_name);) + } else { + quote! (builder.entry(&Educe__RawString(stringify!(#key)), &self.#field_name);) + }); + } + + has_fields = true; + } + } + } else { + builder_token_stream + .extend(quote!(let mut builder = f.debug_tuple(stringify!(#name));)); + + if let Data::Struct(data) = &ast.data { + for (index, field) in data.fields.iter().enumerate() { + let field_attribute = FieldAttributeBuilder { + enable_name: false, + enable_ignore: true, + enable_method: true, + name: FieldName::Default, + } + .build_from_attributes(&field.attrs, traits)?; + + if field_attribute.ignore { + continue; + } + + let field_name = + IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index); + + let ty = &field.ty; + + if let Some(method) = field_attribute.method { + builder_token_stream.extend(super::common::create_format_arg( + ast, + ty, + &method, + quote!(&self.#field_name), + )); + + builder_token_stream.extend(quote! (builder.field(&arg);)); + } else { + debug_types.push(ty); + + builder_token_stream.extend(quote! (builder.field(&self.#field_name);)); + } + + has_fields = true; + } + } + } + + let ident = &ast.ident; + + if !has_fields && name.is_none() { + return Err(super::panic::unit_struct_need_name(ident)); + } + + let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types( + &ast.generics.params, + &syn::parse2(quote!(::core::fmt::Debug)).unwrap(), + &debug_types, + &[], + ); + + let mut generics = ast.generics.clone(); + let where_clause = generics.make_where_clause(); + + for where_predicate in bound { + where_clause.predicates.push(where_predicate); + } + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + token_stream.extend(quote! { + impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #builder_token_stream + + builder.finish() + } + } + }); + + Ok(()) + } +} diff --git a/vendor/educe/src/trait_handlers/debug/debug_union.rs b/vendor/educe/src/trait_handlers/debug/debug_union.rs new file mode 100644 index 00000000..096ea776 --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/debug_union.rs @@ -0,0 +1,86 @@ +use quote::quote; +use syn::{Data, DeriveInput, Meta}; + +use super::{ + models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName}, + TraitHandler, +}; +use crate::supported_traits::Trait; + +pub(crate) struct DebugUnionHandler; + +impl TraitHandler for DebugUnionHandler { + fn trait_meta_handler( + ast: &DeriveInput, + token_stream: &mut proc_macro2::TokenStream, + traits: &[Trait], + meta: &Meta, + ) -> syn::Result<()> { + let type_attribute = TypeAttributeBuilder { + enable_flag: true, + enable_unsafe: true, + enable_name: true, + enable_named_field: false, + enable_bound: false, + name: TypeName::Default, + named_field: false, + } + .build_from_debug_meta(meta)?; + + if !type_attribute.has_unsafe { + return Err(super::panic::union_without_unsafe(meta)); + } + + let name = type_attribute.name.to_ident_by_ident(&ast.ident); + + let mut builder_token_stream = proc_macro2::TokenStream::new(); + + if let Data::Union(data) = &ast.data { + for field in data.fields.named.iter() { + let _ = FieldAttributeBuilder { + enable_name: false, + enable_ignore: false, + enable_method: false, + name: FieldName::Default, + } + .build_from_attributes(&field.attrs, traits)?; + } + + if let Some(name) = name { + builder_token_stream.extend(quote!( + let mut builder = f.debug_tuple(stringify!(#name)); + + let size = ::core::mem::size_of::<Self>(); + + let data = unsafe { ::core::slice::from_raw_parts(self as *const Self as *const u8, size) }; + + builder.field(&data); + + builder.finish() + )); + } else { + builder_token_stream.extend(quote!( + let size = ::core::mem::size_of::<Self>(); + let data = unsafe { ::core::slice::from_raw_parts(self as *const Self as *const u8, size) }; + + ::core::fmt::Debug::fmt(data, f) + )); + } + } + + let ident = &ast.ident; + + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + + token_stream.extend(quote! { + impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + #builder_token_stream + } + } + }); + + Ok(()) + } +} diff --git a/vendor/educe/src/trait_handlers/debug/mod.rs b/vendor/educe/src/trait_handlers/debug/mod.rs new file mode 100644 index 00000000..367ddb71 --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/mod.rs @@ -0,0 +1,38 @@ +mod common; +mod debug_enum; +mod debug_struct; +mod debug_union; +mod models; +mod panic; + +use syn::{Data, DeriveInput, Meta}; + +use super::TraitHandler; +use crate::Trait; + +pub(crate) struct DebugHandler; + +impl TraitHandler for DebugHandler { + #[inline] + fn trait_meta_handler( + ast: &DeriveInput, + token_stream: &mut proc_macro2::TokenStream, + traits: &[Trait], + meta: &Meta, + ) -> syn::Result<()> { + match ast.data { + Data::Struct(_) => debug_struct::DebugStructHandler::trait_meta_handler( + ast, + token_stream, + traits, + meta, + ), + Data::Enum(_) => { + debug_enum::DebugEnumHandler::trait_meta_handler(ast, token_stream, traits, meta) + }, + Data::Union(_) => { + debug_union::DebugUnionHandler::trait_meta_handler(ast, token_stream, traits, meta) + }, + } + } +} diff --git a/vendor/educe/src/trait_handlers/debug/models/field_attribute.rs b/vendor/educe/src/trait_handlers/debug/models/field_attribute.rs new file mode 100644 index 00000000..f2cd3e28 --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/models/field_attribute.rs @@ -0,0 +1,224 @@ +use syn::{punctuated::Punctuated, Attribute, Ident, Meta, Path, Token}; + +use crate::{ + common::{ + ident_bool::{ + meta_2_bool_allow_path, meta_2_ident, meta_name_value_2_bool, meta_name_value_2_ident, + meta_name_value_2_ident_and_bool, IdentOrBool, + }, + path::meta_2_path, + }, + panic, + supported_traits::Trait, +}; + +#[derive(Debug, Clone)] +pub(crate) enum FieldName { + Default, + Custom(Ident), +} + +pub(crate) struct FieldAttribute { + pub(crate) name: FieldName, + pub(crate) ignore: bool, + pub(crate) method: Option<Path>, +} + +pub(crate) struct FieldAttributeBuilder { + pub(crate) enable_name: bool, + pub(crate) enable_ignore: bool, + pub(crate) enable_method: bool, + pub(crate) name: FieldName, +} + +impl FieldAttributeBuilder { + pub(crate) fn build_from_debug_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> { + debug_assert!(meta.path().is_ident("Debug")); + + let mut name = self.name.clone(); + let mut ignore = false; + let mut method = None; + + let correct_usage_for_debug_attribute = { + let mut usage = vec![]; + + if self.enable_name { + usage.push(stringify!(#[educe(Debug = NewName)])); + usage.push(stringify!(#[educe(Debug(name(NewName)))])); + } + + if self.enable_ignore { + usage.push(stringify!(#[educe(Debug = false)])); + usage.push(stringify!(#[educe(Debug(ignore))])); + } + + if self.enable_method { + usage.push(stringify!(#[educe(Debug(method(path_to_method)))])); + } + + usage + }; + + match meta { + Meta::Path(_) => { + return Err(panic::attribute_incorrect_format( + meta.path().get_ident().unwrap(), + &correct_usage_for_debug_attribute, + )); + }, + Meta::NameValue(name_value) => { + if self.enable_name { + if self.enable_ignore { + match meta_name_value_2_ident_and_bool(name_value)? { + IdentOrBool::Ident(ident) => { + name = FieldName::Custom(ident); + }, + IdentOrBool::Bool(b) => { + ignore = !b; + }, + } + } else { + name = FieldName::Custom(meta_name_value_2_ident(name_value)?); + } + } else if self.enable_ignore { + ignore = !meta_name_value_2_bool(name_value)?; + } else { + return Err(panic::attribute_incorrect_format( + meta.path().get_ident().unwrap(), + &correct_usage_for_debug_attribute, + )); + } + }, + Meta::List(list) => { + let result = + list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; + + let mut name_is_set = false; + let mut ignore_is_set = false; + let mut method_is_set = false; + + let mut handler = |meta: Meta| -> syn::Result<bool> { + if let Some(ident) = meta.path().get_ident() { + match ident.to_string().as_str() { + "name" | "rename" => { + if !self.enable_name { + return Ok(false); + } + + let v = meta_2_ident(&meta)?; + + if name_is_set { + return Err(panic::parameter_reset(ident)); + } + + name_is_set = true; + + name = FieldName::Custom(v); + + return Ok(true); + }, + "ignore" => { + if !self.enable_ignore { + return Ok(false); + } + + let v = meta_2_bool_allow_path(&meta)?; + + if ignore_is_set { + return Err(panic::parameter_reset(ident)); + } + + ignore_is_set = true; + + ignore = v; + + return Ok(true); + }, + "method" => { + if !self.enable_method { + return Ok(false); + } + + let v = meta_2_path(&meta)?; + + if method_is_set { + return Err(panic::parameter_reset(ident)); + } + + method_is_set = true; + + method = Some(v); + + return Ok(true); + }, + _ => (), + } + } + + Ok(false) + }; + + for p in result { + if !handler(p)? { + return Err(panic::attribute_incorrect_format( + meta.path().get_ident().unwrap(), + &correct_usage_for_debug_attribute, + )); + } + } + }, + } + + Ok(FieldAttribute { + name, + ignore, + method, + }) + } + + pub(crate) fn build_from_attributes( + &self, + attributes: &[Attribute], + traits: &[Trait], + ) -> syn::Result<FieldAttribute> { + let mut output = None; + + for attribute in attributes.iter() { + let path = attribute.path(); + + if path.is_ident("educe") { + if let Meta::List(list) = &attribute.meta { + let result = + list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; + + for meta in result { + let path = meta.path(); + + let t = match Trait::from_path(path) { + Some(t) => t, + None => return Err(panic::unsupported_trait(meta.path())), + }; + + if !traits.contains(&t) { + return Err(panic::trait_not_used(path.get_ident().unwrap())); + } + + if t == Trait::Debug { + if output.is_some() { + return Err(panic::reuse_a_trait(path.get_ident().unwrap())); + } + + output = Some(self.build_from_debug_meta(&meta)?); + } + } + } + } + } + + Ok(output.unwrap_or(FieldAttribute { + name: self.name.clone(), + ignore: false, + method: None, + })) + } +} diff --git a/vendor/educe/src/trait_handlers/debug/models/mod.rs b/vendor/educe/src/trait_handlers/debug/models/mod.rs new file mode 100644 index 00000000..1e4681f8 --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/models/mod.rs @@ -0,0 +1,5 @@ +mod field_attribute; +mod type_attribute; + +pub(crate) use field_attribute::*; +pub(crate) use type_attribute::*; diff --git a/vendor/educe/src/trait_handlers/debug/models/type_attribute.rs b/vendor/educe/src/trait_handlers/debug/models/type_attribute.rs new file mode 100644 index 00000000..ed84ddf3 --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/models/type_attribute.rs @@ -0,0 +1,264 @@ +use proc_macro2::Ident; +use syn::{punctuated::Punctuated, Attribute, Meta, Token}; + +use crate::{ + common::{ + bound::Bound, + ident_bool::{meta_2_bool, meta_2_ident_and_bool, meta_name_value_2_ident, IdentOrBool}, + unsafe_punctuated_meta::UnsafePunctuatedMeta, + }, + panic, Trait, +}; + +#[derive(Debug, Clone)] +pub(crate) enum TypeName { + Disable, + Default, + Custom(Ident), +} + +impl TypeName { + #[inline] + pub(crate) fn to_ident_by_ident<'a, 'b: 'a>(&'a self, ident: &'b Ident) -> Option<&'a Ident> { + match self { + Self::Disable => None, + Self::Default => Some(ident), + Self::Custom(ident) => Some(ident), + } + } +} + +pub(crate) struct TypeAttribute { + pub(crate) has_unsafe: bool, + pub(crate) name: TypeName, + pub(crate) named_field: bool, + pub(crate) bound: Bound, +} + +#[derive(Debug)] +pub(crate) struct TypeAttributeBuilder { + pub(crate) enable_flag: bool, + pub(crate) enable_unsafe: bool, + pub(crate) enable_name: bool, + pub(crate) enable_named_field: bool, + pub(crate) enable_bound: bool, + pub(crate) name: TypeName, + pub(crate) named_field: bool, +} + +impl TypeAttributeBuilder { + pub(crate) fn build_from_debug_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> { + debug_assert!(meta.path().is_ident("Debug")); + + let mut has_unsafe = false; + let mut name = self.name.clone(); + let mut named_field = self.named_field; + let mut bound = Bound::Auto; + + let correct_usage_for_debug_attribute = { + let mut usage = vec![]; + + if self.enable_flag { + usage.push(stringify!(#[educe(Debug)])); + } + + if self.enable_name { + if !self.enable_unsafe { + usage.push(stringify!(#[educe(Debug = NewName)])); + } + + usage.push(stringify!(#[educe(Debug(name(NewName)))])); + + if let TypeName::Disable = &name { + usage.push(stringify!(#[educe(Debug(name = true))])); + } else { + usage.push(stringify!(#[educe(Debug(name = false))])); + } + } + + if self.enable_named_field { + if !self.named_field { + usage.push(stringify!(#[educe(Debug(named_field = true))])); + } else { + usage.push(stringify!(#[educe(Debug(named_field = false))])); + } + } + + if self.enable_bound { + usage.push(stringify!(#[educe(Debug(bound(where_predicates)))])); + usage.push(stringify!(#[educe(Debug(bound = false))])); + } + + usage + }; + + match meta { + Meta::Path(_) => { + if !self.enable_flag { + return Err(panic::attribute_incorrect_format( + meta.path().get_ident().unwrap(), + &correct_usage_for_debug_attribute, + )); + } + }, + Meta::NameValue(name_value) => { + if !self.enable_name { + return Err(panic::attribute_incorrect_format( + meta.path().get_ident().unwrap(), + &correct_usage_for_debug_attribute, + )); + } + + name = TypeName::Custom(meta_name_value_2_ident(name_value)?); + }, + Meta::List(list) => { + let result = if self.enable_unsafe { + let result: UnsafePunctuatedMeta = list.parse_args()?; + + has_unsafe = result.has_unsafe; + + result.list + } else { + list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? + }; + + let mut name_is_set = false; + let mut named_field_is_set = false; + let mut bound_is_set = false; + + let mut handler = |meta: Meta| -> syn::Result<bool> { + if let Some(ident) = meta.path().get_ident() { + match ident.to_string().as_str() { + "name" | "rename" => { + if !self.enable_name { + return Ok(false); + } + + let v = meta_2_ident_and_bool(&meta)?; + + if name_is_set { + return Err(panic::parameter_reset(ident)); + } + + name_is_set = true; + + name = match v { + IdentOrBool::Ident(ident) => TypeName::Custom(ident), + IdentOrBool::Bool(b) => { + if b { + TypeName::Default + } else { + TypeName::Disable + } + }, + }; + + return Ok(true); + }, + "named_field" => { + if !self.enable_named_field { + return Ok(false); + } + + let v = meta_2_bool(&meta)?; + + if named_field_is_set { + return Err(panic::parameter_reset(ident)); + } + + named_field_is_set = true; + + named_field = v; + + return Ok(true); + }, + "bound" => { + if !self.enable_bound { + return Ok(false); + } + + let v = Bound::from_meta(&meta)?; + + if bound_is_set { + return Err(panic::parameter_reset(ident)); + } + + bound_is_set = true; + + bound = v; + + return Ok(true); + }, + _ => (), + } + } + + Ok(false) + }; + + for p in result { + if !handler(p)? { + return Err(panic::attribute_incorrect_format( + meta.path().get_ident().unwrap(), + &correct_usage_for_debug_attribute, + )); + } + } + }, + } + + Ok(TypeAttribute { + has_unsafe, + name, + named_field, + bound, + }) + } + + pub(crate) fn build_from_attributes( + &self, + attributes: &[Attribute], + traits: &[Trait], + ) -> syn::Result<TypeAttribute> { + let mut output = None; + + for attribute in attributes.iter() { + let path = attribute.path(); + + if path.is_ident("educe") { + if let Meta::List(list) = &attribute.meta { + let result = + list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; + + for meta in result { + let path = meta.path(); + + let t = match Trait::from_path(path) { + Some(t) => t, + None => return Err(panic::unsupported_trait(meta.path())), + }; + + if !traits.contains(&t) { + return Err(panic::trait_not_used(path.get_ident().unwrap())); + } + + if t == Trait::Debug { + if output.is_some() { + return Err(panic::reuse_a_trait(path.get_ident().unwrap())); + } + + output = Some(self.build_from_debug_meta(&meta)?); + } + } + } + } + } + + Ok(output.unwrap_or(TypeAttribute { + has_unsafe: false, + name: self.name.clone(), + named_field: self.named_field, + bound: Bound::Auto, + })) + } +} diff --git a/vendor/educe/src/trait_handlers/debug/panic.rs b/vendor/educe/src/trait_handlers/debug/panic.rs new file mode 100644 index 00000000..a29646a7 --- /dev/null +++ b/vendor/educe/src/trait_handlers/debug/panic.rs @@ -0,0 +1,43 @@ +use quote::ToTokens; +use syn::{spanned::Spanned, Ident, Meta, Variant}; + +#[inline] +pub(crate) fn unit_struct_need_name(name: &Ident) -> syn::Error { + syn::Error::new(name.span(), "a unit struct needs to have a name") +} + +#[inline] +pub(crate) fn unit_variant_need_name(variant: &Variant) -> syn::Error { + syn::Error::new( + variant.span(), + "a unit variant which doesn't use an enum name needs to have a name", + ) +} + +#[inline] +pub(crate) fn unit_enum_need_name(name: &Ident) -> syn::Error { + syn::Error::new(name.span(), "a unit enum needs to have a name") +} + +#[inline] +pub(crate) fn union_without_unsafe(meta: &Meta) -> syn::Error { + let mut s = meta.into_token_stream().to_string().replace(" , ", ", "); + + match s.len() { + 5 => s.push_str("(unsafe)"), + 7 => s.insert_str(6, "unsafe"), + _ => s.insert_str(6, "unsafe, "), + } + + syn::Error::new( + meta.span(), + format!( + "a union's `Debug` implementation may expose uninitialized memory\n* It is \ + recommended that, for a union where `Debug` is implemented, types that allow \ + uninitialized memory should not be used in it.\n* If you can ensure that the union \ + uses no such types, use `#[educe({s})]` to implement the `Debug` trait for it.\n* \ + The `unsafe` keyword should be placed as the first parameter of the `Debug` \ + attribute." + ), + ) +} |
