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