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