diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/de.rs | 56 | ||||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/pretend.rs | 137 | ||||
-rw-r--r-- | src/ser.rs | 2 |
4 files changed, 141 insertions, 58 deletions
@@ -36,7 +36,7 @@ pub fn expand_derive_deserialize( let impl_block = if let Some(remote) = cont.attrs.remote() { let vis = &input.vis; - let used = pretend::pretend_used(&cont); + let used = pretend::pretend_used(&cont, params.is_packed); quote! { impl #de_impl_generics #ident #ty_generics #where_clause { #vis fn deserialize<__D>(__deserializer: __D) -> #serde::__private::Result<#remote #ty_generics, __D::Error> @@ -125,6 +125,9 @@ struct Parameters { /// At least one field has a serde(getter) attribute, implying that the /// remote type has a private field. has_getter: bool, + + /// Type has a repr(packed) attribute. + is_packed: bool, } impl Parameters { @@ -137,6 +140,7 @@ impl Parameters { let borrowed = borrowed_lifetimes(cont); let generics = build_generics(cont, &borrowed); let has_getter = cont.data.has_getter(); + let is_packed = cont.attrs.is_packed(); Parameters { local, @@ -144,6 +148,7 @@ impl Parameters { generics, borrowed, has_getter, + is_packed, } } @@ -1728,6 +1733,8 @@ fn deserialize_externally_tagged_variant( } } +// Generates significant part of the visit_seq and visit_map bodies of visitors +// for the variants of internally tagged enum. fn deserialize_internally_tagged_variant( params: &Parameters, variant: &Variant, @@ -1779,11 +1786,9 @@ fn deserialize_untagged_variant( deserializer: TokenStream, ) -> Fragment { if let Some(path) = variant.attrs.deserialize_with() { - let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path); + let unwrap_fn = unwrap_to_variant_closure(params, variant, false); return quote_block! { - #wrapper - _serde::__private::Result::map( - <#wrapper_ty as _serde::Deserialize>::deserialize(#deserializer), #unwrap_fn) + _serde::__private::Result::map(#path(#deserializer), #unwrap_fn) }; } @@ -2883,44 +2888,61 @@ fn wrap_deserialize_variant_with( variant: &Variant, deserialize_with: &syn::ExprPath, ) -> (TokenStream, TokenStream, TokenStream) { - let this = ¶ms.this; - let variant_ident = &variant.ident; - let field_tys = variant.fields.iter().map(|field| field.ty); let (wrapper, wrapper_ty) = wrap_deserialize_with(params, "e!((#(#field_tys),*)), deserialize_with); + let unwrap_fn = unwrap_to_variant_closure(params, variant, true); + + (wrapper, wrapper_ty, unwrap_fn) +} + +// Generates closure that converts single input parameter to the final value. +fn unwrap_to_variant_closure( + params: &Parameters, + variant: &Variant, + with_wrapper: bool, +) -> TokenStream { + let this = ¶ms.this; + let variant_ident = &variant.ident; + + let (arg, wrapper) = if with_wrapper { + (quote! { __wrap }, quote! { __wrap.value }) + } else { + let field_tys = variant.fields.iter().map(|field| field.ty); + (quote! { __wrap: (#(#field_tys),*) }, quote! { __wrap }) + }; + let field_access = (0..variant.fields.len()).map(|n| { Member::Unnamed(Index { index: n as u32, span: Span::call_site(), }) }); - let unwrap_fn = match variant.style { + + match variant.style { Style::Struct if variant.fields.len() == 1 => { let member = &variant.fields[0].member; quote! { - |__wrap| #this::#variant_ident { #member: __wrap.value } + |#arg| #this::#variant_ident { #member: #wrapper } } } Style::Struct => { let members = variant.fields.iter().map(|field| &field.member); quote! { - |__wrap| #this::#variant_ident { #(#members: __wrap.value.#field_access),* } + |#arg| #this::#variant_ident { #(#members: #wrapper.#field_access),* } } } Style::Tuple => quote! { - |__wrap| #this::#variant_ident(#(__wrap.value.#field_access),*) + |#arg| #this::#variant_ident(#(#wrapper.#field_access),*) }, Style::Newtype => quote! { - |__wrap| #this::#variant_ident(__wrap.value) + |#arg| #this::#variant_ident(#wrapper) }, Style::Unit => quote! { - |__wrap| #this::#variant_ident + |#arg| #this::#variant_ident }, - }; - - (wrapper, wrapper_ty, unwrap_fn) + } } fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment { @@ -13,7 +13,7 @@ //! //! [https://serde.rs/derive.html]: https://serde.rs/derive.html -#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.127")] +#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.130")] #![allow(unknown_lints, bare_trait_objects)] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints @@ -21,6 +21,8 @@ // clippy false positive: https://github.com/rust-lang/rust-clippy/issues/7054 clippy::branches_sharing_code, clippy::cognitive_complexity, + // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7575 + clippy::collapsible_match, clippy::enum_variant_names, // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6797 clippy::manual_map, diff --git a/src/pretend.rs b/src/pretend.rs index 955ce3d..c4e5874 100644 --- a/src/pretend.rs +++ b/src/pretend.rs @@ -1,7 +1,7 @@ -use proc_macro2::{Span, TokenStream}; -use syn::Ident; +use proc_macro2::TokenStream; +use quote::format_ident; -use internals::ast::{Container, Data, Field, Style}; +use internals::ast::{Container, Data, Field, Style, Variant}; // Suppress dead_code warnings that would otherwise appear when using a remote // derive. Other than this pretend code, a struct annotated with remote derive @@ -20,8 +20,8 @@ use internals::ast::{Container, Data, Field, Style}; // 8 | enum EnumDef { V } // | ^ // -pub fn pretend_used(cont: &Container) -> TokenStream { - let pretend_fields = pretend_fields_used(cont); +pub fn pretend_used(cont: &Container, is_packed: bool) -> TokenStream { + let pretend_fields = pretend_fields_used(cont, is_packed); let pretend_variants = pretend_variants_used(cont); quote! { @@ -32,49 +32,115 @@ pub fn pretend_used(cont: &Container) -> TokenStream { // For structs with named fields, expands to: // +// match None::<&T> { +// Some(T { a: __v0, b: __v1 }) => {} +// _ => {} +// } +// +// For packed structs on sufficiently new rustc, expands to: +// +// match None::<&T> { +// Some(__v @ T { a: _, b: _ }) => { +// let _ = addr_of!(__v.a); +// let _ = addr_of!(__v.b); +// } +// _ => {} +// } +// +// For packed structs on older rustc, we assume Sized and !Drop, and expand to: +// // match None::<T> { -// Some(T { a: ref __v0, b: ref __v1 }) => {} +// Some(T { a: __v0, b: __v1 }) => {} // _ => {} // } // // For enums, expands to the following but only including struct variants: // -// match None::<T> { -// Some(T::A { a: ref __v0 }) => {} -// Some(T::B { b: ref __v0 }) => {} +// match None::<&T> { +// Some(T::A { a: __v0 }) => {} +// Some(T::B { b: __v0 }) => {} // _ => {} // } // -// The `ref` is important in case the user has written a Drop impl on their -// type. Rust does not allow destructuring a struct or enum that has a Drop -// impl. -fn pretend_fields_used(cont: &Container) -> TokenStream { +fn pretend_fields_used(cont: &Container, is_packed: bool) -> TokenStream { + match &cont.data { + Data::Enum(variants) => pretend_fields_used_enum(cont, variants), + Data::Struct(Style::Struct, fields) => if is_packed { + pretend_fields_used_struct_packed(cont, fields) + } else { + pretend_fields_used_struct(cont, fields) + }, + Data::Struct(_, _) => quote!(), + } +} + +fn pretend_fields_used_struct(cont: &Container, fields: &[Field]) -> TokenStream { let type_ident = &cont.ident; let (_, ty_generics, _) = cont.generics.split_for_impl(); - let patterns = match &cont.data { - Data::Enum(variants) => variants - .iter() - .filter_map(|variant| match variant.style { - Style::Struct => { - let variant_ident = &variant.ident; - let pat = struct_pattern(&variant.fields); - Some(quote!(#type_ident::#variant_ident #pat)) + let members = fields.iter().map(|field| &field.member); + let placeholders = (0usize..).map(|i| format_ident!("__v{}", i)); + + quote! { + match _serde::__private::None::<&#type_ident #ty_generics> { + _serde::__private::Some(#type_ident { #(#members: #placeholders),* }) => {} + _ => {} + } + } +} + +fn pretend_fields_used_struct_packed(cont: &Container, fields: &[Field]) -> TokenStream { + let type_ident = &cont.ident; + let (_, ty_generics, _) = cont.generics.split_for_impl(); + + let members = fields.iter().map(|field| &field.member).collect::<Vec<_>>(); + + #[cfg(ptr_addr_of)] + { + quote! { + match _serde::__private::None::<&#type_ident #ty_generics> { + _serde::__private::Some(__v @ #type_ident { #(#members: _),* }) => { + #( + let _ = _serde::__private::ptr::addr_of!(__v.#members); + )* } - _ => None, - }) - .collect::<Vec<_>>(), - Data::Struct(Style::Struct, fields) => { - let pat = struct_pattern(fields); - vec![quote!(#type_ident #pat)] + _ => {} + } } - Data::Struct(_, _) => { - return quote!(); + } + + #[cfg(not(ptr_addr_of))] + { + let placeholders = (0usize..).map(|i| format_ident!("__v{}", i)); + + quote! { + match _serde::__private::None::<#type_ident #ty_generics> { + _serde::__private::Some(#type_ident { #(#members: #placeholders),* }) => {} + _ => {} + } } - }; + } +} + +fn pretend_fields_used_enum(cont: &Container, variants: &[Variant]) -> TokenStream { + let type_ident = &cont.ident; + let (_, ty_generics, _) = cont.generics.split_for_impl(); + + let patterns = variants + .iter() + .filter_map(|variant| match variant.style { + Style::Struct => { + let variant_ident = &variant.ident; + let members = variant.fields.iter().map(|field| &field.member); + let placeholders = (0usize..).map(|i| format_ident!("__v{}", i)); + Some(quote!(#type_ident::#variant_ident { #(#members: #placeholders),* })) + } + _ => None, + }) + .collect::<Vec<_>>(); quote! { - match _serde::__private::None::<#type_ident #ty_generics> { + match _serde::__private::None::<&#type_ident #ty_generics> { #( _serde::__private::Some(#patterns) => {} )* @@ -107,7 +173,7 @@ fn pretend_variants_used(cont: &Container) -> TokenStream { let cases = variants.iter().map(|variant| { let variant_ident = &variant.ident; let placeholders = &(0..variant.fields.len()) - .map(|i| Ident::new(&format!("__v{}", i), Span::call_site())) + .map(|i| format_ident!("__v{}", i)) .collect::<Vec<_>>(); let pat = match variant.style { @@ -131,10 +197,3 @@ fn pretend_variants_used(cont: &Container) -> TokenStream { quote!(#(#cases)*) } - -fn struct_pattern(fields: &[Field]) -> TokenStream { - let members = fields.iter().map(|field| &field.member); - let placeholders = - (0..fields.len()).map(|i| Ident::new(&format!("__v{}", i), Span::call_site())); - quote!({ #(#members: ref #placeholders),* }) -} @@ -30,7 +30,7 @@ pub fn expand_derive_serialize( let impl_block = if let Some(remote) = cont.attrs.remote() { let vis = &input.vis; - let used = pretend::pretend_used(&cont); + let used = pretend::pretend_used(&cont, params.is_packed); quote! { impl #impl_generics #ident #ty_generics #where_clause { #vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> #serde::__private::Result<__S::Ok, __S::Error> |