From e4e505d02c0041c00bf8fc672d43a950603eac85 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 11:07:35 +0100 Subject: [PATCH 01/14] Preparation: impl Clone for IdentOrIndex We'll use this shortly. --- src/common/ident_index.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/ident_index.rs b/src/common/ident_index.rs index c0e38d4..23403cb 100644 --- a/src/common/ident_index.rs +++ b/src/common/ident_index.rs @@ -1,6 +1,7 @@ use quote::ToTokens; use syn::{Ident, Index}; +#[derive(Clone)] pub(crate) enum IdentOrIndex { Ident(Ident), Index(Index), From 62bc987c764e1ff54f38558c97a2e3f0aeeeb3b4 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 10:30:46 +0100 Subject: [PATCH 02/14] Preparation: Impl IdentFragment for IdentOrIndex We'll use this in a moment. --- src/common/ident_index.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/common/ident_index.rs b/src/common/ident_index.rs index 23403cb..496ac59 100644 --- a/src/common/ident_index.rs +++ b/src/common/ident_index.rs @@ -1,4 +1,6 @@ -use quote::ToTokens; +use std::fmt; + +use quote::{IdentFragment, ToTokens}; use syn::{Ident, Index}; #[derive(Clone)] @@ -55,3 +57,12 @@ impl IdentOrIndex { } } } + +impl IdentFragment for IdentOrIndex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Ident(ident) => IdentFragment::fmt(ident, f), + Self::Index(index) => IdentFragment::fmt(index, f), + } + } +} From aead687414f7c0355172447c2140c3f0ace067c6 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 10:32:11 +0100 Subject: [PATCH 03/14] Introduce FieldInfo and use in clone_enum No functional change, but this prepares the code for unification of handling of named and unnamed fields. --- src/common/field_info.rs | 32 ++++++++++++++++++++++++++ src/common/mod.rs | 2 ++ src/trait_handlers/clone/clone_enum.rs | 19 ++++++++------- 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 src/common/field_info.rs diff --git a/src/common/field_info.rs b/src/common/field_info.rs new file mode 100644 index 0000000..212c799 --- /dev/null +++ b/src/common/field_info.rs @@ -0,0 +1,32 @@ +use std::ops::Deref; + +use syn::Field; + +use crate::common::ident_index::IdentOrIndex; + +/// A field, in a `Fields::Named` or a `Fields::Unnamed` +/// +/// Allows unification of the data handling code, +/// to always use `{ }` syntax even for tuple data. +pub(crate) struct FieldInfo<'f> { + pub(crate) name: IdentOrIndex, + pub(crate) field: &'f Field, +} + +impl<'f> FieldInfo<'f> { + pub(crate) fn new(index: usize, field: &'f Field) -> Self { + let name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index); + FieldInfo { + name, + field, + } + } +} + +impl<'f> Deref for FieldInfo<'f> { + type Target = &'f Field; + + fn deref(&self) -> &&'f Field { + &self.field + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 79e10bb..f18965a 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,6 +1,8 @@ #[allow(dead_code)] pub(crate) mod bound; #[allow(dead_code)] +pub(crate) mod field_info; +#[allow(dead_code)] pub(crate) mod path; #[allow(dead_code)] pub(crate) mod r#type; diff --git a/src/trait_handlers/clone/clone_enum.rs b/src/trait_handlers/clone/clone_enum.rs index 991243a..7732889 100644 --- a/src/trait_handlers/clone/clone_enum.rs +++ b/src/trait_handlers/clone/clone_enum.rs @@ -1,9 +1,10 @@ use quote::{format_ident, quote}; -use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Meta, Type, Variant}; +use syn::{punctuated::Punctuated, Data, DeriveInput, Fields, Meta, Type, Variant}; use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder}; use crate::{ common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler, + common::field_info::FieldInfo, }; pub(crate) struct CloneEnumHandler; @@ -27,7 +28,7 @@ impl TraitHandler for CloneEnumHandler { let mut clone_from_token_stream = proc_macro2::TokenStream::new(); if let Data::Enum(data) = &ast.data { - type Variants<'a> = Vec<(&'a Variant, Vec<(&'a Field, FieldAttribute)>)>; + type Variants<'a> = Vec<(&'a Variant, Vec<(FieldInfo<'a>, FieldAttribute)>)>; let mut variants: Variants = Vec::new(); @@ -40,9 +41,9 @@ impl TraitHandler for CloneEnumHandler { } .build_from_attributes(&variant.attrs, traits)?; - let mut variant_fields: Vec<(&Field, FieldAttribute)> = Vec::new(); + let mut variant_fields: Vec<(FieldInfo, FieldAttribute)> = Vec::new(); - for field in variant.fields.iter() { + for (index, field) in variant.fields.iter().enumerate() { let field_attribute = FieldAttributeBuilder { enable_method: true } @@ -53,7 +54,7 @@ impl TraitHandler for CloneEnumHandler { has_custom_clone_method = true; } - variant_fields.push((field, field_attribute)); + variant_fields.push((FieldInfo::new(index, field), field_attribute)); } variants.push((variant, variant_fields)); @@ -105,7 +106,7 @@ impl TraitHandler for CloneEnumHandler { let mut cf_body_token_stream = proc_macro2::TokenStream::new(); for (field, field_attribute) in variant_fields { - let field_name_real = field.ident.as_ref().unwrap(); + let field_name_real = &field.name; let field_name_src = format_ident!("_s_{}", field_name_real); let field_name_dst = format_ident!("_d_{}", field_name_real); @@ -153,10 +154,8 @@ impl TraitHandler for CloneEnumHandler { let mut fields_token_stream = proc_macro2::TokenStream::new(); let mut body_token_stream = proc_macro2::TokenStream::new(); - for (index, (field, field_attribute)) in - variant_fields.into_iter().enumerate() - { - let field_name_src = format_ident!("_{}", index); + for (field, field_attribute) in variant_fields { + let field_name_src = format_ident!("_{}", field.name); pattern_token_stream.extend(quote!(#field_name_src,)); From 10cd174716d176a868ed5b021c239db910371309 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 10:33:16 +0100 Subject: [PATCH 04/14] Use braced syntax even for tuple and units, in clone_enum Demonstrate the named-vs-unnamed-agnostic approach. Unit, Named and Unnamed are all now handled by the named fields code, using the `{ }` syntax which Rust allows for every `Data`. The `match` statement is redundant, but removing it will involve a deindent so is very textually invasive. We'll do that in the next commit. --- src/trait_handlers/clone/clone_enum.rs | 64 +------------------------- 1 file changed, 2 insertions(+), 62 deletions(-) diff --git a/src/trait_handlers/clone/clone_enum.rs b/src/trait_handlers/clone/clone_enum.rs index 7732889..46840c9 100644 --- a/src/trait_handlers/clone/clone_enum.rs +++ b/src/trait_handlers/clone/clone_enum.rs @@ -1,5 +1,5 @@ use quote::{format_ident, quote}; -use syn::{punctuated::Punctuated, Data, DeriveInput, Fields, Meta, Type, Variant}; +use syn::{punctuated::Punctuated, Data, DeriveInput, Meta, Type, Variant}; use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder}; use crate::{ @@ -85,21 +85,7 @@ impl TraitHandler for CloneEnumHandler { let variant_ident = &variant.ident; match &variant.fields { - Fields::Unit => { - clone_variants_token_stream.extend(quote! { - Self::#variant_ident => Self::#variant_ident, - }); - clone_from_variants_token_stream.extend(quote! { - Self::#variant_ident => { - if let Self::#variant_ident = source { - // same - } else { - *self = ::core::clone::Clone::clone(source); - } - }, - }); - }, - Fields::Named(_) => { + _any => { let mut pattern_src_token_stream = proc_macro2::TokenStream::new(); let mut pattern_dst_token_stream = proc_macro2::TokenStream::new(); let mut cl_fields_token_stream = proc_macro2::TokenStream::new(); @@ -148,52 +134,6 @@ impl TraitHandler for CloneEnumHandler { }, }); }, - Fields::Unnamed(_) => { - let mut pattern_token_stream = proc_macro2::TokenStream::new(); - let mut pattern2_token_stream = proc_macro2::TokenStream::new(); - let mut fields_token_stream = proc_macro2::TokenStream::new(); - let mut body_token_stream = proc_macro2::TokenStream::new(); - - for (field, field_attribute) in variant_fields { - let field_name_src = format_ident!("_{}", field.name); - - pattern_token_stream.extend(quote!(#field_name_src,)); - - let field_name_dst = format_ident!("_{}", field_name_src); - - pattern2_token_stream.extend(quote!(#field_name_dst,)); - - if let Some(clone) = field_attribute.method.as_ref() { - fields_token_stream.extend(quote! (#clone(#field_name_src),)); - body_token_stream.extend( - quote!(*#field_name_src = #clone(#field_name_dst);), - ); - } else { - clone_types.push(&field.ty); - - fields_token_stream.extend( - quote! ( ::core::clone::Clone::clone(#field_name_src), ), - ); - body_token_stream.extend( - quote!( ::core::clone::Clone::clone_from(#field_name_src, #field_name_dst); ), - ); - } - } - - clone_variants_token_stream.extend(quote! { - Self::#variant_ident ( #pattern_token_stream ) => Self::#variant_ident ( #fields_token_stream ), - }); - - clone_from_variants_token_stream.extend(quote! { - Self::#variant_ident ( #pattern_token_stream ) => { - if let Self::#variant_ident ( #pattern2_token_stream ) = source { - #body_token_stream - } else { - *self = ::core::clone::Clone::clone(source); - } - }, - }); - }, } } From 3324410a4a98ba24706133ef2711db56b0fcc7ab Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 10:58:22 +0100 Subject: [PATCH 05/14] Post-unification cleanup: remove now-redundant match Best reviewed with `git show -b`. --- src/trait_handlers/clone/clone_enum.rs | 94 ++++++++++++-------------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/src/trait_handlers/clone/clone_enum.rs b/src/trait_handlers/clone/clone_enum.rs index 46840c9..74e1d23 100644 --- a/src/trait_handlers/clone/clone_enum.rs +++ b/src/trait_handlers/clone/clone_enum.rs @@ -84,57 +84,53 @@ impl TraitHandler for CloneEnumHandler { for (variant, variant_fields) in variants { let variant_ident = &variant.ident; - match &variant.fields { - _any => { - let mut pattern_src_token_stream = proc_macro2::TokenStream::new(); - let mut pattern_dst_token_stream = proc_macro2::TokenStream::new(); - let mut cl_fields_token_stream = proc_macro2::TokenStream::new(); - let mut cf_body_token_stream = proc_macro2::TokenStream::new(); - - for (field, field_attribute) in variant_fields { - let field_name_real = &field.name; - let field_name_src = format_ident!("_s_{}", field_name_real); - let field_name_dst = format_ident!("_d_{}", field_name_real); - - pattern_src_token_stream - .extend(quote!(#field_name_real: #field_name_src,)); - pattern_dst_token_stream - .extend(quote!(#field_name_real: #field_name_dst,)); - - if let Some(clone) = field_attribute.method.as_ref() { - cl_fields_token_stream.extend(quote! { - #field_name_real: #clone(#field_name_src), - }); - cf_body_token_stream.extend( - quote!(*#field_name_dst = #clone(#field_name_src);), - ); + let mut pattern_src_token_stream = proc_macro2::TokenStream::new(); + let mut pattern_dst_token_stream = proc_macro2::TokenStream::new(); + let mut cl_fields_token_stream = proc_macro2::TokenStream::new(); + let mut cf_body_token_stream = proc_macro2::TokenStream::new(); + + for (field, field_attribute) in variant_fields { + let field_name_real = &field.name; + let field_name_src = format_ident!("_s_{}", field_name_real); + let field_name_dst = format_ident!("_d_{}", field_name_real); + + pattern_src_token_stream + .extend(quote!(#field_name_real: #field_name_src,)); + pattern_dst_token_stream + .extend(quote!(#field_name_real: #field_name_dst,)); + + if let Some(clone) = field_attribute.method.as_ref() { + cl_fields_token_stream.extend(quote! { + #field_name_real: #clone(#field_name_src), + }); + cf_body_token_stream.extend( + quote!(*#field_name_dst = #clone(#field_name_src);), + ); + } else { + clone_types.push(&field.ty); + + cl_fields_token_stream.extend(quote! { + #field_name_real: ::core::clone::Clone::clone(#field_name_src), + }); + cf_body_token_stream.extend( + quote!( ::core::clone::Clone::clone_from(#field_name_dst, #field_name_src); ), + ); + } + } + + clone_variants_token_stream.extend(quote! { + Self::#variant_ident { #pattern_src_token_stream } => Self::#variant_ident { #cl_fields_token_stream }, + }); + + clone_from_variants_token_stream.extend(quote! { + Self::#variant_ident { #pattern_dst_token_stream } => { + if let Self::#variant_ident { #pattern_src_token_stream } = source { + #cf_body_token_stream } else { - clone_types.push(&field.ty); - - cl_fields_token_stream.extend(quote! { - #field_name_real: ::core::clone::Clone::clone(#field_name_src), - }); - cf_body_token_stream.extend( - quote!( ::core::clone::Clone::clone_from(#field_name_dst, #field_name_src); ), - ); + *self = ::core::clone::Clone::clone(source); } - } - - clone_variants_token_stream.extend(quote! { - Self::#variant_ident { #pattern_src_token_stream } => Self::#variant_ident { #cl_fields_token_stream }, - }); - - clone_from_variants_token_stream.extend(quote! { - Self::#variant_ident { #pattern_dst_token_stream } => { - if let Self::#variant_ident { #pattern_src_token_stream } = source { - #cf_body_token_stream - } else { - *self = ::core::clone::Clone::clone(source); - } - }, - }); - }, - } + }, + }); } if !contains_copy { From 941b9b2f16272ec19bb3ed3d918baf86f005b3b9 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 11:31:45 +0100 Subject: [PATCH 06/14] Introduce VariantInfo and use it in clone_enum --- src/common/mod.rs | 2 ++ src/common/variant_info.rs | 50 ++++++++++++++++++++++++++ src/trait_handlers/clone/clone_enum.rs | 17 ++++----- 3 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 src/common/variant_info.rs diff --git a/src/common/mod.rs b/src/common/mod.rs index f18965a..ef0a226 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -7,6 +7,8 @@ pub(crate) mod path; #[allow(dead_code)] pub(crate) mod r#type; #[allow(dead_code)] +pub(crate) mod variant_info; +#[allow(dead_code)] pub(crate) mod where_predicates_bool; #[cfg(feature = "Default")] diff --git a/src/common/variant_info.rs b/src/common/variant_info.rs new file mode 100644 index 0000000..15047f6 --- /dev/null +++ b/src/common/variant_info.rs @@ -0,0 +1,50 @@ +use std::iter; + +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::{Data, Ident, Variant}; + +pub(crate) struct VariantInfo<'v> { + pub(crate) variant: Option<&'v Variant>, + pub(crate) fields: &'v syn::Fields, + pub(crate) attrs: &'v [syn::Attribute], +} + +pub(crate) struct VariantSelector<'v> { + pub(crate) name: Option<&'v Ident>, +} + +impl<'v> VariantInfo<'v> { + pub(crate) fn iter_from_data(data: &'v syn::Data) -> Box + 'v> { + match data { + Data::Struct(s) => Box::new(iter::once(VariantInfo { + variant: None, + fields: &s.fields, + attrs: &[], + })), + + Data::Enum(e) => Box::new(e.variants.iter().map(|v| VariantInfo { + variant: Some(v), + fields: &v.fields, + attrs: &v.attrs, + })), + + Data::Union(_) => panic!("VariantInfo cannot be used for unions"), + } + } + + pub(crate) fn selector(&self) -> VariantSelector<'v> { + let name = self.variant.as_ref().map(|v| &v.ident); + VariantSelector { + name, + } + } +} + +impl ToTokens for VariantSelector<'_> { + fn to_tokens(&self, out: &mut TokenStream) { + if let Some(name) = &self.name { + quote! { :: #name }.to_tokens(out) + } + } +} diff --git a/src/trait_handlers/clone/clone_enum.rs b/src/trait_handlers/clone/clone_enum.rs index 74e1d23..26f9446 100644 --- a/src/trait_handlers/clone/clone_enum.rs +++ b/src/trait_handlers/clone/clone_enum.rs @@ -1,10 +1,11 @@ use quote::{format_ident, quote}; -use syn::{punctuated::Punctuated, Data, DeriveInput, Meta, Type, Variant}; +use syn::{punctuated::Punctuated, Data, DeriveInput, Meta, Type}; use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder}; use crate::{ common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler, common::field_info::FieldInfo, + common::variant_info::VariantInfo, }; pub(crate) struct CloneEnumHandler; @@ -27,15 +28,15 @@ impl TraitHandler for CloneEnumHandler { let mut clone_token_stream = proc_macro2::TokenStream::new(); let mut clone_from_token_stream = proc_macro2::TokenStream::new(); - if let Data::Enum(data) = &ast.data { - type Variants<'a> = Vec<(&'a Variant, Vec<(FieldInfo<'a>, FieldAttribute)>)>; + if let Data::Enum(_) = &ast.data { + type Variants<'a> = Vec<(VariantInfo<'a>, Vec<(FieldInfo<'a>, FieldAttribute)>)>; let mut variants: Variants = Vec::new(); #[cfg(feature = "Copy")] let mut has_custom_clone_method = false; - for variant in data.variants.iter() { + for variant in VariantInfo::iter_from_data(&ast.data) { let _ = TypeAttributeBuilder { enable_flag: false, enable_bound: false } @@ -82,7 +83,7 @@ impl TraitHandler for CloneEnumHandler { let mut clone_from_variants_token_stream = proc_macro2::TokenStream::new(); for (variant, variant_fields) in variants { - let variant_ident = &variant.ident; + let variant_sel = variant.selector(); let mut pattern_src_token_stream = proc_macro2::TokenStream::new(); let mut pattern_dst_token_stream = proc_macro2::TokenStream::new(); @@ -119,12 +120,12 @@ impl TraitHandler for CloneEnumHandler { } clone_variants_token_stream.extend(quote! { - Self::#variant_ident { #pattern_src_token_stream } => Self::#variant_ident { #cl_fields_token_stream }, + Self #variant_sel { #pattern_src_token_stream } => Self #variant_sel { #cl_fields_token_stream }, }); clone_from_variants_token_stream.extend(quote! { - Self::#variant_ident { #pattern_dst_token_stream } => { - if let Self::#variant_ident { #pattern_src_token_stream } = source { + Self #variant_sel { #pattern_dst_token_stream } => { + if let Self #variant_sel { #pattern_src_token_stream } = source { #cf_body_token_stream } else { *self = ::core::clone::Clone::clone(source); From 1accc1d4e6797e0aadd4fb1c04015c742b5b7684 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 11:41:12 +0100 Subject: [PATCH 07/14] Use match approach for struct clone The clone_enum code is now good for cloning structs. Use it. The redundant block will be removed in a moment. --- src/trait_handlers/clone/clone_enum.rs | 8 ++++---- src/trait_handlers/clone/clone_struct.rs | 2 ++ src/trait_handlers/clone/mod.rs | 8 +------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/trait_handlers/clone/clone_enum.rs b/src/trait_handlers/clone/clone_enum.rs index 26f9446..016af69 100644 --- a/src/trait_handlers/clone/clone_enum.rs +++ b/src/trait_handlers/clone/clone_enum.rs @@ -1,9 +1,9 @@ use quote::{format_ident, quote}; -use syn::{punctuated::Punctuated, Data, DeriveInput, Meta, Type}; +use syn::{DeriveInput, Meta, Type}; use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder}; use crate::{ - common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler, + supported_traits::Trait, TraitHandler, common::field_info::FieldInfo, common::variant_info::VariantInfo, }; @@ -23,12 +23,12 @@ impl TraitHandler for CloneEnumHandler { } .build_from_clone_meta(meta)?; - let mut bound: WherePredicates = Punctuated::new(); + let bound; let mut clone_token_stream = proc_macro2::TokenStream::new(); let mut clone_from_token_stream = proc_macro2::TokenStream::new(); - if let Data::Enum(_) = &ast.data { + { type Variants<'a> = Vec<(VariantInfo<'a>, Vec<(FieldInfo<'a>, FieldAttribute)>)>; let mut variants: Variants = Vec::new(); diff --git a/src/trait_handlers/clone/clone_struct.rs b/src/trait_handlers/clone/clone_struct.rs index 73aa8fa..8c29a7e 100644 --- a/src/trait_handlers/clone/clone_struct.rs +++ b/src/trait_handlers/clone/clone_struct.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] // XXXX + use quote::quote; use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Index, Meta, Type}; diff --git a/src/trait_handlers/clone/mod.rs b/src/trait_handlers/clone/mod.rs index 8c8a929..f33b0b7 100644 --- a/src/trait_handlers/clone/mod.rs +++ b/src/trait_handlers/clone/mod.rs @@ -19,13 +19,7 @@ impl TraitHandler for CloneHandler { meta: &Meta, ) -> syn::Result<()> { match ast.data { - Data::Struct(_) => clone_struct::CloneStructHandler::trait_meta_handler( - ast, - token_stream, - traits, - meta, - ), - Data::Enum(_) => { + Data::Struct(_) | Data::Enum(_) => { clone_enum::CloneEnumHandler::trait_meta_handler(ast, token_stream, traits, meta) }, Data::Union(_) => { From 461123b08aa2d56ab44d32826044047f5b22fe5b Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 11:55:50 +0100 Subject: [PATCH 08/14] Post-unification cleanup: Remove redundant block Best reviewed with `git show -b` --- src/trait_handlers/clone/clone_enum.rs | 216 ++++++++++++------------- 1 file changed, 106 insertions(+), 110 deletions(-) diff --git a/src/trait_handlers/clone/clone_enum.rs b/src/trait_handlers/clone/clone_enum.rs index 016af69..4d63faf 100644 --- a/src/trait_handlers/clone/clone_enum.rs +++ b/src/trait_handlers/clone/clone_enum.rs @@ -23,145 +23,141 @@ impl TraitHandler for CloneEnumHandler { } .build_from_clone_meta(meta)?; - let bound; - let mut clone_token_stream = proc_macro2::TokenStream::new(); let mut clone_from_token_stream = proc_macro2::TokenStream::new(); - { - type Variants<'a> = Vec<(VariantInfo<'a>, Vec<(FieldInfo<'a>, FieldAttribute)>)>; - - let mut variants: Variants = Vec::new(); + type Variants<'a> = Vec<(VariantInfo<'a>, Vec<(FieldInfo<'a>, FieldAttribute)>)>; - #[cfg(feature = "Copy")] - let mut has_custom_clone_method = false; + let mut variants: Variants = Vec::new(); - for variant in VariantInfo::iter_from_data(&ast.data) { - let _ = TypeAttributeBuilder { - enable_flag: false, enable_bound: false - } - .build_from_attributes(&variant.attrs, traits)?; + #[cfg(feature = "Copy")] + let mut has_custom_clone_method = false; - let mut variant_fields: Vec<(FieldInfo, FieldAttribute)> = Vec::new(); + for variant in VariantInfo::iter_from_data(&ast.data) { + let _ = TypeAttributeBuilder { + enable_flag: false, enable_bound: false + } + .build_from_attributes(&variant.attrs, traits)?; - for (index, field) in variant.fields.iter().enumerate() { - let field_attribute = FieldAttributeBuilder { - enable_method: true - } - .build_from_attributes(&field.attrs, traits)?; + let mut variant_fields: Vec<(FieldInfo, FieldAttribute)> = Vec::new(); - #[cfg(feature = "Copy")] - if field_attribute.method.is_some() { - has_custom_clone_method = true; - } + for (index, field) in variant.fields.iter().enumerate() { + let field_attribute = FieldAttributeBuilder { + enable_method: true + } + .build_from_attributes(&field.attrs, traits)?; - variant_fields.push((FieldInfo::new(index, field), field_attribute)); + #[cfg(feature = "Copy")] + if field_attribute.method.is_some() { + has_custom_clone_method = true; } - variants.push((variant, variant_fields)); + variant_fields.push((FieldInfo::new(index, field), field_attribute)); } - #[cfg(feature = "Copy")] - let contains_copy = !has_custom_clone_method && traits.contains(&Trait::Copy); + variants.push((variant, variant_fields)); + } - #[cfg(not(feature = "Copy"))] - let contains_copy = false; + #[cfg(feature = "Copy")] + let contains_copy = !has_custom_clone_method && traits.contains(&Trait::Copy); - if contains_copy { - clone_token_stream.extend(quote!(*self)); - } + #[cfg(not(feature = "Copy"))] + let contains_copy = false; - let mut clone_types: Vec<&Type> = Vec::new(); + if contains_copy { + clone_token_stream.extend(quote!(*self)); + } - if variants.is_empty() { - if !contains_copy { - clone_token_stream.extend(quote!(unreachable!())); - clone_from_token_stream.extend(quote!(let _ = source;)); - } - } else { - let mut clone_variants_token_stream = proc_macro2::TokenStream::new(); - let mut clone_from_variants_token_stream = proc_macro2::TokenStream::new(); - - for (variant, variant_fields) in variants { - let variant_sel = variant.selector(); - - let mut pattern_src_token_stream = proc_macro2::TokenStream::new(); - let mut pattern_dst_token_stream = proc_macro2::TokenStream::new(); - let mut cl_fields_token_stream = proc_macro2::TokenStream::new(); - let mut cf_body_token_stream = proc_macro2::TokenStream::new(); - - for (field, field_attribute) in variant_fields { - let field_name_real = &field.name; - let field_name_src = format_ident!("_s_{}", field_name_real); - let field_name_dst = format_ident!("_d_{}", field_name_real); - - pattern_src_token_stream - .extend(quote!(#field_name_real: #field_name_src,)); - pattern_dst_token_stream - .extend(quote!(#field_name_real: #field_name_dst,)); - - if let Some(clone) = field_attribute.method.as_ref() { - cl_fields_token_stream.extend(quote! { - #field_name_real: #clone(#field_name_src), - }); - cf_body_token_stream.extend( - quote!(*#field_name_dst = #clone(#field_name_src);), - ); - } else { - clone_types.push(&field.ty); - - cl_fields_token_stream.extend(quote! { - #field_name_real: ::core::clone::Clone::clone(#field_name_src), - }); - cf_body_token_stream.extend( - quote!( ::core::clone::Clone::clone_from(#field_name_dst, #field_name_src); ), - ); - } - } + let mut clone_types: Vec<&Type> = Vec::new(); - clone_variants_token_stream.extend(quote! { - Self #variant_sel { #pattern_src_token_stream } => Self #variant_sel { #cl_fields_token_stream }, + if variants.is_empty() { + if !contains_copy { + clone_token_stream.extend(quote!(unreachable!())); + clone_from_token_stream.extend(quote!(let _ = source;)); + } + } else { + let mut clone_variants_token_stream = proc_macro2::TokenStream::new(); + let mut clone_from_variants_token_stream = proc_macro2::TokenStream::new(); + + for (variant, variant_fields) in variants { + let variant_sel = variant.selector(); + + let mut pattern_src_token_stream = proc_macro2::TokenStream::new(); + let mut pattern_dst_token_stream = proc_macro2::TokenStream::new(); + let mut cl_fields_token_stream = proc_macro2::TokenStream::new(); + let mut cf_body_token_stream = proc_macro2::TokenStream::new(); + + for (field, field_attribute) in variant_fields { + let field_name_real = &field.name; + let field_name_src = format_ident!("_s_{}", field_name_real); + let field_name_dst = format_ident!("_d_{}", field_name_real); + + pattern_src_token_stream + .extend(quote!(#field_name_real: #field_name_src,)); + pattern_dst_token_stream + .extend(quote!(#field_name_real: #field_name_dst,)); + + if let Some(clone) = field_attribute.method.as_ref() { + cl_fields_token_stream.extend(quote! { + #field_name_real: #clone(#field_name_src), }); - - clone_from_variants_token_stream.extend(quote! { - Self #variant_sel { #pattern_dst_token_stream } => { - if let Self #variant_sel { #pattern_src_token_stream } = source { - #cf_body_token_stream - } else { - *self = ::core::clone::Clone::clone(source); - } - }, + cf_body_token_stream.extend( + quote!(*#field_name_dst = #clone(#field_name_src);), + ); + } else { + clone_types.push(&field.ty); + + cl_fields_token_stream.extend(quote! { + #field_name_real: ::core::clone::Clone::clone(#field_name_src), }); + cf_body_token_stream.extend( + quote!( ::core::clone::Clone::clone_from(#field_name_dst, #field_name_src); ), + ); + } } - if !contains_copy { - clone_token_stream.extend(quote! { - match self { - #clone_variants_token_stream - } + clone_variants_token_stream.extend(quote! { + Self #variant_sel { #pattern_src_token_stream } => Self #variant_sel { #cl_fields_token_stream }, }); - clone_from_token_stream.extend(quote! { - match self { - #clone_from_variants_token_stream - } + clone_from_variants_token_stream.extend(quote! { + Self #variant_sel { #pattern_dst_token_stream } => { + if let Self #variant_sel { #pattern_src_token_stream } = source { + #cf_body_token_stream + } else { + *self = ::core::clone::Clone::clone(source); + } + }, }); - } } - bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types( - &ast.generics.params, - &syn::parse2(if contains_copy { - quote!(::core::marker::Copy) - } else { - quote!(::core::clone::Clone) - }) - .unwrap(), - &clone_types, - &[], - ); + if !contains_copy { + clone_token_stream.extend(quote! { + match self { + #clone_variants_token_stream + } + }); + + clone_from_token_stream.extend(quote! { + match self { + #clone_from_variants_token_stream + } + }); + } } + let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types( + &ast.generics.params, + &syn::parse2(if contains_copy { + quote!(::core::marker::Copy) + } else { + quote!(::core::clone::Clone) + }) + .unwrap(), + &clone_types, + &[], + ); + let clone_from_fn_token_stream = if clone_from_token_stream.is_empty() { None } else { From 831b9e1e900eda5db9bad1dd13533615bff1cf32 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 11:42:29 +0100 Subject: [PATCH 09/14] Post-unification cleanup: Remove redundant clone_struct.rs --- src/trait_handlers/clone/clone_struct.rs | 197 ----------------------- src/trait_handlers/clone/mod.rs | 1 - 2 files changed, 198 deletions(-) delete mode 100644 src/trait_handlers/clone/clone_struct.rs diff --git a/src/trait_handlers/clone/clone_struct.rs b/src/trait_handlers/clone/clone_struct.rs deleted file mode 100644 index 8c29a7e..0000000 --- a/src/trait_handlers/clone/clone_struct.rs +++ /dev/null @@ -1,197 +0,0 @@ -#![allow(dead_code)] // XXXX - -use quote::quote; -use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Index, Meta, Type}; - -use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder}; -use crate::{ - common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler, -}; - -pub(crate) struct CloneStructHandler; - -impl TraitHandler for CloneStructHandler { - #[inline] - 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_bound: true - } - .build_from_clone_meta(meta)?; - - let mut bound: WherePredicates = Punctuated::new(); - - let mut clone_token_stream = proc_macro2::TokenStream::new(); - let mut clone_from_token_stream = proc_macro2::TokenStream::new(); - - if let Data::Struct(data) = &ast.data { - let mut fields: Vec<(&Field, FieldAttribute)> = Vec::new(); - - #[cfg(feature = "Copy")] - let contains_copy = traits.contains(&Trait::Copy); - - #[cfg(not(feature = "Copy"))] - let contains_copy = false; - - if contains_copy { - clone_token_stream.extend(quote!(*self)); - } - - for field in data.fields.iter() { - let field_attribute = FieldAttributeBuilder { - enable_method: !contains_copy - } - .build_from_attributes(&field.attrs, traits)?; - - fields.push((field, field_attribute)); - } - - let mut clone_types: Vec<&Type> = Vec::new(); - - match &data.fields { - Fields::Unit => { - if !contains_copy { - clone_token_stream.extend(quote!(Self)); - clone_from_token_stream.extend(quote!(let _ = source;)); - } - }, - Fields::Named(_) => { - let mut fields_token_stream = proc_macro2::TokenStream::new(); - let mut clone_from_body_token_stream = proc_macro2::TokenStream::new(); - - if fields.is_empty() { - clone_from_body_token_stream.extend(quote!(let _ = source;)); - } else { - for (field, field_attribute) in fields { - let field_name = field.ident.as_ref().unwrap(); - - if let Some(clone) = field_attribute.method.as_ref() { - fields_token_stream.extend(quote! { - #field_name: #clone(&self.#field_name), - }); - - clone_from_body_token_stream.extend( - quote!(self.#field_name = #clone(&source.#field_name);), - ); - } else { - clone_types.push(&field.ty); - - fields_token_stream.extend(quote! { - #field_name: ::core::clone::Clone::clone(&self.#field_name), - }); - - clone_from_body_token_stream.extend( - quote!( ::core::clone::Clone::clone_from(&mut self.#field_name, &source.#field_name); ), - ); - } - } - } - - if !contains_copy { - clone_token_stream.extend(quote! { - Self { - #fields_token_stream - } - }); - - clone_from_token_stream.extend(clone_from_body_token_stream); - } - }, - Fields::Unnamed(_) => { - let mut fields_token_stream = proc_macro2::TokenStream::new(); - let mut clone_from_body_token_stream = proc_macro2::TokenStream::new(); - - if fields.is_empty() { - clone_from_body_token_stream.extend(quote!(let _ = source;)); - } else { - for (index, (field, field_attribute)) in fields.into_iter().enumerate() { - let field_name = Index::from(index); - - if let Some(clone) = field_attribute.method.as_ref() { - fields_token_stream.extend(quote!(#clone(&self.#field_name),)); - - clone_from_body_token_stream.extend( - quote!(self.#field_name = #clone(&source.#field_name);), - ); - } else { - clone_types.push(&field.ty); - - fields_token_stream.extend( - quote! ( ::core::clone::Clone::clone(&self.#field_name), ), - ); - - clone_from_body_token_stream.extend( - quote!( ::core::clone::Clone::clone_from(&mut self.#field_name, &source.#field_name); ), - ); - } - } - } - - if !contains_copy { - clone_token_stream.extend(quote!(Self ( #fields_token_stream ))); - clone_from_token_stream.extend(clone_from_body_token_stream); - } - }, - } - - bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types( - &ast.generics.params, - &syn::parse2(if contains_copy { - quote!(::core::marker::Copy) - } else { - quote!(::core::clone::Clone) - }) - .unwrap(), - &clone_types, - &[], - ); - } - - let clone_from_fn_token_stream = if clone_from_token_stream.is_empty() { - None - } else { - Some(quote! { - #[inline] - fn clone_from(&mut self, source: &Self) { - #clone_from_token_stream - } - }) - }; - - let ident = &ast.ident; - - 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::clone::Clone for #ident #ty_generics #where_clause { - #[inline] - fn clone(&self) -> Self { - #clone_token_stream - } - - #clone_from_fn_token_stream - } - }); - - #[cfg(feature = "Copy")] - if traits.contains(&Trait::Copy) { - token_stream.extend(quote! { - impl #impl_generics ::core::marker::Copy for #ident #ty_generics #where_clause { - } - }); - } - - Ok(()) - } -} diff --git a/src/trait_handlers/clone/mod.rs b/src/trait_handlers/clone/mod.rs index f33b0b7..200dbf4 100644 --- a/src/trait_handlers/clone/mod.rs +++ b/src/trait_handlers/clone/mod.rs @@ -1,5 +1,4 @@ mod clone_enum; -mod clone_struct; mod clone_union; mod models; From 869dff880cb2e3bf6d35f4404c43cb76346b8b3d Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 11:43:31 +0100 Subject: [PATCH 10/14] Post-unification cleanup: Rename clone_enum to clone_data --- src/trait_handlers/clone/{clone_enum.rs => clone_data.rs} | 4 ++-- src/trait_handlers/clone/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/trait_handlers/clone/{clone_enum.rs => clone_data.rs} (98%) diff --git a/src/trait_handlers/clone/clone_enum.rs b/src/trait_handlers/clone/clone_data.rs similarity index 98% rename from src/trait_handlers/clone/clone_enum.rs rename to src/trait_handlers/clone/clone_data.rs index 4d63faf..76921ef 100644 --- a/src/trait_handlers/clone/clone_enum.rs +++ b/src/trait_handlers/clone/clone_data.rs @@ -8,9 +8,9 @@ use crate::{ common::variant_info::VariantInfo, }; -pub(crate) struct CloneEnumHandler; +pub(crate) struct CloneDataHandler; -impl TraitHandler for CloneEnumHandler { +impl TraitHandler for CloneDataHandler { #[inline] fn trait_meta_handler( ast: &DeriveInput, diff --git a/src/trait_handlers/clone/mod.rs b/src/trait_handlers/clone/mod.rs index 200dbf4..2d79252 100644 --- a/src/trait_handlers/clone/mod.rs +++ b/src/trait_handlers/clone/mod.rs @@ -1,4 +1,4 @@ -mod clone_enum; +mod clone_data; mod clone_union; mod models; @@ -19,7 +19,7 @@ impl TraitHandler for CloneHandler { ) -> syn::Result<()> { match ast.data { Data::Struct(_) | Data::Enum(_) => { - clone_enum::CloneEnumHandler::trait_meta_handler(ast, token_stream, traits, meta) + clone_data::CloneDataHandler::trait_meta_handler(ast, token_stream, traits, meta) }, Data::Union(_) => { clone_union::CloneUnionHandler::trait_meta_handler(ast, token_stream, traits, meta) From 1abd260142fe5bd6be306edb6e1d8769fafb853a Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 12:38:04 +0100 Subject: [PATCH 11/14] Post-unification cleanup: rustfmt Apply deferred rewrapping. --- src/trait_handlers/clone/clone_data.rs | 33 ++++++++++++-------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/trait_handlers/clone/clone_data.rs b/src/trait_handlers/clone/clone_data.rs index 76921ef..0894562 100644 --- a/src/trait_handlers/clone/clone_data.rs +++ b/src/trait_handlers/clone/clone_data.rs @@ -3,9 +3,9 @@ use syn::{DeriveInput, Meta, Type}; use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder}; use crate::{ - supported_traits::Trait, TraitHandler, - common::field_info::FieldInfo, - common::variant_info::VariantInfo, + common::{field_info::FieldInfo, variant_info::VariantInfo}, + supported_traits::Trait, + TraitHandler, }; pub(crate) struct CloneDataHandler; @@ -92,18 +92,15 @@ impl TraitHandler for CloneDataHandler { let field_name_src = format_ident!("_s_{}", field_name_real); let field_name_dst = format_ident!("_d_{}", field_name_real); - pattern_src_token_stream - .extend(quote!(#field_name_real: #field_name_src,)); - pattern_dst_token_stream - .extend(quote!(#field_name_real: #field_name_dst,)); + pattern_src_token_stream.extend(quote!(#field_name_real: #field_name_src,)); + pattern_dst_token_stream.extend(quote!(#field_name_real: #field_name_dst,)); if let Some(clone) = field_attribute.method.as_ref() { cl_fields_token_stream.extend(quote! { #field_name_real: #clone(#field_name_src), }); - cf_body_token_stream.extend( - quote!(*#field_name_dst = #clone(#field_name_src);), - ); + cf_body_token_stream + .extend(quote!(*#field_name_dst = #clone(#field_name_src);)); } else { clone_types.push(&field.ty); @@ -121,14 +118,14 @@ impl TraitHandler for CloneDataHandler { }); clone_from_variants_token_stream.extend(quote! { - Self #variant_sel { #pattern_dst_token_stream } => { - if let Self #variant_sel { #pattern_src_token_stream } = source { - #cf_body_token_stream - } else { - *self = ::core::clone::Clone::clone(source); - } - }, - }); + Self #variant_sel { #pattern_dst_token_stream } => { + if let Self #variant_sel { #pattern_src_token_stream } = source { + #cf_body_token_stream + } else { + *self = ::core::clone::Clone::clone(source); + } + }, + }); } if !contains_copy { From 4329fe8d6a90f7ed77064990e9e15e2b8fb6f95a Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 13:06:12 +0100 Subject: [PATCH 12/14] Post-unification cleanup: Fix a clippy lint --- src/trait_handlers/clone/clone_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trait_handlers/clone/clone_data.rs b/src/trait_handlers/clone/clone_data.rs index 0894562..b1ff5cd 100644 --- a/src/trait_handlers/clone/clone_data.rs +++ b/src/trait_handlers/clone/clone_data.rs @@ -37,7 +37,7 @@ impl TraitHandler for CloneDataHandler { let _ = TypeAttributeBuilder { enable_flag: false, enable_bound: false } - .build_from_attributes(&variant.attrs, traits)?; + .build_from_attributes(variant.attrs, traits)?; let mut variant_fields: Vec<(FieldInfo, FieldAttribute)> = Vec::new(); From 7434288b8c3966b2d24076e147d785d3dff244ec Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 13:20:55 +0100 Subject: [PATCH 13/14] fixup! Introduce FieldInfo and use in clone_enum --- src/common/mod.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index ef0a226..d584540 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -24,16 +24,6 @@ pub(crate) mod expr; ))] #[allow(dead_code)] pub(crate) mod ident_bool; -#[cfg(any( - feature = "Debug", - feature = "PartialEq", - feature = "PartialOrd", - feature = "Ord", - feature = "Hash", - feature = "Deref", - feature = "DerefMut", - feature = "Into" -))] #[allow(dead_code)] pub(crate) mod ident_index; #[cfg(any(feature = "PartialOrd", feature = "Ord"))] From 87287776510a46553bddd387782a6ca4019e27a7 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 1 Aug 2024 13:21:10 +0100 Subject: [PATCH 14/14] Post-unification cleanup: Move ident_index module to top --- src/common/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index d584540..8b3ed90 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -3,6 +3,8 @@ pub(crate) mod bound; #[allow(dead_code)] pub(crate) mod field_info; #[allow(dead_code)] +pub(crate) mod ident_index; +#[allow(dead_code)] pub(crate) mod path; #[allow(dead_code)] pub(crate) mod r#type; @@ -24,8 +26,6 @@ pub(crate) mod expr; ))] #[allow(dead_code)] pub(crate) mod ident_bool; -#[allow(dead_code)] -pub(crate) mod ident_index; #[cfg(any(feature = "PartialOrd", feature = "Ord"))] #[allow(dead_code)] pub(crate) mod int;