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