]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/encodable.rs
Rollup merge of #34436 - jseyfried:no_block_expr, r=eddyb
[rust.git] / src / libsyntax_ext / deriving / encodable.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 to implement the `#[derive(Encodable)]`
12 //! (and `Decodable`, in decodable.rs) extension.  The idea here is that
13 //! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`.
14 //!
15 //! For example, a type like:
16 //!
17 //! ```ignore
18 //! #[derive(Encodable, Decodable)]
19 //! struct Node { id: usize }
20 //! ```
21 //!
22 //! would generate two implementations like:
23 //!
24 //! ```ignore
25 //! impl<S: Encoder<E>, E> Encodable<S, E> for Node {
26 //!     fn encode(&self, s: &mut S) -> Result<(), E> {
27 //!         s.emit_struct("Node", 1, |this| {
28 //!             this.emit_struct_field("id", 0, |this| {
29 //!                 Encodable::encode(&self.id, this)
30 //!                 /* this.emit_usize(self.id) can also be used */
31 //!             })
32 //!         })
33 //!     }
34 //! }
35 //!
36 //! impl<D: Decoder<E>, E> Decodable<D, E> for Node {
37 //!     fn decode(d: &mut D) -> Result<Node, E> {
38 //!         d.read_struct("Node", 1, |this| {
39 //!             match this.read_struct_field("id", 0, |this| Decodable::decode(this)) {
40 //!                 Ok(id) => Ok(Node { id: id }),
41 //!                 Err(e) => Err(e),
42 //!             }
43 //!         })
44 //!     }
45 //! }
46 //! ```
47 //!
48 //! Other interesting scenarios are when the item has type parameters or
49 //! references other non-built-in types.  A type definition like:
50 //!
51 //! ```ignore
52 //! #[derive(Encodable, Decodable)]
53 //! struct Spanned<T> { node: T, span: Span }
54 //! ```
55 //!
56 //! would yield functions like:
57 //!
58 //! ```ignore
59 //! impl<
60 //!     S: Encoder<E>,
61 //!     E,
62 //!     T: Encodable<S, E>
63 //! > Encodable<S, E> for Spanned<T> {
64 //!     fn encode(&self, s: &mut S) -> Result<(), E> {
65 //!         s.emit_struct("Spanned", 2, |this| {
66 //!             this.emit_struct_field("node", 0, |this| self.node.encode(this))
67 //!                 .unwrap();
68 //!             this.emit_struct_field("span", 1, |this| self.span.encode(this))
69 //!         })
70 //!     }
71 //! }
72 //!
73 //! impl<
74 //!     D: Decoder<E>,
75 //!     E,
76 //!     T: Decodable<D, E>
77 //! > Decodable<D, E> for Spanned<T> {
78 //!     fn decode(d: &mut D) -> Result<Spanned<T>, E> {
79 //!         d.read_struct("Spanned", 2, |this| {
80 //!             Ok(Spanned {
81 //!                 node: this.read_struct_field("node", 0, |this| Decodable::decode(this))
82 //!                     .unwrap(),
83 //!                 span: this.read_struct_field("span", 1, |this| Decodable::decode(this))
84 //!                     .unwrap(),
85 //!             })
86 //!         })
87 //!     }
88 //! }
89 //! ```
90
91 use deriving;
92 use deriving::generic::*;
93 use deriving::generic::ty::*;
94
95 use syntax::ast::{MetaItem, Expr, ExprKind, Mutability};
96 use syntax::ext::base::{ExtCtxt,Annotatable};
97 use syntax::ext::build::AstBuilder;
98 use syntax::parse::token;
99 use syntax::ptr::P;
100 use syntax_pos::Span;
101
102 pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt,
103                                        span: Span,
104                                        mitem: &MetaItem,
105                                        item: &Annotatable,
106                                        push: &mut FnMut(Annotatable))
107 {
108     expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize")
109 }
110
111 pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
112                                  span: Span,
113                                  mitem: &MetaItem,
114                                  item: &Annotatable,
115                                  push: &mut FnMut(Annotatable))
116 {
117     expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
118 }
119
120 fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
121                                  span: Span,
122                                  mitem: &MetaItem,
123                                  item: &Annotatable,
124                                  push: &mut FnMut(Annotatable),
125                                  krate: &'static str)
126 {
127     if cx.crate_root != Some("std") {
128         // FIXME(#21880): lift this requirement.
129         cx.span_err(span, "this trait cannot be derived with #![no_std] \
130                            or #![no_core]");
131         return;
132     }
133
134     let typaram = &*deriving::hygienic_type_parameter(item, "__S");
135
136     let trait_def = TraitDef {
137         span: span,
138         attributes: Vec::new(),
139         path: Path::new_(vec!(krate, "Encodable"), None, vec!(), true),
140         additional_bounds: Vec::new(),
141         generics: LifetimeBounds::empty(),
142         is_unsafe: false,
143         methods: vec!(
144             MethodDef {
145                 name: "encode",
146                 generics: LifetimeBounds {
147                     lifetimes: Vec::new(),
148                     bounds: vec![(typaram,
149                                   vec![Path::new_(vec![krate, "Encoder"], None, vec!(), true)])]
150                 },
151                 explicit_self: borrowed_explicit_self(),
152                 args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))),
153                            Borrowed(None, Mutability::Mutable))),
154                 ret_ty: Literal(Path::new_(
155                     pathvec_std!(cx, core::result::Result),
156                     None,
157                     vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
158                         vec![typaram, "Error"], None, vec![], false
159                     )))),
160                     true
161                 )),
162                 attributes: Vec::new(),
163                 is_unsafe: false,
164                 unify_fieldless_variants: false,
165                 combine_substructure: combine_substructure(Box::new(|a, b, c| {
166                     encodable_substructure(a, b, c, krate)
167                 })),
168             }
169         ),
170         associated_types: Vec::new(),
171     };
172
173     trait_def.expand(cx, mitem, item, push)
174 }
175
176 fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
177                           substr: &Substructure, krate: &'static str) -> P<Expr> {
178     let encoder = substr.nonself_args[0].clone();
179     // throw an underscore in front to suppress unused variable warnings
180     let blkarg = cx.ident_of("_e");
181     let blkencoder = cx.expr_ident(trait_span, blkarg);
182     let fn_path = cx.expr_path(cx.path_global(trait_span, vec![cx.ident_of(krate),
183                                                                cx.ident_of("Encodable"),
184                                                                cx.ident_of("encode")]));
185
186     return match *substr.fields {
187         Struct(_, ref fields) => {
188             let emit_struct_field = cx.ident_of("emit_struct_field");
189             let mut stmts = Vec::new();
190             for (i, &FieldInfo {
191                     name,
192                     ref self_,
193                     span,
194                     ..
195                 }) in fields.iter().enumerate() {
196                 let name = match name {
197                     Some(id) => id.name.as_str(),
198                     None => {
199                         token::intern_and_get_ident(&format!("_field{}", i))
200                     }
201                 };
202                 let self_ref = cx.expr_addr_of(span, self_.clone());
203                 let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]);
204                 let lambda = cx.lambda_expr_1(span, enc, blkarg);
205                 let call = cx.expr_method_call(span, blkencoder.clone(),
206                                                emit_struct_field,
207                                                vec!(cx.expr_str(span, name),
208                                                  cx.expr_usize(span, i),
209                                                  lambda));
210
211                 // last call doesn't need a try!
212                 let last = fields.len() - 1;
213                 let call = if i != last {
214                     cx.expr_try(span, call)
215                 } else {
216                     cx.expr(span, ExprKind::Ret(Some(call)))
217                 };
218                 stmts.push(cx.stmt_expr(call));
219             }
220
221             // unit structs have no fields and need to return Ok()
222             if stmts.is_empty() {
223                 let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![]));
224                 let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok)));
225                 stmts.push(cx.stmt_expr(ret_ok));
226             }
227
228             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
229             cx.expr_method_call(trait_span,
230                                 encoder,
231                                 cx.ident_of("emit_struct"),
232                                 vec!(
233                 cx.expr_str(trait_span, substr.type_ident.name.as_str()),
234                 cx.expr_usize(trait_span, fields.len()),
235                 blk
236             ))
237         }
238
239         EnumMatching(idx, variant, ref fields) => {
240             // We're not generating an AST that the borrow checker is expecting,
241             // so we need to generate a unique local variable to take the
242             // mutable loan out on, otherwise we get conflicts which don't
243             // actually exist.
244             let me = cx.stmt_let(trait_span, false, blkarg, encoder);
245             let encoder = cx.expr_ident(trait_span, blkarg);
246             let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
247             let mut stmts = Vec::new();
248             if !fields.is_empty() {
249                 let last = fields.len() - 1;
250                 for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() {
251                 let self_ref = cx.expr_addr_of(span, self_.clone());
252                     let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref,
253                                                                        blkencoder.clone()]);
254                     let lambda = cx.lambda_expr_1(span, enc, blkarg);
255                     let call = cx.expr_method_call(span, blkencoder.clone(),
256                                                    emit_variant_arg,
257                                                    vec!(cx.expr_usize(span, i),
258                                                         lambda));
259                     let call = if i != last {
260                         cx.expr_try(span, call)
261                     } else {
262                         cx.expr(span, ExprKind::Ret(Some(call)))
263                     };
264                     stmts.push(cx.stmt_expr(call));
265                 }
266             } else {
267                 let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![]));
268                 let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok)));
269                 stmts.push(cx.stmt_expr(ret_ok));
270             }
271
272             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
273             let name = cx.expr_str(trait_span, variant.node.name.name.as_str());
274             let call = cx.expr_method_call(trait_span, blkencoder,
275                                            cx.ident_of("emit_enum_variant"),
276                                            vec!(name,
277                                              cx.expr_usize(trait_span, idx),
278                                              cx.expr_usize(trait_span, fields.len()),
279                                              blk));
280             let blk = cx.lambda_expr_1(trait_span, call, blkarg);
281             let ret = cx.expr_method_call(trait_span,
282                                           encoder,
283                                           cx.ident_of("emit_enum"),
284                                           vec!(
285                 cx.expr_str(trait_span, substr.type_ident.name.as_str()),
286                 blk
287             ));
288             cx.expr_block(cx.block(trait_span, vec![me, cx.stmt_expr(ret)]))
289         }
290
291         _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)")
292     };
293 }