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