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, 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(_, _) => 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 => {
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 _ => 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}
186