]>
Commit | Line | Data |
---|---|---|
970d7e83 LB |
1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
1a4d82fc | 11 | //! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more. |
970d7e83 | 12 | |
54a0048b | 13 | use deriving; |
9cc50fc6 SL |
14 | use deriving::generic::*; |
15 | use deriving::generic::ty::*; | |
16 | ||
17 | use syntax::ast; | |
7453a54e | 18 | use syntax::ast::{MetaItem, Expr, Mutability}; |
9cc50fc6 SL |
19 | use syntax::codemap::Span; |
20 | use syntax::ext::base::{ExtCtxt, Annotatable}; | |
21 | use syntax::ext::build::AstBuilder; | |
22 | use syntax::parse::token::InternedString; | |
23 | use syntax::parse::token; | |
24 | use syntax::ptr::P; | |
1a4d82fc | 25 | |
d9579d0f AL |
26 | pub fn expand_deriving_rustc_decodable(cx: &mut ExtCtxt, |
27 | span: Span, | |
28 | mitem: &MetaItem, | |
62682a34 | 29 | item: &Annotatable, |
d9579d0f | 30 | push: &mut FnMut(Annotatable)) |
1a4d82fc JJ |
31 | { |
32 | expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize") | |
33 | } | |
34 | ||
d9579d0f AL |
35 | pub fn expand_deriving_decodable(cx: &mut ExtCtxt, |
36 | span: Span, | |
37 | mitem: &MetaItem, | |
62682a34 | 38 | item: &Annotatable, |
d9579d0f | 39 | push: &mut FnMut(Annotatable)) |
1a4d82fc JJ |
40 | { |
41 | expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize") | |
42 | } | |
970d7e83 | 43 | |
d9579d0f AL |
44 | fn expand_deriving_decodable_imp(cx: &mut ExtCtxt, |
45 | span: Span, | |
46 | mitem: &MetaItem, | |
62682a34 | 47 | item: &Annotatable, |
d9579d0f AL |
48 | push: &mut FnMut(Annotatable), |
49 | krate: &'static str) | |
1a4d82fc | 50 | { |
e9174d1e | 51 | if cx.crate_root != Some("std") { |
85aaf69f | 52 | // FIXME(#21880): lift this requirement. |
e9174d1e SL |
53 | cx.span_err(span, "this trait cannot be derived with #![no_std] \ |
54 | or #![no_core]"); | |
55 | return | |
85aaf69f SL |
56 | } |
57 | ||
54a0048b SL |
58 | let typaram = &*deriving::hygienic_type_parameter(item, "__D"); |
59 | ||
970d7e83 | 60 | let trait_def = TraitDef { |
1a4d82fc JJ |
61 | span: span, |
62 | attributes: Vec::new(), | |
63 | path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true), | |
64 | additional_bounds: Vec::new(), | |
65 | generics: LifetimeBounds::empty(), | |
e9174d1e | 66 | is_unsafe: false, |
1a4d82fc | 67 | methods: vec!( |
970d7e83 LB |
68 | MethodDef { |
69 | name: "decode", | |
1a4d82fc JJ |
70 | generics: LifetimeBounds { |
71 | lifetimes: Vec::new(), | |
54a0048b SL |
72 | bounds: vec![(typaram, |
73 | vec![Path::new_(vec!(krate, "Decoder"), None, vec!(), true)])] | |
1a4d82fc | 74 | }, |
970d7e83 | 75 | explicit_self: None, |
54a0048b | 76 | args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))), |
7453a54e | 77 | Borrowed(None, Mutability::Mutable))), |
1a4d82fc | 78 | ret_ty: Literal(Path::new_( |
85aaf69f | 79 | pathvec_std!(cx, core::result::Result), |
1a4d82fc | 80 | None, |
d9579d0f | 81 | vec!(Box::new(Self_), Box::new(Literal(Path::new_( |
54a0048b | 82 | vec![typaram, "Error"], None, vec![], false |
d9579d0f | 83 | )))), |
1a4d82fc JJ |
84 | true |
85 | )), | |
86 | attributes: Vec::new(), | |
62682a34 | 87 | is_unsafe: false, |
c34b1796 | 88 | combine_substructure: combine_substructure(Box::new(|a, b, c| { |
1a4d82fc | 89 | decodable_substructure(a, b, c, krate) |
c34b1796 | 90 | })), |
85aaf69f SL |
91 | } |
92 | ), | |
93 | associated_types: Vec::new(), | |
970d7e83 LB |
94 | }; |
95 | ||
62682a34 | 96 | trait_def.expand(cx, mitem, item, push) |
970d7e83 LB |
97 | } |
98 | ||
1a4d82fc JJ |
99 | fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span, |
100 | substr: &Substructure, | |
101 | krate: &str) -> P<Expr> { | |
102 | let decoder = substr.nonself_args[0].clone(); | |
103 | let recurse = vec!(cx.ident_of(krate), | |
970d7e83 | 104 | cx.ident_of("Decodable"), |
1a4d82fc | 105 | cx.ident_of("decode")); |
85aaf69f | 106 | let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse)); |
970d7e83 LB |
107 | // throw an underscore in front to suppress unused variable warnings |
108 | let blkarg = cx.ident_of("_d"); | |
1a4d82fc | 109 | let blkdecoder = cx.expr_ident(trait_span, blkarg); |
970d7e83 LB |
110 | |
111 | return match *substr.fields { | |
112 | StaticStruct(_, ref summary) => { | |
113 | let nfields = match *summary { | |
1a4d82fc JJ |
114 | Unnamed(ref fields) => fields.len(), |
115 | Named(ref fields) => fields.len() | |
970d7e83 LB |
116 | }; |
117 | let read_struct_field = cx.ident_of("read_struct_field"); | |
118 | ||
1a4d82fc JJ |
119 | let path = cx.path_ident(trait_span, substr.type_ident); |
120 | let result = decode_static_fields(cx, | |
121 | trait_span, | |
122 | path, | |
123 | summary, | |
124 | |cx, span, name, field| { | |
125 | cx.expr_try(span, | |
126 | cx.expr_method_call(span, blkdecoder.clone(), read_struct_field, | |
127 | vec!(cx.expr_str(span, name), | |
85aaf69f SL |
128 | cx.expr_usize(span, field), |
129 | exprdecode.clone()))) | |
1a4d82fc JJ |
130 | }); |
131 | let result = cx.expr_ok(trait_span, result); | |
132 | cx.expr_method_call(trait_span, | |
133 | decoder, | |
134 | cx.ident_of("read_struct"), | |
135 | vec!( | |
c1a9b12d | 136 | cx.expr_str(trait_span, substr.type_ident.name.as_str()), |
85aaf69f | 137 | cx.expr_usize(trait_span, nfields), |
1a4d82fc JJ |
138 | cx.lambda_expr_1(trait_span, result, blkarg) |
139 | )) | |
970d7e83 LB |
140 | } |
141 | StaticEnum(_, ref fields) => { | |
142 | let variant = cx.ident_of("i"); | |
143 | ||
1a4d82fc JJ |
144 | let mut arms = Vec::new(); |
145 | let mut variants = Vec::new(); | |
970d7e83 LB |
146 | let rvariant_arg = cx.ident_of("read_enum_variant_arg"); |
147 | ||
c1a9b12d SL |
148 | for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() { |
149 | variants.push(cx.expr_str(v_span, ident.name.as_str())); | |
1a4d82fc | 150 | |
c1a9b12d | 151 | let path = cx.path(trait_span, vec![substr.type_ident, ident]); |
1a4d82fc JJ |
152 | let decoded = decode_static_fields(cx, |
153 | v_span, | |
154 | path, | |
155 | parts, | |
156 | |cx, span, _, field| { | |
85aaf69f | 157 | let idx = cx.expr_usize(span, field); |
1a4d82fc JJ |
158 | cx.expr_try(span, |
159 | cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg, | |
85aaf69f | 160 | vec!(idx, exprdecode.clone()))) |
1a4d82fc JJ |
161 | }); |
162 | ||
163 | arms.push(cx.arm(v_span, | |
85aaf69f | 164 | vec!(cx.pat_lit(v_span, cx.expr_usize(v_span, i))), |
970d7e83 LB |
165 | decoded)); |
166 | } | |
167 | ||
1a4d82fc | 168 | arms.push(cx.arm_unreachable(trait_span)); |
970d7e83 | 169 | |
1a4d82fc JJ |
170 | let result = cx.expr_ok(trait_span, |
171 | cx.expr_match(trait_span, | |
172 | cx.expr_ident(trait_span, variant), arms)); | |
173 | let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result); | |
174 | let variant_vec = cx.expr_vec(trait_span, variants); | |
175 | let variant_vec = cx.expr_addr_of(trait_span, variant_vec); | |
176 | let result = cx.expr_method_call(trait_span, blkdecoder, | |
970d7e83 | 177 | cx.ident_of("read_enum_variant"), |
1a4d82fc JJ |
178 | vec!(variant_vec, lambda)); |
179 | cx.expr_method_call(trait_span, | |
180 | decoder, | |
181 | cx.ident_of("read_enum"), | |
182 | vec!( | |
c1a9b12d | 183 | cx.expr_str(trait_span, substr.type_ident.name.as_str()), |
1a4d82fc JJ |
184 | cx.lambda_expr_1(trait_span, result, blkarg) |
185 | )) | |
970d7e83 | 186 | } |
85aaf69f | 187 | _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)") |
970d7e83 LB |
188 | }; |
189 | } | |
1a4d82fc JJ |
190 | |
191 | /// Create a decoder for a single enum variant/struct: | |
192 | /// - `outer_pat_path` is the path to this enum variant/struct | |
85aaf69f | 193 | /// - `getarg` should retrieve the `usize`-th field with name `@str`. |
1a4d82fc JJ |
194 | fn decode_static_fields<F>(cx: &mut ExtCtxt, |
195 | trait_span: Span, | |
196 | outer_pat_path: ast::Path, | |
197 | fields: &StaticFields, | |
198 | mut getarg: F) | |
199 | -> P<Expr> where | |
85aaf69f | 200 | F: FnMut(&mut ExtCtxt, Span, InternedString, usize) -> P<Expr>, |
1a4d82fc JJ |
201 | { |
202 | match *fields { | |
203 | Unnamed(ref fields) => { | |
204 | let path_expr = cx.expr_path(outer_pat_path); | |
205 | if fields.is_empty() { | |
206 | path_expr | |
207 | } else { | |
208 | let fields = fields.iter().enumerate().map(|(i, &span)| { | |
209 | getarg(cx, span, | |
c34b1796 | 210 | token::intern_and_get_ident(&format!("_field{}", i)), |
1a4d82fc JJ |
211 | i) |
212 | }).collect(); | |
213 | ||
214 | cx.expr_call(trait_span, path_expr, fields) | |
215 | } | |
216 | } | |
217 | Named(ref fields) => { | |
218 | // use the field's span to get nicer error messages. | |
c1a9b12d SL |
219 | let fields = fields.iter().enumerate().map(|(i, &(ident, span))| { |
220 | let arg = getarg(cx, span, ident.name.as_str(), i); | |
221 | cx.field_imm(span, ident, arg) | |
1a4d82fc JJ |
222 | }).collect(); |
223 | cx.expr_struct(trait_span, outer_pat_path, fields) | |
224 | } | |
225 | } | |
226 | } |