Merge pull request #2795 from Mingun/has-flatten-rework

`has_flatten` rework
This commit is contained in:
David Tolnay
2024-08-12 13:02:58 -07:00
committed by GitHub
5 changed files with 54 additions and 114 deletions
+17 -25
View File
@@ -281,21 +281,11 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
} else if let attr::Identifier::No = cont.attrs.identifier() { } else if let attr::Identifier::No = cont.attrs.identifier() {
match &cont.data { match &cont.data {
Data::Enum(variants) => deserialize_enum(params, variants, &cont.attrs), Data::Enum(variants) => deserialize_enum(params, variants, &cont.attrs),
Data::Struct(Style::Struct, fields) => deserialize_struct( Data::Struct(Style::Struct, fields) => {
params, deserialize_struct(params, fields, &cont.attrs, StructForm::Struct)
fields, }
&cont.attrs,
cont.attrs.has_flatten(),
StructForm::Struct,
),
Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => { Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => {
deserialize_tuple( deserialize_tuple(params, fields, &cont.attrs, TupleForm::Tuple)
params,
fields,
&cont.attrs,
cont.attrs.has_flatten(),
TupleForm::Tuple,
)
} }
Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs), Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs),
} }
@@ -469,11 +459,10 @@ fn deserialize_tuple(
params: &Parameters, params: &Parameters,
fields: &[Field], fields: &[Field],
cattrs: &attr::Container, cattrs: &attr::Container,
has_flatten: bool,
form: TupleForm, form: TupleForm,
) -> Fragment { ) -> Fragment {
assert!( assert!(
!has_flatten, !has_flatten(fields),
"tuples and tuple variants cannot have flatten fields" "tuples and tuple variants cannot have flatten fields"
); );
@@ -594,7 +583,7 @@ fn deserialize_tuple_in_place(
cattrs: &attr::Container, cattrs: &attr::Container,
) -> Fragment { ) -> Fragment {
assert!( assert!(
!cattrs.has_flatten(), !has_flatten(fields),
"tuples and tuple variants cannot have flatten fields" "tuples and tuple variants cannot have flatten fields"
); );
@@ -927,7 +916,6 @@ fn deserialize_struct(
params: &Parameters, params: &Parameters,
fields: &[Field], fields: &[Field],
cattrs: &attr::Container, cattrs: &attr::Container,
has_flatten: bool,
form: StructForm, form: StructForm,
) -> Fragment { ) -> Fragment {
let this_type = &params.this_type; let this_type = &params.this_type;
@@ -976,6 +964,8 @@ fn deserialize_struct(
) )
}) })
.collect(); .collect();
let has_flatten = has_flatten(fields);
let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs, has_flatten); let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs, has_flatten);
// untagged struct variants do not get a visit_seq method. The same applies to // untagged struct variants do not get a visit_seq method. The same applies to
@@ -1115,7 +1105,7 @@ fn deserialize_struct_in_place(
) -> Option<Fragment> { ) -> Option<Fragment> {
// for now we do not support in_place deserialization for structs that // for now we do not support in_place deserialization for structs that
// are represented as map. // are represented as map.
if cattrs.has_flatten() { if has_flatten(fields) {
return None; return None;
} }
@@ -1831,14 +1821,12 @@ fn deserialize_externally_tagged_variant(
params, params,
&variant.fields, &variant.fields,
cattrs, cattrs,
variant.attrs.has_flatten(),
TupleForm::ExternallyTagged(variant_ident), TupleForm::ExternallyTagged(variant_ident),
), ),
Style::Struct => deserialize_struct( Style::Struct => deserialize_struct(
params, params,
&variant.fields, &variant.fields,
cattrs, cattrs,
variant.attrs.has_flatten(),
StructForm::ExternallyTagged(variant_ident), StructForm::ExternallyTagged(variant_ident),
), ),
} }
@@ -1882,7 +1870,6 @@ fn deserialize_internally_tagged_variant(
params, params,
&variant.fields, &variant.fields,
cattrs, cattrs,
variant.attrs.has_flatten(),
StructForm::InternallyTagged(variant_ident, deserializer), StructForm::InternallyTagged(variant_ident, deserializer),
), ),
Style::Tuple => unreachable!("checked in serde_derive_internals"), Style::Tuple => unreachable!("checked in serde_derive_internals"),
@@ -1933,14 +1920,12 @@ fn deserialize_untagged_variant(
params, params,
&variant.fields, &variant.fields,
cattrs, cattrs,
variant.attrs.has_flatten(),
TupleForm::Untagged(variant_ident, deserializer), TupleForm::Untagged(variant_ident, deserializer),
), ),
Style::Struct => deserialize_struct( Style::Struct => deserialize_struct(
params, params,
&variant.fields, &variant.fields,
cattrs, cattrs,
variant.attrs.has_flatten(),
StructForm::Untagged(variant_ident, deserializer), StructForm::Untagged(variant_ident, deserializer),
), ),
} }
@@ -2707,7 +2692,7 @@ fn deserialize_map_in_place(
cattrs: &attr::Container, cattrs: &attr::Container,
) -> Fragment { ) -> Fragment {
assert!( assert!(
!cattrs.has_flatten(), !has_flatten(fields),
"inplace deserialization of maps does not support flatten fields" "inplace deserialization of maps does not support flatten fields"
); );
@@ -3042,6 +3027,13 @@ fn effective_style(variant: &Variant) -> Style {
} }
} }
/// True if there are fields that is not skipped and has a `#[serde(flatten)]` attribute.
fn has_flatten(fields: &[Field]) -> bool {
fields
.iter()
.any(|field| field.attrs.flatten() && !field.attrs.skip_deserializing())
}
struct DeImplGenerics<'a>(&'a Parameters); struct DeImplGenerics<'a>(&'a Parameters);
#[cfg(feature = "deserialize_in_place")] #[cfg(feature = "deserialize_in_place")]
struct InPlaceImplGenerics<'a>(&'a Parameters); struct InPlaceImplGenerics<'a>(&'a Parameters);
+1 -13
View File
@@ -63,7 +63,7 @@ impl<'a> Container<'a> {
item: &'a syn::DeriveInput, item: &'a syn::DeriveInput,
derive: Derive, derive: Derive,
) -> Option<Container<'a>> { ) -> Option<Container<'a>> {
let mut attrs = attr::Container::from_ast(cx, item); let attrs = attr::Container::from_ast(cx, item);
let mut data = match &item.data { let mut data = match &item.data {
syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())), syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())),
@@ -77,16 +77,11 @@ impl<'a> Container<'a> {
} }
}; };
let mut has_flatten = false;
match &mut data { match &mut data {
Data::Enum(variants) => { Data::Enum(variants) => {
for variant in variants { for variant in variants {
variant.attrs.rename_by_rules(attrs.rename_all_rules()); variant.attrs.rename_by_rules(attrs.rename_all_rules());
for field in &mut variant.fields { for field in &mut variant.fields {
if field.attrs.flatten() {
has_flatten = true;
variant.attrs.mark_has_flatten();
}
field.attrs.rename_by_rules( field.attrs.rename_by_rules(
variant variant
.attrs .attrs
@@ -98,18 +93,11 @@ impl<'a> Container<'a> {
} }
Data::Struct(_, fields) => { Data::Struct(_, fields) => {
for field in fields { for field in fields {
if field.attrs.flatten() {
has_flatten = true;
}
field.attrs.rename_by_rules(attrs.rename_all_rules()); field.attrs.rename_by_rules(attrs.rename_all_rules());
} }
} }
} }
if has_flatten {
attrs.mark_has_flatten();
}
let mut item = Container { let mut item = Container {
ident: item.ident.clone(), ident: item.ident.clone(),
attrs, attrs,
-47
View File
@@ -216,23 +216,6 @@ pub struct Container {
type_into: Option<syn::Type>, type_into: Option<syn::Type>,
remote: Option<syn::Path>, remote: Option<syn::Path>,
identifier: Identifier, identifier: Identifier,
/// True if container is a struct and has a field with `#[serde(flatten)]`,
/// or is an enum with a struct variant which has a field with
/// `#[serde(flatten)]`.
///
/// ```ignore
/// struct Container {
/// #[serde(flatten)]
/// some_field: (),
/// }
/// enum Container {
/// Variant {
/// #[serde(flatten)]
/// some_field: (),
/// },
/// }
/// ```
has_flatten: bool,
serde_path: Option<syn::Path>, serde_path: Option<syn::Path>,
is_packed: bool, is_packed: bool,
/// Error message generated when type can't be deserialized /// Error message generated when type can't be deserialized
@@ -603,7 +586,6 @@ impl Container {
type_into: type_into.get(), type_into: type_into.get(),
remote: remote.get(), remote: remote.get(),
identifier: decide_identifier(cx, item, field_identifier, variant_identifier), identifier: decide_identifier(cx, item, field_identifier, variant_identifier),
has_flatten: false,
serde_path: serde_path.get(), serde_path: serde_path.get(),
is_packed, is_packed,
expecting: expecting.get(), expecting: expecting.get(),
@@ -671,14 +653,6 @@ impl Container {
self.identifier self.identifier
} }
pub fn has_flatten(&self) -> bool {
self.has_flatten
}
pub fn mark_has_flatten(&mut self) {
self.has_flatten = true;
}
pub fn custom_serde_path(&self) -> Option<&syn::Path> { pub fn custom_serde_path(&self) -> Option<&syn::Path> {
self.serde_path.as_ref() self.serde_path.as_ref()
} }
@@ -810,18 +784,6 @@ pub struct Variant {
rename_all_rules: RenameAllRules, rename_all_rules: RenameAllRules,
ser_bound: Option<Vec<syn::WherePredicate>>, ser_bound: Option<Vec<syn::WherePredicate>>,
de_bound: Option<Vec<syn::WherePredicate>>, de_bound: Option<Vec<syn::WherePredicate>>,
/// True if variant is a struct variant which contains a field with
/// `#[serde(flatten)]`.
///
/// ```ignore
/// enum Enum {
/// Variant {
/// #[serde(flatten)]
/// some_field: (),
/// },
/// }
/// ```
has_flatten: bool,
skip_deserializing: bool, skip_deserializing: bool,
skip_serializing: bool, skip_serializing: bool,
other: bool, other: bool,
@@ -991,7 +953,6 @@ impl Variant {
}, },
ser_bound: ser_bound.get(), ser_bound: ser_bound.get(),
de_bound: de_bound.get(), de_bound: de_bound.get(),
has_flatten: false,
skip_deserializing: skip_deserializing.get(), skip_deserializing: skip_deserializing.get(),
skip_serializing: skip_serializing.get(), skip_serializing: skip_serializing.get(),
other: other.get(), other: other.get(),
@@ -1034,14 +995,6 @@ impl Variant {
self.de_bound.as_ref().map(|vec| &vec[..]) self.de_bound.as_ref().map(|vec| &vec[..])
} }
pub fn has_flatten(&self) -> bool {
self.has_flatten
}
pub fn mark_has_flatten(&mut self) {
self.has_flatten = true;
}
pub fn skip_deserializing(&self) -> bool { pub fn skip_deserializing(&self) -> bool {
self.skip_deserializing self.skip_deserializing
} }
+12 -21
View File
@@ -289,9 +289,18 @@ fn serialize_tuple_struct(
} }
fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment { fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
assert!(fields.len() as u64 <= u64::from(u32::MAX)); assert!(
fields.len() as u64 <= u64::from(u32::MAX),
"too many fields in {}: {}, maximum supported count is {}",
cattrs.name().serialize_name(),
fields.len(),
u32::MAX
);
if cattrs.has_flatten() { let has_non_skipped_flatten = fields
.iter()
.any(|field| field.attrs.flatten() && !field.attrs.skip_serializing());
if has_non_skipped_flatten {
serialize_struct_as_map(params, fields, cattrs) serialize_struct_as_map(params, fields, cattrs)
} else { } else {
serialize_struct_as_struct(params, fields, cattrs) serialize_struct_as_struct(params, fields, cattrs)
@@ -370,26 +379,8 @@ fn serialize_struct_as_map(
let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists); let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists);
let len = if cattrs.has_flatten() {
quote!(_serde::__private::None)
} else {
let len = serialized_fields
.map(|field| match field.attrs.skip_serializing_if() {
None => quote!(1),
Some(path) => {
let field_expr = get_member(params, field, &field.member);
quote!(if #path(#field_expr) { 0 } else { 1 })
}
})
.fold(
quote!(#tag_field_exists as usize),
|sum, expr| quote!(#sum + #expr),
);
quote!(_serde::__private::Some(#len))
};
quote_block! { quote_block! {
let #let_mut __serde_state = _serde::Serializer::serialize_map(__serializer, #len)?; let #let_mut __serde_state = _serde::Serializer::serialize_map(__serializer, _serde::__private::None)?;
#tag_field #tag_field
#(#serialize_fields)* #(#serialize_fields)*
_serde::ser::SerializeMap::end(__serde_state) _serde::ser::SerializeMap::end(__serde_state)
+24 -8
View File
@@ -547,6 +547,12 @@ fn test_gen() {
} }
assert::<FlattenWith>(); assert::<FlattenWith>();
#[derive(Serialize, Deserialize)]
pub struct Flatten<T> {
#[serde(flatten)]
t: T,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct FlattenDenyUnknown<T> { pub struct FlattenDenyUnknown<T> {
@@ -554,6 +560,19 @@ fn test_gen() {
t: T, t: T,
} }
#[derive(Serialize, Deserialize)]
pub struct SkipDeserializing<T> {
#[serde(skip_deserializing)]
flat: T,
}
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SkipDeserializingDenyUnknown<T> {
#[serde(skip_deserializing)]
flat: T,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct StaticStrStruct<'a> { pub struct StaticStrStruct<'a> {
a: &'a str, a: &'a str,
@@ -720,14 +739,11 @@ fn test_gen() {
flat: StdOption<T>, flat: StdOption<T>,
} }
#[allow(clippy::collection_is_never_read)] // FIXME #[derive(Serialize, Deserialize)]
const _: () = { pub struct FlattenSkipDeserializing<T> {
#[derive(Serialize, Deserialize)] #[serde(flatten, skip_deserializing)]
pub struct FlattenSkipDeserializing<T> { flat: T,
#[serde(flatten, skip_deserializing)] }
flat: T,
}
};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]