]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | //! The compiler code necessary for `#[derive(RustcDecodable)]`. See encodable.rs for more. |
2 | ||
3 | use crate::deriving::generic::ty::*; | |
4 | use crate::deriving::generic::*; | |
5 | use crate::deriving::pathvec_std; | |
6 | ||
74b04a01 | 7 | use rustc_ast::ptr::P; |
3dfed10e | 8 | use rustc_ast::{self as ast, Expr, MetaItem, Mutability}; |
dfeec247 | 9 | use rustc_expand::base::{Annotatable, ExtCtxt}; |
3dfed10e | 10 | use rustc_span::symbol::{sym, Ident, Symbol}; |
dfeec247 | 11 | use rustc_span::Span; |
dfeec247 XL |
12 | |
13 | pub fn expand_deriving_rustc_decodable( | |
14 | cx: &mut ExtCtxt<'_>, | |
15 | span: Span, | |
16 | mitem: &MetaItem, | |
17 | item: &Annotatable, | |
18 | push: &mut dyn FnMut(Annotatable), | |
487cf647 | 19 | is_const: bool, |
dfeec247 | 20 | ) { |
3dfed10e XL |
21 | let krate = sym::rustc_serialize; |
22 | let typaram = sym::__D; | |
dfeec247 XL |
23 | |
24 | let trait_def = TraitDef { | |
25 | span, | |
064997fb | 26 | path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global), |
2b03887a | 27 | skip_path_as_bound: false, |
dfeec247 | 28 | additional_bounds: Vec::new(), |
dfeec247 XL |
29 | supports_unions: false, |
30 | methods: vec![MethodDef { | |
3dfed10e XL |
31 | name: sym::decode, |
32 | generics: Bounds { | |
dfeec247 XL |
33 | bounds: vec![( |
34 | typaram, | |
064997fb | 35 | vec![Path::new_(vec![krate, sym::Decoder], vec![], PathKind::Global)], |
dfeec247 XL |
36 | )], |
37 | }, | |
064997fb FG |
38 | explicit_self: false, |
39 | nonself_args: vec![( | |
40 | Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), | |
3dfed10e | 41 | sym::d, |
dfeec247 | 42 | )], |
064997fb | 43 | ret_ty: Path(Path::new_( |
3dfed10e | 44 | pathvec_std!(result::Result), |
dfeec247 XL |
45 | vec![ |
46 | Box::new(Self_), | |
064997fb | 47 | Box::new(Path(Path::new_(vec![typaram, sym::Error], vec![], PathKind::Local))), |
dfeec247 XL |
48 | ], |
49 | PathKind::Std, | |
50 | )), | |
f2b60f7d | 51 | attributes: ast::AttrVec::new(), |
9c376795 | 52 | fieldless_variants_strategy: FieldlessVariantsStrategy::Default, |
dfeec247 XL |
53 | combine_substructure: combine_substructure(Box::new(|a, b, c| { |
54 | decodable_substructure(a, b, c, krate) | |
55 | })), | |
56 | }], | |
57 | associated_types: Vec::new(), | |
487cf647 | 58 | is_const, |
dfeec247 XL |
59 | }; |
60 | ||
61 | trait_def.expand(cx, mitem, item, push) | |
62 | } | |
63 | ||
64 | fn decodable_substructure( | |
65 | cx: &mut ExtCtxt<'_>, | |
66 | trait_span: Span, | |
67 | substr: &Substructure<'_>, | |
3dfed10e | 68 | krate: Symbol, |
064997fb FG |
69 | ) -> BlockOrExpr { |
70 | let decoder = substr.nonselflike_args[0].clone(); | |
dfeec247 | 71 | let recurse = vec![ |
3dfed10e XL |
72 | Ident::new(krate, trait_span), |
73 | Ident::new(sym::Decodable, trait_span), | |
74 | Ident::new(sym::decode, trait_span), | |
dfeec247 XL |
75 | ]; |
76 | let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse)); | |
77 | // throw an underscore in front to suppress unused variable warnings | |
3dfed10e | 78 | let blkarg = Ident::new(sym::_d, trait_span); |
dfeec247 XL |
79 | let blkdecoder = cx.expr_ident(trait_span, blkarg); |
80 | ||
487cf647 FG |
81 | let expr = match substr.fields { |
82 | StaticStruct(_, summary) => { | |
83 | let nfields = match summary { | |
84 | Unnamed(fields, _) => fields.len(), | |
85 | Named(fields) => fields.len(), | |
dfeec247 | 86 | }; |
6a06907d XL |
87 | let fn_read_struct_field_path: Vec<_> = |
88 | cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_struct_field]); | |
dfeec247 XL |
89 | |
90 | let path = cx.path_ident(trait_span, substr.type_ident); | |
91 | let result = | |
92 | decode_static_fields(cx, trait_span, path, summary, |cx, span, name, field| { | |
93 | cx.expr_try( | |
94 | span, | |
6a06907d | 95 | cx.expr_call_global( |
dfeec247 | 96 | span, |
6a06907d | 97 | fn_read_struct_field_path.clone(), |
dfeec247 | 98 | vec![ |
6a06907d | 99 | blkdecoder.clone(), |
dfeec247 XL |
100 | cx.expr_str(span, name), |
101 | cx.expr_usize(span, field), | |
102 | exprdecode.clone(), | |
103 | ], | |
104 | ), | |
105 | ) | |
106 | }); | |
107 | let result = cx.expr_ok(trait_span, result); | |
6a06907d XL |
108 | let fn_read_struct_path: Vec<_> = |
109 | cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_struct]); | |
110 | ||
111 | cx.expr_call_global( | |
dfeec247 | 112 | trait_span, |
6a06907d | 113 | fn_read_struct_path, |
dfeec247 | 114 | vec![ |
6a06907d | 115 | decoder, |
dfeec247 XL |
116 | cx.expr_str(trait_span, substr.type_ident.name), |
117 | cx.expr_usize(trait_span, nfields), | |
118 | cx.lambda1(trait_span, result, blkarg), | |
119 | ], | |
120 | ) | |
121 | } | |
487cf647 | 122 | StaticEnum(_, fields) => { |
3dfed10e | 123 | let variant = Ident::new(sym::i, trait_span); |
dfeec247 XL |
124 | |
125 | let mut arms = Vec::with_capacity(fields.len() + 1); | |
126 | let mut variants = Vec::with_capacity(fields.len()); | |
6a06907d XL |
127 | |
128 | let fn_read_enum_variant_arg_path: Vec<_> = | |
129 | cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant_arg]); | |
dfeec247 XL |
130 | |
131 | for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() { | |
132 | variants.push(cx.expr_str(v_span, ident.name)); | |
133 | ||
134 | let path = cx.path(trait_span, vec![substr.type_ident, ident]); | |
135 | let decoded = | |
136 | decode_static_fields(cx, v_span, path, parts, |cx, span, _, field| { | |
137 | let idx = cx.expr_usize(span, field); | |
138 | cx.expr_try( | |
139 | span, | |
6a06907d | 140 | cx.expr_call_global( |
dfeec247 | 141 | span, |
6a06907d XL |
142 | fn_read_enum_variant_arg_path.clone(), |
143 | vec![blkdecoder.clone(), idx, exprdecode.clone()], | |
dfeec247 XL |
144 | ), |
145 | ) | |
146 | }); | |
147 | ||
148 | arms.push(cx.arm(v_span, cx.pat_lit(v_span, cx.expr_usize(v_span, i)), decoded)); | |
149 | } | |
150 | ||
151 | arms.push(cx.arm_unreachable(trait_span)); | |
152 | ||
153 | let result = cx.expr_ok( | |
154 | trait_span, | |
155 | cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms), | |
156 | ); | |
157 | let lambda = cx.lambda(trait_span, vec![blkarg, variant], result); | |
064997fb | 158 | let variant_array_ref = cx.expr_array_ref(trait_span, variants); |
6a06907d XL |
159 | let fn_read_enum_variant_path: Vec<_> = |
160 | cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]); | |
161 | let result = cx.expr_call_global( | |
dfeec247 | 162 | trait_span, |
6a06907d | 163 | fn_read_enum_variant_path, |
064997fb | 164 | vec![blkdecoder, variant_array_ref, lambda], |
dfeec247 | 165 | ); |
6a06907d XL |
166 | let fn_read_enum_path: Vec<_> = |
167 | cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum]); | |
168 | ||
169 | cx.expr_call_global( | |
dfeec247 | 170 | trait_span, |
6a06907d | 171 | fn_read_enum_path, |
dfeec247 | 172 | vec![ |
6a06907d | 173 | decoder, |
dfeec247 XL |
174 | cx.expr_str(trait_span, substr.type_ident.name), |
175 | cx.lambda1(trait_span, result, blkarg), | |
176 | ], | |
177 | ) | |
178 | } | |
179 | _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)"), | |
064997fb FG |
180 | }; |
181 | BlockOrExpr::new_expr(expr) | |
dfeec247 XL |
182 | } |
183 | ||
184 | /// Creates a decoder for a single enum variant/struct: | |
185 | /// - `outer_pat_path` is the path to this enum variant/struct | |
186 | /// - `getarg` should retrieve the `usize`-th field with name `@str`. | |
187 | fn decode_static_fields<F>( | |
188 | cx: &mut ExtCtxt<'_>, | |
189 | trait_span: Span, | |
190 | outer_pat_path: ast::Path, | |
191 | fields: &StaticFields, | |
192 | mut getarg: F, | |
193 | ) -> P<Expr> | |
194 | where | |
195 | F: FnMut(&mut ExtCtxt<'_>, Span, Symbol, usize) -> P<Expr>, | |
196 | { | |
487cf647 FG |
197 | match fields { |
198 | Unnamed(fields, is_tuple) => { | |
dfeec247 | 199 | let path_expr = cx.expr_path(outer_pat_path); |
487cf647 | 200 | if !*is_tuple { |
dfeec247 XL |
201 | path_expr |
202 | } else { | |
203 | let fields = fields | |
204 | .iter() | |
205 | .enumerate() | |
206 | .map(|(i, &span)| getarg(cx, span, Symbol::intern(&format!("_field{}", i)), i)) | |
207 | .collect(); | |
208 | ||
209 | cx.expr_call(trait_span, path_expr, fields) | |
210 | } | |
211 | } | |
487cf647 | 212 | Named(fields) => { |
dfeec247 XL |
213 | // use the field's span to get nicer error messages. |
214 | let fields = fields | |
215 | .iter() | |
216 | .enumerate() | |
217 | .map(|(i, &(ident, span))| { | |
218 | let arg = getarg(cx, span, ident.name, i); | |
219 | cx.field_imm(span, ident, arg) | |
220 | }) | |
221 | .collect(); | |
222 | cx.expr_struct(trait_span, outer_pat_path, fields) | |
223 | } | |
224 | } | |
225 | } |