| use proc_macro2::TokenStream; |
| use quote::format_ident; |
| |
| 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 |
| // never has its fields referenced and an enum annotated with remote derive |
| // never has its variants constructed. |
| // |
| // warning: field is never used: `i` |
| // --> src/main.rs:4:20 |
| // | |
| // 4 | struct StructDef { i: i32 } |
| // | ^^^^^^ |
| // |
| // warning: variant is never constructed: `V` |
| // --> src/main.rs:8:16 |
| // | |
| // 8 | enum EnumDef { V } |
| // | ^ |
| // |
| 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! { |
| #pretend_fields |
| #pretend_variants |
| } |
| } |
| |
| // 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: __v0, b: __v1 }) => {} |
| // _ => {} |
| // } |
| // |
| // For enums, expands to the following but only including struct variants: |
| // |
| // match None::<&T> { |
| // Some(T::A { a: __v0 }) => {} |
| // Some(T::B { b: __v0 }) => {} |
| // _ => {} |
| // } |
| // |
| 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 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); |
| )* |
| } |
| _ => {} |
| } |
| } |
| } |
| |
| #[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> { |
| #( |
| _serde::__private::Some(#patterns) => {} |
| )* |
| _ => {} |
| } |
| } |
| } |
| |
| // Expands to one of these per enum variant: |
| // |
| // match None { |
| // Some((__v0, __v1,)) => { |
| // let _ = E::V { a: __v0, b: __v1 }; |
| // } |
| // _ => {} |
| // } |
| // |
| fn pretend_variants_used(cont: &Container) -> TokenStream { |
| let variants = match &cont.data { |
| Data::Enum(variants) => variants, |
| Data::Struct(_, _) => { |
| return quote!(); |
| } |
| }; |
| |
| let type_ident = &cont.ident; |
| let (_, ty_generics, _) = cont.generics.split_for_impl(); |
| let turbofish = ty_generics.as_turbofish(); |
| |
| let cases = variants.iter().map(|variant| { |
| let variant_ident = &variant.ident; |
| let placeholders = &(0..variant.fields.len()) |
| .map(|i| format_ident!("__v{}", i)) |
| .collect::<Vec<_>>(); |
| |
| let pat = match variant.style { |
| Style::Struct => { |
| let members = variant.fields.iter().map(|field| &field.member); |
| quote!({ #(#members: #placeholders),* }) |
| } |
| Style::Tuple | Style::Newtype => quote!(( #(#placeholders),* )), |
| Style::Unit => quote!(), |
| }; |
| |
| quote! { |
| match _serde::__private::None { |
| _serde::__private::Some((#(#placeholders,)*)) => { |
| let _ = #type_ident::#variant_ident #turbofish #pat; |
| } |
| _ => {} |
| } |
| } |
| }); |
| |
| quote!(#(#cases)*) |
| } |