1 use crate::internals::ast::{Container, Data, Field, Style, Variant};
2 use proc_macro2::TokenStream;
3 use 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 //
pretend_used(cont: &Container, is_packed: bool) -> TokenStream22 pub 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 //
pretend_fields_used(cont: &Container, is_packed: bool) -> TokenStream64 fn 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 
pretend_fields_used_struct(cont: &Container, fields: &[Field]) -> TokenStream78 fn 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 
pretend_fields_used_struct_packed(cont: &Container, fields: &[Field]) -> TokenStream93 fn 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 
pretend_fields_used_enum(cont: &Container, variants: &[Variant]) -> TokenStream111 fn 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 //
pretend_variants_used(cont: &Container) -> TokenStream147 fn 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 }
186