rustc_macros/
serialize.rs

1use proc_macro2::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::parse_quote;
4use syn::spanned::Spanned;
5
6pub(super) fn type_decodable_derive(
7    mut s: synstructure::Structure<'_>,
8) -> proc_macro2::TokenStream {
9    let decoder_ty = quote! { __D };
10    let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
11        quote! { <I = ::rustc_middle::ty::TyCtxt<'tcx>> }
12    } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") {
13        quote! { <I = I> }
14    } else {
15        quote! {}
16    };
17
18    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound });
19    s.add_bounds(synstructure::AddBounds::Fields);
20    s.underscore_const(true);
21
22    decodable_body(s, decoder_ty)
23}
24
25pub(super) fn meta_decodable_derive(
26    mut s: synstructure::Structure<'_>,
27) -> proc_macro2::TokenStream {
28    if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
29        s.add_impl_generic(parse_quote! { 'tcx });
30    }
31    s.add_impl_generic(parse_quote! { '__a });
32    let decoder_ty = quote! { DecodeContext<'__a, 'tcx> };
33    s.add_bounds(synstructure::AddBounds::Generics);
34    s.underscore_const(true);
35
36    decodable_body(s, decoder_ty)
37}
38
39pub(super) fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
40    let decoder_ty = quote! { __D };
41    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_span::SpanDecoder });
42    s.add_bounds(synstructure::AddBounds::Generics);
43    s.underscore_const(true);
44
45    decodable_body(s, decoder_ty)
46}
47
48pub(super) fn decodable_generic_derive(
49    mut s: synstructure::Structure<'_>,
50) -> proc_macro2::TokenStream {
51    let decoder_ty = quote! { __D };
52    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_serialize::Decoder });
53    s.add_bounds(synstructure::AddBounds::Generics);
54    s.underscore_const(true);
55
56    decodable_body(s, decoder_ty)
57}
58
59fn decodable_body(
60    mut s: synstructure::Structure<'_>,
61    decoder_ty: TokenStream,
62) -> proc_macro2::TokenStream {
63    if let syn::Data::Union(_) = s.ast().data {
64        panic!("cannot derive on union")
65    }
66    let ty_name = s.ast().ident.to_string();
67    let decode_body = match s.variants() {
68        [] => {
69            let message = format!("`{ty_name}` has no variants to decode");
70            quote! {
71                panic!(#message)
72            }
73        }
74        [vi] => vi.construct(|field, _index| decode_field(field)),
75        variants => {
76            let match_inner: TokenStream = variants
77                .iter()
78                .enumerate()
79                .map(|(idx, vi)| {
80                    let construct = vi.construct(|field, _index| decode_field(field));
81                    quote! { #idx => { #construct } }
82                })
83                .collect();
84            let message = format!(
85                "invalid enum variant tag while decoding `{}`, expected 0..{}, actual {{}}",
86                ty_name,
87                variants.len()
88            );
89            let tag = if variants.len() < u8::MAX as usize {
90                quote! {
91                    ::rustc_serialize::Decoder::read_u8(__decoder) as usize
92                }
93            } else {
94                quote! {
95                    ::rustc_serialize::Decoder::read_usize(__decoder)
96                }
97            };
98            quote! {
99                match #tag {
100                    #match_inner
101                    n => panic!(#message, n),
102                }
103            }
104        }
105    };
106    s.underscore_const(true);
107
108    s.bound_impl(
109        quote!(::rustc_serialize::Decodable<#decoder_ty>),
110        quote! {
111            fn decode(__decoder: &mut #decoder_ty) -> Self {
112                #decode_body
113            }
114        },
115    )
116}
117
118fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream {
119    let field_span = field.ident.as_ref().map_or(field.ty.span(), |ident| ident.span());
120
121    let decode_inner_method = if let syn::Type::Reference(_) = field.ty {
122        quote! { ::rustc_middle::ty::codec::RefDecodable::decode }
123    } else {
124        quote! { ::rustc_serialize::Decodable::decode }
125    };
126    let __decoder = quote! { __decoder };
127    // Use the span of the field for the method call, so
128    // that backtraces will point to the field.
129    quote_spanned! { field_span=> #decode_inner_method(#__decoder) }
130}
131
132pub(super) fn type_encodable_derive(
133    mut s: synstructure::Structure<'_>,
134) -> proc_macro2::TokenStream {
135    let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
136        quote! { <I = ::rustc_middle::ty::TyCtxt<'tcx>> }
137    } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") {
138        quote! { <I = I> }
139    } else {
140        quote! {}
141    };
142
143    let encoder_ty = quote! { __E };
144    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound });
145    s.add_bounds(synstructure::AddBounds::Fields);
146    s.underscore_const(true);
147
148    encodable_body(s, encoder_ty, false)
149}
150
151pub(super) fn meta_encodable_derive(
152    mut s: synstructure::Structure<'_>,
153) -> proc_macro2::TokenStream {
154    if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
155        s.add_impl_generic(parse_quote! { 'tcx });
156    }
157    s.add_impl_generic(parse_quote! { '__a });
158    let encoder_ty = quote! { EncodeContext<'__a, 'tcx> };
159    s.add_bounds(synstructure::AddBounds::Generics);
160    s.underscore_const(true);
161
162    encodable_body(s, encoder_ty, true)
163}
164
165pub(super) fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
166    let encoder_ty = quote! { __E };
167    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder });
168    s.add_bounds(synstructure::AddBounds::Generics);
169    s.underscore_const(true);
170
171    encodable_body(s, encoder_ty, false)
172}
173
174pub(super) fn encodable_generic_derive(
175    mut s: synstructure::Structure<'_>,
176) -> proc_macro2::TokenStream {
177    let encoder_ty = quote! { __E };
178    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder });
179    s.add_bounds(synstructure::AddBounds::Generics);
180    s.underscore_const(true);
181
182    encodable_body(s, encoder_ty, false)
183}
184
185fn encodable_body(
186    mut s: synstructure::Structure<'_>,
187    encoder_ty: TokenStream,
188    allow_unreachable_code: bool,
189) -> proc_macro2::TokenStream {
190    if let syn::Data::Union(_) = s.ast().data {
191        panic!("cannot derive on union")
192    }
193
194    s.underscore_const(true);
195    s.bind_with(|binding| {
196        // Handle the lack of a blanket reference impl.
197        if let syn::Type::Reference(_) = binding.ast().ty {
198            synstructure::BindStyle::Move
199        } else {
200            synstructure::BindStyle::Ref
201        }
202    });
203
204    let encode_body = match s.variants() {
205        [] => {
206            quote! {
207                match *self {}
208            }
209        }
210        [_] => {
211            let encode_inner = s.each_variant(|vi| {
212                vi.bindings()
213                    .iter()
214                    .map(|binding| {
215                        let bind_ident = &binding.binding;
216                        let result = quote! {
217                            ::rustc_serialize::Encodable::<#encoder_ty>::encode(
218                                #bind_ident,
219                                __encoder,
220                            );
221                        };
222                        result
223                    })
224                    .collect::<TokenStream>()
225            });
226            quote! {
227                match *self { #encode_inner }
228            }
229        }
230        _ => {
231            let disc = {
232                let mut variant_idx = 0usize;
233                let encode_inner = s.each_variant(|_| {
234                    let result = quote! {
235                        #variant_idx
236                    };
237                    variant_idx += 1;
238                    result
239                });
240                if variant_idx < u8::MAX as usize {
241                    quote! {
242                        let disc = match *self {
243                            #encode_inner
244                        };
245                        ::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
246                    }
247                } else {
248                    quote! {
249                        let disc = match *self {
250                            #encode_inner
251                        };
252                        ::rustc_serialize::Encoder::emit_usize(__encoder, disc);
253                    }
254                }
255            };
256
257            let mut variant_idx = 0usize;
258            let encode_inner = s.each_variant(|vi| {
259                let encode_fields: TokenStream = vi
260                    .bindings()
261                    .iter()
262                    .map(|binding| {
263                        let bind_ident = &binding.binding;
264                        let result = quote! {
265                            ::rustc_serialize::Encodable::<#encoder_ty>::encode(
266                                #bind_ident,
267                                __encoder,
268                            );
269                        };
270                        result
271                    })
272                    .collect();
273                variant_idx += 1;
274                encode_fields
275            });
276            quote! {
277                #disc
278                match *self {
279                    #encode_inner
280                }
281            }
282        }
283    };
284
285    let lints = if allow_unreachable_code {
286        quote! { #![allow(unreachable_code)] }
287    } else {
288        quote! {}
289    };
290
291    s.bound_impl(
292        quote!(::rustc_serialize::Encodable<#encoder_ty>),
293        quote! {
294            fn encode(
295                &self,
296                __encoder: &mut #encoder_ty,
297            ) {
298                #lints
299                #encode_body
300            }
301        },
302    )
303}