serde_derive/
pretend.rs

1use proc_macro2::{Span, TokenStream};
2use syn::Ident;
3
4use internals::ast::{Container, Data, Field, Style};
5
6// Suppress dead_code warnings that would otherwise appear when using a remote
7// derive. Other than this pretend code, a struct annotated with remote derive
8// never has its fields referenced and an enum annotated with remote derive
9// never has its variants constructed.
10//
11//     warning: field is never used: `i`
12//      --> src/main.rs:4:20
13//       |
14//     4 | struct StructDef { i: i32 }
15//       |                    ^^^^^^
16//
17//     warning: variant is never constructed: `V`
18//      --> src/main.rs:8:16
19//       |
20//     8 | enum EnumDef { V }
21//       |                ^
22//
23pub fn pretend_used(cont: &Container) -> TokenStream {
24    let pretend_fields = pretend_fields_used(cont);
25    let pretend_variants = pretend_variants_used(cont);
26
27    quote! {
28        #pretend_fields
29        #pretend_variants
30    }
31}
32
33// For structs with named fields, expands to:
34//
35//     match None::<T> {
36//         Some(T { a: ref __v0, b: ref __v1 }) => {}
37//         _ => {}
38//     }
39//
40// For enums, expands to the following but only including struct variants:
41//
42//     match None::<T> {
43//         Some(T::A { a: ref __v0 }) => {}
44//         Some(T::B { b: ref __v0 }) => {}
45//         _ => {}
46//     }
47//
48// The `ref` is important in case the user has written a Drop impl on their
49// type. Rust does not allow destructuring a struct or enum that has a Drop
50// impl.
51fn pretend_fields_used(cont: &Container) -> TokenStream {
52    let type_ident = &cont.ident;
53    let (_, ty_generics, _) = cont.generics.split_for_impl();
54
55    let patterns = match &cont.data {
56        Data::Enum(variants) => variants
57            .iter()
58            .filter_map(|variant| match variant.style {
59                Style::Struct => {
60                    let variant_ident = &variant.ident;
61                    let pat = struct_pattern(&variant.fields);
62                    Some(quote!(#type_ident::#variant_ident #pat))
63                }
64                _ => None,
65            })
66            .collect::<Vec<_>>(),
67        Data::Struct(Style::Struct, fields) => {
68            let pat = struct_pattern(fields);
69            vec![quote!(#type_ident #pat)]
70        }
71        Data::Struct(_, _) => {
72            return quote!();
73        }
74    };
75
76    quote! {
77        match _serde::export::None::<#type_ident #ty_generics> {
78            #(
79                _serde::export::Some(#patterns) => {}
80            )*
81            _ => {}
82        }
83    }
84}
85
86// Expands to one of these per enum variant:
87//
88//     match None {
89//         Some((__v0, __v1,)) => {
90//             let _ = E::V { a: __v0, b: __v1 };
91//         }
92//         _ => {}
93//     }
94//
95fn pretend_variants_used(cont: &Container) -> TokenStream {
96    let variants = match &cont.data {
97        Data::Enum(variants) => variants,
98        Data::Struct(_, _) => {
99            return quote!();
100        }
101    };
102
103    let type_ident = &cont.ident;
104    let (_, ty_generics, _) = cont.generics.split_for_impl();
105    let turbofish = ty_generics.as_turbofish();
106
107    let cases = variants.iter().map(|variant| {
108        let variant_ident = &variant.ident;
109        let placeholders = &(0..variant.fields.len())
110            .map(|i| Ident::new(&format!("__v{}", i), Span::call_site()))
111            .collect::<Vec<_>>();
112
113        let pat = match variant.style {
114            Style::Struct => {
115                let members = variant.fields.iter().map(|field| &field.member);
116                quote!({ #(#members: #placeholders),* })
117            }
118            Style::Tuple | Style::Newtype => quote!(( #(#placeholders),* )),
119            Style::Unit => quote!(),
120        };
121
122        quote! {
123            match _serde::export::None {
124                _serde::export::Some((#(#placeholders,)*)) => {
125                    let _ = #type_ident::#variant_ident #turbofish #pat;
126                }
127                _ => {}
128            }
129        }
130    });
131
132    quote!(#(#cases)*)
133}
134
135fn struct_pattern(fields: &[Field]) -> TokenStream {
136    let members = fields.iter().map(|field| &field.member);
137    let placeholders =
138        (0..fields.len()).map(|i| Ident::new(&format!("__v{}", i), Span::call_site()));
139    quote!({ #(#members: ref #placeholders),* })
140}