From 795261919f159249a59c92f16bc3c2ed0b5f9c7c Mon Sep 17 00:00:00 2001 From: Mingun Date: Mon, 1 May 2023 02:08:10 +0500 Subject: [PATCH 01/10] Generate `visit_seq` only when needed --- serde_derive/src/de.rs | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 2dfe87ef0..c2b01ca1e 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -942,10 +942,6 @@ fn deserialize_struct( }; let expecting = cattrs.expecting().unwrap_or(&expecting); - let visit_seq = Stmts(deserialize_seq( - &type_path, params, fields, true, cattrs, expecting, - )); - let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() { deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs) } else { @@ -985,25 +981,31 @@ fn deserialize_struct( } }; - let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); - let visitor_var = if all_skipped { - quote!(_) - } else { - quote!(mut __seq) - }; - // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. let visit_seq = match *untagged { - Untagged::No if !cattrs.has_flatten() => Some(quote! { - #[inline] - fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - #visit_seq - } - }), + Untagged::No if !cattrs.has_flatten() => { + let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); + let mut_seq = if all_skipped { + quote!(_) + } else { + quote!(mut __seq) + }; + + let visit_seq = Stmts(deserialize_seq( + &type_path, params, fields, true, cattrs, expecting, + )); + + Some(quote! { + #[inline] + fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + #visit_seq + } + }) + } _ => None, }; From 95730dc7f7175ad9e766035c10393138d05e1f03 Mon Sep 17 00:00:00 2001 From: Mingun Date: Mon, 1 May 2023 02:43:25 +0500 Subject: [PATCH 02/10] Reorder variables to match order in final quote! --- serde_derive/src/de.rs | 63 +++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index c2b01ca1e..b7f82b28d 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -914,8 +914,6 @@ fn deserialize_struct( deserializer: Option, untagged: &Untagged, ) -> Fragment { - let is_enum = variant_ident.is_some(); - let this_type = ¶ms.this_type; let this_value = ¶ms.this_value; let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = @@ -951,36 +949,6 @@ fn deserialize_struct( let fields_stmt = fields_stmt.map(Stmts); let visit_map = Stmts(visit_map); - let visitor_expr = quote! { - __Visitor { - marker: _serde::__private::PhantomData::<#this_type #ty_generics>, - lifetime: _serde::__private::PhantomData, - } - }; - let need_seed = deserializer.is_none(); - let dispatch = if let Some(deserializer) = deserializer { - quote! { - _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) - } - } else if is_enum && cattrs.has_flatten() { - quote! { - _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr) - } - } else if is_enum { - quote! { - _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) - } - } else if cattrs.has_flatten() { - quote! { - _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) - } - } else { - let type_name = cattrs.name().deserialize_name(); - quote! { - _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) - } - }; - // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. let visit_seq = match *untagged { @@ -1009,6 +977,8 @@ fn deserialize_struct( _ => None, }; + let is_enum = variant_ident.is_some(); + let need_seed = deserializer.is_none(); let visitor_seed = if need_seed && is_enum && cattrs.has_flatten() { Some(quote! { impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause { @@ -1026,6 +996,35 @@ fn deserialize_struct( None }; + let visitor_expr = quote! { + __Visitor { + marker: _serde::__private::PhantomData::<#this_type #ty_generics>, + lifetime: _serde::__private::PhantomData, + } + }; + let dispatch = if let Some(deserializer) = deserializer { + quote! { + _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) + } + } else if is_enum && cattrs.has_flatten() { + quote! { + _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr) + } + } else if is_enum { + quote! { + _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) + } + } else if cattrs.has_flatten() { + quote! { + _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) + } + } else { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) + } + }; + quote_block! { #field_visitor From 2796833c82638ee1e121fc0ba596bac5ef98ac70 Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 1 Oct 2020 13:29:39 +0500 Subject: [PATCH 03/10] Pull up call to `deserialize_map` because it's identical --- serde_derive/src/de.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index b7f82b28d..ef1298213 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -940,14 +940,13 @@ fn deserialize_struct( }; let expecting = cattrs.expecting().unwrap_or(&expecting); - let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() { - deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs) + let (field_visitor, fields_stmt) = if cattrs.has_flatten() { + deserialize_struct_as_map_visitor(fields, cattrs) } else { - deserialize_struct_as_struct_visitor(&type_path, params, fields, cattrs) + deserialize_struct_as_struct_visitor(fields, cattrs) }; let field_visitor = Stmts(field_visitor); let fields_stmt = fields_stmt.map(Stmts); - let visit_map = Stmts(visit_map); // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. @@ -976,6 +975,7 @@ fn deserialize_struct( } _ => None, }; + let visit_map = Stmts(deserialize_map(&type_path, params, fields, cattrs)); let is_enum = variant_ident.is_some(); let need_seed = deserializer.is_none(); @@ -2425,11 +2425,9 @@ fn deserialize_identifier( } fn deserialize_struct_as_struct_visitor( - struct_path: &TokenStream, - params: &Parameters, fields: &[Field], cattrs: &attr::Container, -) -> (Fragment, Option, Fragment) { +) -> (Fragment, Option) { assert!(!cattrs.has_flatten()); let field_names_idents: Vec<_> = fields @@ -2458,17 +2456,13 @@ fn deserialize_struct_as_struct_visitor( let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); - let visit_map = deserialize_map(struct_path, params, fields, cattrs); - - (field_visitor, Some(fields_stmt), visit_map) + (field_visitor, Some(fields_stmt)) } fn deserialize_struct_as_map_visitor( - struct_path: &TokenStream, - params: &Parameters, fields: &[Field], cattrs: &attr::Container, -) -> (Fragment, Option, Fragment) { +) -> (Fragment, Option) { let field_names_idents: Vec<_> = fields .iter() .enumerate() @@ -2484,9 +2478,7 @@ fn deserialize_struct_as_map_visitor( let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); - let visit_map = deserialize_map(struct_path, params, fields, cattrs); - - (field_visitor, None, visit_map) + (field_visitor, None) } fn deserialize_map( From 75db73066b54ca06928a352e5915c31715ced081 Mon Sep 17 00:00:00 2001 From: Mingun Date: Mon, 1 May 2023 01:26:56 +0500 Subject: [PATCH 04/10] Inline `deserialize_struct_as_map_visitor` and `deserialize_struct_as_struct_visitor` --- serde_derive/src/de.rs | 102 +++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index ef1298213..c80845ede 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -941,9 +941,50 @@ fn deserialize_struct( let expecting = cattrs.expecting().unwrap_or(&expecting); let (field_visitor, fields_stmt) = if cattrs.has_flatten() { - deserialize_struct_as_map_visitor(fields, cattrs) + let field_names_idents: Vec<_> = fields + .iter() + .enumerate() + .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) + .map(|(i, field)| { + ( + field.attrs.name().deserialize_name(), + field_i(i), + field.attrs.aliases(), + ) + }) + .collect(); + + let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); + + (field_visitor, None) } else { - deserialize_struct_as_struct_visitor(fields, cattrs) + let field_names_idents: Vec<_> = fields + .iter() + .enumerate() + .filter(|&(_, field)| !field.attrs.skip_deserializing()) + .map(|(i, field)| { + ( + field.attrs.name().deserialize_name(), + field_i(i), + field.attrs.aliases(), + ) + }) + .collect(); + + let fields_stmt = { + let field_names = field_names_idents + .iter() + .flat_map(|(_, _, aliases)| aliases); + + quote_block! { + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; + } + }; + + let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); + + (field_visitor, Some(fields_stmt)) }; let field_visitor = Stmts(field_visitor); let fields_stmt = fields_stmt.map(Stmts); @@ -2424,63 +2465,6 @@ fn deserialize_identifier( } } -fn deserialize_struct_as_struct_visitor( - fields: &[Field], - cattrs: &attr::Container, -) -> (Fragment, Option) { - assert!(!cattrs.has_flatten()); - - let field_names_idents: Vec<_> = fields - .iter() - .enumerate() - .filter(|&(_, field)| !field.attrs.skip_deserializing()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) - }) - .collect(); - - let fields_stmt = { - let field_names = field_names_idents - .iter() - .flat_map(|(_, _, aliases)| aliases); - - quote_block! { - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; - } - }; - - let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); - - (field_visitor, Some(fields_stmt)) -} - -fn deserialize_struct_as_map_visitor( - fields: &[Field], - cattrs: &attr::Container, -) -> (Fragment, Option) { - let field_names_idents: Vec<_> = fields - .iter() - .enumerate() - .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) - }) - .collect(); - - let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); - - (field_visitor, None) -} - fn deserialize_map( struct_path: &TokenStream, params: &Parameters, From 5ffebeb6efe026868180acb7301d2707e1963a9b Mon Sep 17 00:00:00 2001 From: Mingun Date: Mon, 1 May 2023 01:45:37 +0500 Subject: [PATCH 05/10] Actually, `field_names_idents` can be calculated using the same code in both cases When !cattrs.has_flatten() all fields is !field.attrs.flatten() Co-authored-by: Oliver Schneider --- serde_derive/src/de.rs | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index c80845ede..4be9a2323 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -940,37 +940,25 @@ fn deserialize_struct( }; let expecting = cattrs.expecting().unwrap_or(&expecting); + let field_names_idents: Vec<_> = fields + .iter() + .enumerate() + // Skip fields that shouldn't be deserialized or that were flattened, + // so they don't appear in the storage in their literal form + .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) + .map(|(i, field)| { + ( + field.attrs.name().deserialize_name(), + field_i(i), + field.attrs.aliases(), + ) + }) + .collect(); let (field_visitor, fields_stmt) = if cattrs.has_flatten() { - let field_names_idents: Vec<_> = fields - .iter() - .enumerate() - .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) - }) - .collect(); - let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); (field_visitor, None) } else { - let field_names_idents: Vec<_> = fields - .iter() - .enumerate() - .filter(|&(_, field)| !field.attrs.skip_deserializing()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) - }) - .collect(); - let fields_stmt = { let field_names = field_names_idents .iter() From fb3a9e0d7c752b425c6f1bf613bd48c8808ec568 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 6 May 2023 22:53:29 +0500 Subject: [PATCH 06/10] Simplify check for missing fields --- serde_derive/src/de.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 4be9a2323..8067f5d78 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -981,8 +981,7 @@ fn deserialize_struct( // structs that only have a map representation. let visit_seq = match *untagged { Untagged::No if !cattrs.has_flatten() => { - let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); - let mut_seq = if all_skipped { + let mut_seq = if field_names_idents.is_empty() { quote!(_) } else { quote!(mut __seq) From bbbd1d24c96b8d142c1f3bf54109a6ee984a84a0 Mon Sep 17 00:00:00 2001 From: Mingun Date: Mon, 1 May 2023 01:49:11 +0500 Subject: [PATCH 07/10] Move `deserialize_generated_identifier` out from if because the call is same in both arms --- serde_derive/src/de.rs | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 8067f5d78..59d0c4d8a 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -954,28 +954,12 @@ fn deserialize_struct( ) }) .collect(); - let (field_visitor, fields_stmt) = if cattrs.has_flatten() { - let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); - - (field_visitor, None) - } else { - let fields_stmt = { - let field_names = field_names_idents - .iter() - .flat_map(|(_, _, aliases)| aliases); - - quote_block! { - #[doc(hidden)] - const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; - } - }; - - let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None); - - (field_visitor, Some(fields_stmt)) - }; - let field_visitor = Stmts(field_visitor); - let fields_stmt = fields_stmt.map(Stmts); + let field_visitor = Stmts(deserialize_generated_identifier( + &field_names_idents, + cattrs, + false, + None, + )); // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. @@ -1024,6 +1008,19 @@ fn deserialize_struct( None }; + let fields_stmt = if cattrs.has_flatten() { + None + } else { + let field_names = field_names_idents + .iter() + .flat_map(|(_, _, aliases)| aliases); + + Some(quote! { + #[doc(hidden)] + const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; + }) + }; + let visitor_expr = quote! { __Visitor { marker: _serde::__private::PhantomData::<#this_type #ty_generics>, From d0dfc4577e6d1c291341fc7862672f30f9a54a24 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 6 May 2023 23:25:28 +0500 Subject: [PATCH 08/10] Replace enum with boolean parameter --- serde_derive/src/de.rs | 56 +++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 59d0c4d8a..bb090cc60 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -287,7 +287,7 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment { match &cont.data { Data::Enum(variants) => deserialize_enum(params, variants, &cont.attrs), Data::Struct(Style::Struct, fields) => { - deserialize_struct(None, params, fields, &cont.attrs, None, &Untagged::No) + deserialize_struct(None, params, fields, &cont.attrs, None, false) } Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => { deserialize_tuple(None, params, fields, &cont.attrs, None) @@ -901,18 +901,13 @@ fn deserialize_newtype_struct_in_place(params: &Parameters, field: &Field) -> To } } -enum Untagged { - Yes, - No, -} - fn deserialize_struct( variant_ident: Option<&syn::Ident>, params: &Parameters, fields: &[Field], cattrs: &attr::Container, deserializer: Option, - untagged: &Untagged, + untagged: bool, ) -> Fragment { let this_type = ¶ms.this_type; let this_value = ¶ms.this_value; @@ -963,29 +958,28 @@ fn deserialize_struct( // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. - let visit_seq = match *untagged { - Untagged::No if !cattrs.has_flatten() => { - let mut_seq = if field_names_idents.is_empty() { - quote!(_) - } else { - quote!(mut __seq) - }; + let visit_seq = if untagged || cattrs.has_flatten() { + None + } else { + let mut_seq = if field_names_idents.is_empty() { + quote!(_) + } else { + quote!(mut __seq) + }; - let visit_seq = Stmts(deserialize_seq( - &type_path, params, fields, true, cattrs, expecting, - )); + let visit_seq = Stmts(deserialize_seq( + &type_path, params, fields, true, cattrs, expecting, + )); - Some(quote! { - #[inline] - fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - #visit_seq - } - }) - } - _ => None, + Some(quote! { + #[inline] + fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + #visit_seq + } + }) }; let visit_map = Stmts(deserialize_map(&type_path, params, fields, cattrs)); @@ -1804,7 +1798,7 @@ fn deserialize_externally_tagged_variant( &variant.fields, cattrs, None, - &Untagged::No, + false, ), } } @@ -1849,7 +1843,7 @@ fn deserialize_internally_tagged_variant( &variant.fields, cattrs, Some(deserializer), - &Untagged::No, + false, ), Style::Tuple => unreachable!("checked in serde_derive_internals"), } @@ -1908,7 +1902,7 @@ fn deserialize_untagged_variant( &variant.fields, cattrs, Some(deserializer), - &Untagged::Yes, + true, ), } } From ee7d77defa073c419ecd7fa2c4cfa5982ab1d9aa Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 7 May 2023 01:12:49 +0500 Subject: [PATCH 09/10] Replace several linked variables with enumeration for structs --- serde_derive/src/de.rs | 138 ++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index bb090cc60..89068055c 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -287,7 +287,7 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment { match &cont.data { Data::Enum(variants) => deserialize_enum(params, variants, &cont.attrs), Data::Struct(Style::Struct, fields) => { - deserialize_struct(None, params, fields, &cont.attrs, None, false) + deserialize_struct(params, fields, &cont.attrs, StructForm::Struct) } Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => { deserialize_tuple(None, params, fields, &cont.attrs, None) @@ -901,13 +901,23 @@ fn deserialize_newtype_struct_in_place(params: &Parameters, field: &Field) -> To } } +enum StructForm<'a> { + Struct, + /// Contains a variant name + ExternallyTagged(&'a syn::Ident), + /// Contains a variant name and an intermediate deserializer from which actual + /// deserialization will be performed + InternallyTagged(&'a syn::Ident, TokenStream), + /// Contains a variant name and an intermediate deserializer from which actual + /// deserialization will be performed + Untagged(&'a syn::Ident, TokenStream), +} + fn deserialize_struct( - variant_ident: Option<&syn::Ident>, params: &Parameters, fields: &[Field], cattrs: &attr::Container, - deserializer: Option, - untagged: bool, + form: StructForm, ) -> Fragment { let this_type = ¶ms.this_type; let this_value = ¶ms.this_value; @@ -925,13 +935,19 @@ fn deserialize_struct( quote!(#this_value) }; - let type_path = match variant_ident { - Some(variant_ident) => quote!(#construct::#variant_ident), - None => construct, + let type_path = match form { + StructForm::Struct => construct, + StructForm::ExternallyTagged(variant_ident) + | StructForm::InternallyTagged(variant_ident, _) + | StructForm::Untagged(variant_ident, _) => quote!(#construct::#variant_ident), }; - let expecting = match variant_ident { - Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident), - None => format!("struct {}", params.type_name()), + let expecting = match form { + StructForm::Struct => format!("struct {}", params.type_name()), + StructForm::ExternallyTagged(variant_ident) + | StructForm::InternallyTagged(variant_ident, _) + | StructForm::Untagged(variant_ident, _) => { + format!("struct variant {}::{}", params.type_name(), variant_ident) + } }; let expecting = cattrs.expecting().unwrap_or(&expecting); @@ -958,35 +974,35 @@ fn deserialize_struct( // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. - let visit_seq = if untagged || cattrs.has_flatten() { - None - } else { - let mut_seq = if field_names_idents.is_empty() { - quote!(_) - } else { - quote!(mut __seq) - }; + let visit_seq = match form { + StructForm::Untagged(..) => None, + _ if cattrs.has_flatten() => None, + _ => { + let mut_seq = if field_names_idents.is_empty() { + quote!(_) + } else { + quote!(mut __seq) + }; - let visit_seq = Stmts(deserialize_seq( - &type_path, params, fields, true, cattrs, expecting, - )); + let visit_seq = Stmts(deserialize_seq( + &type_path, params, fields, true, cattrs, expecting, + )); - Some(quote! { - #[inline] - fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result - where - __A: _serde::de::SeqAccess<#delife>, - { - #visit_seq - } - }) + Some(quote! { + #[inline] + fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::__private::Result + where + __A: _serde::de::SeqAccess<#delife>, + { + #visit_seq + } + }) + } }; let visit_map = Stmts(deserialize_map(&type_path, params, fields, cattrs)); - let is_enum = variant_ident.is_some(); - let need_seed = deserializer.is_none(); - let visitor_seed = if need_seed && is_enum && cattrs.has_flatten() { - Some(quote! { + let visitor_seed = match form { + StructForm::ExternallyTagged(..) if cattrs.has_flatten() => Some(quote! { impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause { type Value = #this_type #ty_generics; @@ -997,9 +1013,8 @@ fn deserialize_struct( _serde::Deserializer::deserialize_map(__deserializer, self) } } - }) - } else { - None + }), + _ => None, }; let fields_stmt = if cattrs.has_flatten() { @@ -1021,27 +1036,28 @@ fn deserialize_struct( lifetime: _serde::__private::PhantomData, } }; - let dispatch = if let Some(deserializer) = deserializer { - quote! { - _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) + let dispatch = match form { + StructForm::Struct if cattrs.has_flatten() => quote! { + _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) + }, + StructForm::Struct => { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) + } } - } else if is_enum && cattrs.has_flatten() { - quote! { + StructForm::ExternallyTagged(_) if cattrs.has_flatten() => quote! { _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr) - } - } else if is_enum { - quote! { + }, + StructForm::ExternallyTagged(_) => quote! { _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) - } - } else if cattrs.has_flatten() { - quote! { - _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr) - } - } else { - let type_name = cattrs.name().deserialize_name(); - quote! { - _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) - } + }, + StructForm::InternallyTagged(_, deserializer) => quote! { + _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) + }, + StructForm::Untagged(_, deserializer) => quote! { + _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) + }, }; quote_block! { @@ -1793,12 +1809,10 @@ fn deserialize_externally_tagged_variant( deserialize_tuple(Some(variant_ident), params, &variant.fields, cattrs, None) } Style::Struct => deserialize_struct( - Some(variant_ident), params, &variant.fields, cattrs, - None, - false, + StructForm::ExternallyTagged(variant_ident), ), } } @@ -1838,12 +1852,10 @@ fn deserialize_internally_tagged_variant( &deserializer, ), Style::Struct => deserialize_struct( - Some(variant_ident), params, &variant.fields, cattrs, - Some(deserializer), - false, + StructForm::InternallyTagged(variant_ident, deserializer), ), Style::Tuple => unreachable!("checked in serde_derive_internals"), } @@ -1897,12 +1909,10 @@ fn deserialize_untagged_variant( Some(deserializer), ), Style::Struct => deserialize_struct( - Some(variant_ident), params, &variant.fields, cattrs, - Some(deserializer), - true, + StructForm::Untagged(variant_ident, deserializer), ), } } From 4cf1fec575d435d20080ceed6db8ae2ce207132a Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 7 May 2023 01:28:47 +0500 Subject: [PATCH 10/10] Replace several linked variables with enumeration for tuples --- serde_derive/src/de.rs | 81 +++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 89068055c..4a2180938 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -290,7 +290,7 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment { deserialize_struct(params, fields, &cont.attrs, StructForm::Struct) } Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => { - deserialize_tuple(None, params, fields, &cont.attrs, None) + deserialize_tuple(params, fields, &cont.attrs, TupleForm::Tuple) } Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs), } @@ -438,12 +438,20 @@ fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fra } } +enum TupleForm<'a> { + Tuple, + /// Contains a variant name + ExternallyTagged(&'a syn::Ident), + /// Contains a variant name and an intermediate deserializer from which actual + /// deserialization will be performed + Untagged(&'a syn::Ident, TokenStream), +} + fn deserialize_tuple( - variant_ident: Option<&syn::Ident>, params: &Parameters, fields: &[Field], cattrs: &attr::Container, - deserializer: Option, + form: TupleForm, ) -> Fragment { assert!(!cattrs.has_flatten()); @@ -468,23 +476,27 @@ fn deserialize_tuple( quote!(#this_value) }; - let is_enum = variant_ident.is_some(); - let type_path = match variant_ident { - Some(variant_ident) => quote!(#construct::#variant_ident), - None => construct, + let type_path = match form { + TupleForm::Tuple => construct, + TupleForm::ExternallyTagged(variant_ident) | TupleForm::Untagged(variant_ident, _) => { + quote!(#construct::#variant_ident) + } }; - let expecting = match variant_ident { - Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident), - None => format!("tuple struct {}", params.type_name()), + let expecting = match form { + TupleForm::Tuple => format!("tuple struct {}", params.type_name()), + TupleForm::ExternallyTagged(variant_ident) | TupleForm::Untagged(variant_ident, _) => { + format!("tuple variant {}::{}", params.type_name(), variant_ident) + } }; let expecting = cattrs.expecting().unwrap_or(&expecting); let nfields = fields.len(); - let visit_newtype_struct = if !is_enum && nfields == 1 { - Some(deserialize_newtype_struct(&type_path, params, &fields[0])) - } else { - None + let visit_newtype_struct = match form { + TupleForm::Tuple if nfields == 1 => { + Some(deserialize_newtype_struct(&type_path, params, &fields[0])) + } + _ => None, }; let visit_seq = Stmts(deserialize_seq( @@ -497,16 +509,25 @@ fn deserialize_tuple( lifetime: _serde::__private::PhantomData, } }; - let dispatch = if let Some(deserializer) = deserializer { - quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #field_count, #visitor_expr)) - } else if is_enum { - quote!(_serde::de::VariantAccess::tuple_variant(__variant, #field_count, #visitor_expr)) - } else if nfields == 1 { - let type_name = cattrs.name().deserialize_name(); - quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr)) - } else { - let type_name = cattrs.name().deserialize_name(); - quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr)) + let dispatch = match form { + TupleForm::Tuple if nfields == 1 => { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr) + } + } + TupleForm::Tuple => { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #field_count, #visitor_expr) + } + } + TupleForm::ExternallyTagged(_) => quote! { + _serde::de::VariantAccess::tuple_variant(__variant, #field_count, #visitor_expr) + }, + TupleForm::Untagged(_, deserializer) => quote! { + _serde::Deserializer::deserialize_tuple(#deserializer, #field_count, #visitor_expr) + }, }; let visitor_var = if field_count == 0 { @@ -1805,9 +1826,12 @@ fn deserialize_externally_tagged_variant( &variant.fields[0], cattrs, ), - Style::Tuple => { - deserialize_tuple(Some(variant_ident), params, &variant.fields, cattrs, None) - } + Style::Tuple => deserialize_tuple( + params, + &variant.fields, + cattrs, + TupleForm::ExternallyTagged(variant_ident), + ), Style::Struct => deserialize_struct( params, &variant.fields, @@ -1902,11 +1926,10 @@ fn deserialize_untagged_variant( &deserializer, ), Style::Tuple => deserialize_tuple( - Some(variant_ident), params, &variant.fields, cattrs, - Some(deserializer), + TupleForm::Untagged(variant_ident, deserializer), ), Style::Struct => deserialize_struct( params,