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