]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/encodable.rs
Auto merge of #62468 - rust-lang:mutable-overloaded-operators, r=estebank
[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::ext::build::AstBuilder;
92 use syntax::ptr::P;
93 use syntax::symbol::Symbol;
94 use syntax_pos::Span;
95
96 pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt<'_>,
97                                        span: Span,
98                                        mitem: &MetaItem,
99                                        item: &Annotatable,
100                                        push: &mut dyn FnMut(Annotatable)) {
101     expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize")
102 }
103
104 pub fn expand_deriving_encodable(cx: &mut ExtCtxt<'_>,
105                                  span: Span,
106                                  mitem: &MetaItem,
107                                  item: &Annotatable,
108                                  push: &mut dyn FnMut(Annotatable)) {
109     expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
110 }
111
112 fn expand_deriving_encodable_imp(cx: &mut ExtCtxt<'_>,
113                                  span: Span,
114                                  mitem: &MetaItem,
115                                  item: &Annotatable,
116                                  push: &mut dyn FnMut(Annotatable),
117                                  krate: &'static str) {
118     let typaram = &*deriving::hygienic_type_parameter(item, "__S");
119
120     let trait_def = TraitDef {
121         span,
122         attributes: Vec::new(),
123         path: Path::new_(vec![krate, "Encodable"], None, vec![], PathKind::Global),
124         additional_bounds: Vec::new(),
125         generics: LifetimeBounds::empty(),
126         is_unsafe: false,
127         supports_unions: false,
128         methods: vec![
129             MethodDef {
130                 name: "encode",
131                 generics: LifetimeBounds {
132                     lifetimes: Vec::new(),
133                     bounds: vec![
134                         (typaram,
135                          vec![Path::new_(vec![krate, "Encoder"], None, vec![], PathKind::Global)])
136                     ],
137                 },
138                 explicit_self: borrowed_explicit_self(),
139                 args: vec![(Ptr(Box::new(Literal(Path::new_local(typaram))),
140                            Borrowed(None, Mutability::Mutable)), "s")],
141                 ret_ty: Literal(Path::new_(
142                     pathvec_std!(cx, result::Result),
143                     None,
144                     vec![Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
145                         vec![typaram, "Error"], None, vec![], PathKind::Local
146                     )))],
147                     PathKind::Std
148                 )),
149                 attributes: Vec::new(),
150                 is_unsafe: false,
151                 unify_fieldless_variants: false,
152                 combine_substructure: combine_substructure(Box::new(|a, b, c| {
153                     encodable_substructure(a, b, c, krate)
154                 })),
155             }
156         ],
157         associated_types: Vec::new(),
158     };
159
160     trait_def.expand(cx, mitem, item, push)
161 }
162
163 fn encodable_substructure(cx: &mut ExtCtxt<'_>,
164                           trait_span: Span,
165                           substr: &Substructure<'_>,
166                           krate: &'static str)
167                           -> P<Expr> {
168     let encoder = substr.nonself_args[0].clone();
169     // throw an underscore in front to suppress unused variable warnings
170     let blkarg = cx.ident_of("_e");
171     let blkencoder = cx.expr_ident(trait_span, blkarg);
172     let fn_path = cx.expr_path(cx.path_global(trait_span,
173                                               vec![cx.ident_of(krate),
174                                                    cx.ident_of("Encodable"),
175                                                    cx.ident_of("encode")]));
176
177     return match *substr.fields {
178         Struct(_, ref fields) => {
179             let emit_struct_field = cx.ident_of("emit_struct_field");
180             let mut stmts = Vec::new();
181             for (i, &FieldInfo { name, ref self_, span, .. }) in fields.iter().enumerate() {
182                 let name = match name {
183                     Some(id) => id.name,
184                     None => Symbol::intern(&format!("_field{}", i)),
185                 };
186                 let self_ref = cx.expr_addr_of(span, self_.clone());
187                 let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]);
188                 let lambda = cx.lambda1(span, enc, blkarg);
189                 let call = cx.expr_method_call(span,
190                                                blkencoder.clone(),
191                                                emit_struct_field,
192                                                vec![cx.expr_str(span, name),
193                                                     cx.expr_usize(span, i),
194                                                     lambda]);
195
196                 // last call doesn't need a try!
197                 let last = fields.len() - 1;
198                 let call = if i != last {
199                     cx.expr_try(span, call)
200                 } else {
201                     cx.expr(span, ExprKind::Ret(Some(call)))
202                 };
203
204                 let stmt = cx.stmt_expr(call);
205                 stmts.push(stmt);
206             }
207
208             // unit structs have no fields and need to return Ok()
209             let blk = if stmts.is_empty() {
210                 let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![]));
211                 cx.lambda1(trait_span, ok, blkarg)
212             } else {
213                 cx.lambda_stmts_1(trait_span, stmts, blkarg)
214             };
215
216             cx.expr_method_call(trait_span,
217                                 encoder,
218                                 cx.ident_of("emit_struct"),
219                                 vec![cx.expr_str(trait_span, substr.type_ident.name),
220                                      cx.expr_usize(trait_span, fields.len()),
221                                      blk])
222         }
223
224         EnumMatching(idx, _, variant, ref fields) => {
225             // We're not generating an AST that the borrow checker is expecting,
226             // so we need to generate a unique local variable to take the
227             // mutable loan out on, otherwise we get conflicts which don't
228             // actually exist.
229             let me = cx.stmt_let(trait_span, false, blkarg, encoder);
230             let encoder = cx.expr_ident(trait_span, blkarg);
231             let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
232             let mut stmts = Vec::new();
233             if !fields.is_empty() {
234                 let last = fields.len() - 1;
235                 for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() {
236                     let self_ref = cx.expr_addr_of(span, self_.clone());
237                     let enc =
238                         cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]);
239                     let lambda = cx.lambda1(span, enc, blkarg);
240                     let call = cx.expr_method_call(span,
241                                                    blkencoder.clone(),
242                                                    emit_variant_arg,
243                                                    vec![cx.expr_usize(span, i), lambda]);
244                     let call = if i != last {
245                         cx.expr_try(span, call)
246                     } else {
247                         cx.expr(span, ExprKind::Ret(Some(call)))
248                     };
249                     stmts.push(cx.stmt_expr(call));
250                 }
251             } else {
252                 let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![]));
253                 let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok)));
254                 stmts.push(cx.stmt_expr(ret_ok));
255             }
256
257             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
258             let name = cx.expr_str(trait_span, variant.node.ident.name);
259             let call = cx.expr_method_call(trait_span,
260                                            blkencoder,
261                                            cx.ident_of("emit_enum_variant"),
262                                            vec![name,
263                                                 cx.expr_usize(trait_span, idx),
264                                                 cx.expr_usize(trait_span, fields.len()),
265                                                 blk]);
266             let blk = cx.lambda1(trait_span, call, blkarg);
267             let ret = cx.expr_method_call(trait_span,
268                                           encoder,
269                                           cx.ident_of("emit_enum"),
270                                           vec![cx.expr_str(trait_span ,substr.type_ident.name),
271                                                blk]);
272             cx.expr_block(cx.block(trait_span, vec![me, cx.stmt_expr(ret)]))
273         }
274
275         _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"),
276     };
277 }