]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_macros/src/serialize.rs
Auto merge of #97367 - WaffleLapkin:stabilize_checked_slice_to_str_conv, r=dtolnay
[rust.git] / compiler / rustc_macros / src / serialize.rs
1 use proc_macro2::TokenStream;
2 use quote::{quote, quote_spanned};
3 use syn::parse_quote;
4 use syn::spanned::Spanned;
5
6 pub fn type_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
7     let decoder_ty = quote! { __D };
8     if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
9         s.add_impl_generic(parse_quote! { 'tcx });
10     }
11     s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder<I = ::rustc_middle::ty::TyCtxt<'tcx>>});
12     s.add_bounds(synstructure::AddBounds::Generics);
13
14     decodable_body(s, decoder_ty)
15 }
16
17 pub fn meta_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
18     if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
19         s.add_impl_generic(parse_quote! { 'tcx });
20     }
21     s.add_impl_generic(parse_quote! { '__a });
22     let decoder_ty = quote! { DecodeContext<'__a, 'tcx> };
23     s.add_bounds(synstructure::AddBounds::Generics);
24
25     decodable_body(s, decoder_ty)
26 }
27
28 pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
29     let decoder_ty = quote! { __D };
30     s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder});
31     s.add_bounds(synstructure::AddBounds::Generics);
32
33     decodable_body(s, decoder_ty)
34 }
35
36 fn decodable_body(
37     s: synstructure::Structure<'_>,
38     decoder_ty: TokenStream,
39 ) -> proc_macro2::TokenStream {
40     if let syn::Data::Union(_) = s.ast().data {
41         panic!("cannot derive on union")
42     }
43     let ty_name = s.ast().ident.to_string();
44     let decode_body = match s.variants() {
45         [vi] => vi.construct(|field, _index| decode_field(field)),
46         variants => {
47             let match_inner: TokenStream = variants
48                 .iter()
49                 .enumerate()
50                 .map(|(idx, vi)| {
51                     let construct = vi.construct(|field, _index| decode_field(field));
52                     quote! { #idx => { #construct } }
53                 })
54                 .collect();
55             let message = format!(
56                 "invalid enum variant tag while decoding `{}`, expected 0..{}",
57                 ty_name,
58                 variants.len()
59             );
60             quote! {
61                 match ::rustc_serialize::Decoder::read_usize(__decoder) {
62                     #match_inner
63                     _ => panic!(#message),
64                 }
65             }
66         }
67     };
68
69     s.bound_impl(
70         quote!(::rustc_serialize::Decodable<#decoder_ty>),
71         quote! {
72             fn decode(__decoder: &mut #decoder_ty) -> Self {
73                 #decode_body
74             }
75         },
76     )
77 }
78
79 fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream {
80     let field_span = field.ident.as_ref().map_or(field.ty.span(), |ident| ident.span());
81
82     let decode_inner_method = if let syn::Type::Reference(_) = field.ty {
83         quote! { ::rustc_middle::ty::codec::RefDecodable::decode }
84     } else {
85         quote! { ::rustc_serialize::Decodable::decode }
86     };
87     let __decoder = quote! { __decoder };
88     // Use the span of the field for the method call, so
89     // that backtraces will point to the field.
90     quote_spanned! {field_span=> #decode_inner_method(#__decoder) }
91 }
92
93 pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
94     if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
95         s.add_impl_generic(parse_quote! {'tcx});
96     }
97     let encoder_ty = quote! { __E };
98     s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder<I = ::rustc_middle::ty::TyCtxt<'tcx>>});
99     s.add_bounds(synstructure::AddBounds::Generics);
100
101     encodable_body(s, encoder_ty, false)
102 }
103
104 pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
105     if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
106         s.add_impl_generic(parse_quote! {'tcx});
107     }
108     s.add_impl_generic(parse_quote! { '__a });
109     let encoder_ty = quote! { EncodeContext<'__a, 'tcx> };
110     s.add_bounds(synstructure::AddBounds::Generics);
111
112     encodable_body(s, encoder_ty, true)
113 }
114
115 pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
116     let encoder_ty = quote! { __E };
117     s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder});
118     s.add_bounds(synstructure::AddBounds::Generics);
119
120     encodable_body(s, encoder_ty, false)
121 }
122
123 fn encodable_body(
124     mut s: synstructure::Structure<'_>,
125     encoder_ty: TokenStream,
126     allow_unreachable_code: bool,
127 ) -> proc_macro2::TokenStream {
128     if let syn::Data::Union(_) = s.ast().data {
129         panic!("cannot derive on union")
130     }
131
132     s.bind_with(|binding| {
133         // Handle the lack of a blanket reference impl.
134         if let syn::Type::Reference(_) = binding.ast().ty {
135             synstructure::BindStyle::Move
136         } else {
137             synstructure::BindStyle::Ref
138         }
139     });
140
141     let encode_body = match s.variants() {
142         [_] => {
143             let encode_inner = s.each_variant(|vi| {
144                 vi.bindings()
145                     .iter()
146                     .map(|binding| {
147                         let bind_ident = &binding.binding;
148                         let result = quote! {
149                             ::rustc_serialize::Encodable::<#encoder_ty>::encode(
150                                 #bind_ident,
151                                 __encoder,
152                             );
153                         };
154                         result
155                     })
156                     .collect::<TokenStream>()
157             });
158             quote! {
159                 match *self { #encode_inner }
160             }
161         }
162         _ => {
163             let mut variant_idx = 0usize;
164             let encode_inner = s.each_variant(|vi| {
165                 let encode_fields: TokenStream = vi
166                     .bindings()
167                     .iter()
168                     .map(|binding| {
169                         let bind_ident = &binding.binding;
170                         let result = quote! {
171                             ::rustc_serialize::Encodable::<#encoder_ty>::encode(
172                                 #bind_ident,
173                                 __encoder,
174                             );
175                         };
176                         result
177                     })
178                     .collect();
179
180                 let result = if !vi.bindings().is_empty() {
181                     quote! {
182                         ::rustc_serialize::Encoder::emit_enum_variant(
183                             __encoder,
184                             #variant_idx,
185                             |__encoder| { #encode_fields }
186                         )
187                     }
188                 } else {
189                     quote! {
190                         ::rustc_serialize::Encoder::emit_fieldless_enum_variant::<#variant_idx>(
191                             __encoder,
192                         )
193                     }
194                 };
195                 variant_idx += 1;
196                 result
197             });
198             quote! {
199                 match *self {
200                     #encode_inner
201                 }
202             }
203         }
204     };
205
206     let lints = if allow_unreachable_code {
207         quote! { #![allow(unreachable_code)] }
208     } else {
209         quote! {}
210     };
211
212     s.bound_impl(
213         quote!(::rustc_serialize::Encodable<#encoder_ty>),
214         quote! {
215             fn encode(
216                 &self,
217                 __encoder: &mut #encoder_ty,
218             ) {
219                 #lints
220                 #encode_body
221             }
222         },
223     )
224 }