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