serde_derive/
pretend.rs

1use crate::internals::ast::{Container, Data, Field, Style, Variant};
2use proc_macro2::TokenStream;
3use quote::{format_ident, quote};
4
5// Suppress dead_code warnings that would otherwise appear when using a remote
6// derive. Other than this pretend code, a struct annotated with remote derive
7// never has its fields referenced and an enum annotated with remote derive
8// never has its variants constructed.
9//
10//     warning: field is never used: `i`
11//      --> src/main.rs:4:20
12//       |
13//     4 | struct StructDef { i: i32 }
14//       |                    ^^^^^^
15//
16//     warning: variant is never constructed: `V`
17//      --> src/main.rs:8:16
18//       |
19//     8 | enum EnumDef { V }
20//       |                ^
21//
22pub fn pretend_used(cont: &Container, is_packed: bool) -> TokenStream {
23    let pretend_fields = pretend_fields_used(cont, is_packed);
24    let pretend_variants = pretend_variants_used(cont);
25
26    quote! {
27        #pretend_fields
28        #pretend_variants
29    }
30}
31
32// For structs with named fields, expands to:
33//
34//     match None::<&T> {
35//         Some(T { a: __v0, b: __v1 }) => {}
36//         _ => {}
37//     }
38//
39// For packed structs on sufficiently new rustc, expands to:
40//
41//     match None::<&T> {
42//         Some(__v @ T { a: _, b: _ }) => {
43//             let _ = addr_of!(__v.a);
44//             let _ = addr_of!(__v.b);
45//         }
46//         _ => {}
47//     }
48//
49// For packed structs on older rustc, we assume Sized and !Drop, and expand to:
50//
51//     match None::<T> {
52//         Some(T { a: __v0, b: __v1 }) => {}
53//         _ => {}
54//     }
55//
56// For enums, expands to the following but only including struct variants:
57//
58//     match None::<&T> {
59//         Some(T::A { a: __v0 }) => {}
60//         Some(T::B { b: __v0 }) => {}
61//         _ => {}
62//     }
63//
64fn pretend_fields_used(cont: &Container, is_packed: bool) -> TokenStream {
65    match &cont.data {
66        Data::Enum(variants) => pretend_fields_used_enum(cont, variants),
67        Data::Struct(Style::Struct | Style::Tuple | Style::Newtype, fields) => {
68            if is_packed {
69                pretend_fields_used_struct_packed(cont, fields)
70            } else {
71                pretend_fields_used_struct(cont, fields)
72            }
73        }
74        Data::Struct(Style::Unit, _) => quote!(),
75    }
76}
77
78fn pretend_fields_used_struct(cont: &Container, fields: &[Field]) -> TokenStream {
79    let type_ident = &cont.ident;
80    let (_, ty_generics, _) = cont.generics.split_for_impl();
81
82    let members = fields.iter().map(|field| &field.member);
83    let placeholders = (0usize..).map(|i| format_ident!("__v{}", i));
84
85    quote! {
86        match _serde::__private::None::<&#type_ident #ty_generics> {
87            _serde::__private::Some(#type_ident { #(#members: #placeholders),* }) => {}
88            _ => {}
89        }
90    }
91}
92
93fn pretend_fields_used_struct_packed(cont: &Container, fields: &[Field]) -> TokenStream {
94    let type_ident = &cont.ident;
95    let (_, ty_generics, _) = cont.generics.split_for_impl();
96
97    let members = fields.iter().map(|field| &field.member).collect::<Vec<_>>();
98
99    quote! {
100        match _serde::__private::None::<&#type_ident #ty_generics> {
101            _serde::__private::Some(__v @ #type_ident { #(#members: _),* }) => {
102                #(
103                    let _ = _serde::__private::ptr::addr_of!(__v.#members);
104                )*
105            }
106            _ => {}
107        }
108    }
109}
110
111fn pretend_fields_used_enum(cont: &Container, variants: &[Variant]) -> TokenStream {
112    let type_ident = &cont.ident;
113    let (_, ty_generics, _) = cont.generics.split_for_impl();
114
115    let patterns = variants
116        .iter()
117        .filter_map(|variant| match variant.style {
118            Style::Struct | Style::Tuple | Style::Newtype => {
119                let variant_ident = &variant.ident;
120                let members = variant.fields.iter().map(|field| &field.member);
121                let placeholders = (0usize..).map(|i| format_ident!("__v{}", i));
122                Some(quote!(#type_ident::#variant_ident { #(#members: #placeholders),* }))
123            }
124            Style::Unit => None,
125        })
126        .collect::<Vec<_>>();
127
128    quote! {
129        match _serde::__private::None::<&#type_ident #ty_generics> {
130            #(
131                _serde::__private::Some(#patterns) => {}
132            )*
133            _ => {}
134        }
135    }
136}
137
138// Expands to one of these per enum variant:
139//
140//     match None {
141//         Some((__v0, __v1,)) => {
142//             let _ = E::V { a: __v0, b: __v1 };
143//         }
144//         _ => {}
145//     }
146//
147fn pretend_variants_used(cont: &Container) -> TokenStream {
148    let variants = match &cont.data {
149        Data::Enum(variants) => variants,
150        Data::Struct(_, _) => {
151            return quote!();
152        }
153    };
154
155    let type_ident = &cont.ident;
156    let (_, ty_generics, _) = cont.generics.split_for_impl();
157    let turbofish = ty_generics.as_turbofish();
158
159    let cases = variants.iter().map(|variant| {
160        let variant_ident = &variant.ident;
161        let placeholders = &(0..variant.fields.len())
162            .map(|i| format_ident!("__v{}", i))
163            .collect::<Vec<_>>();
164
165        let pat = match variant.style {
166            Style::Struct => {
167                let members = variant.fields.iter().map(|field| &field.member);
168                quote!({ #(#members: #placeholders),* })
169            }
170            Style::Tuple | Style::Newtype => quote!(( #(#placeholders),* )),
171            Style::Unit => quote!(),
172        };
173
174        quote! {
175            match _serde::__private::None {
176                _serde::__private::Some((#(#placeholders,)*)) => {
177                    let _ = #type_ident::#variant_ident #turbofish #pat;
178                }
179                _ => {}
180            }
181        }
182    });
183
184    quote!(#(#cases)*)
185}