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} 186