]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/clone.rs
Changed issue number to 36105
[rust.git] / src / libsyntax_ext / deriving / clone.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 use deriving::generic::*;
12 use deriving::generic::ty::*;
13
14 use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData};
15 use syntax::attr;
16 use syntax::ext::base::{Annotatable, ExtCtxt};
17 use syntax::ext::build::AstBuilder;
18 use syntax::parse::token::InternedString;
19 use syntax::ptr::P;
20 use syntax_pos::Span;
21
22 #[derive(PartialEq)]
23 enum Mode {
24     Deep,
25     Shallow,
26 }
27
28 pub fn expand_deriving_clone(cx: &mut ExtCtxt,
29                              span: Span,
30                              mitem: &MetaItem,
31                              item: &Annotatable,
32                              push: &mut FnMut(Annotatable)) {
33     // check if we can use a short form
34     //
35     // the short form is `fn clone(&self) -> Self { *self }`
36     //
37     // we can use the short form if:
38     // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
39     // - there are no generic parameters (after specialization this limitation can be removed)
40     //      if we used the short form with generics, we'd have to bound the generics with
41     //      Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
42     //      that is Clone but not Copy. and until specialization we can't write both impls.
43     let bounds;
44     let unify_fieldless_variants;
45     let substructure;
46     match *item {
47         Annotatable::Item(ref annitem) => {
48             match annitem.node {
49                 ItemKind::Struct(_, Generics { ref ty_params, .. }) |
50                 ItemKind::Enum(_, Generics { ref ty_params, .. })
51                     if ty_params.is_empty() &&
52                        attr::contains_name(&annitem.attrs, "derive_Copy") => {
53
54                     bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
55                     unify_fieldless_variants = true;
56                     substructure = combine_substructure(Box::new(|c, s, sub| {
57                         cs_clone("Clone", c, s, sub, Mode::Shallow)
58                     }));
59                 }
60
61                 _ => {
62                     bounds = vec![];
63                     unify_fieldless_variants = false;
64                     substructure = combine_substructure(Box::new(|c, s, sub| {
65                         cs_clone("Clone", c, s, sub, Mode::Deep)
66                     }));
67                 }
68             }
69         }
70
71         _ => cx.span_bug(span, "#[derive(Clone)] on trait item or impl item"),
72     }
73
74     let inline = cx.meta_word(span, InternedString::new("inline"));
75     let attrs = vec![cx.attribute(span, inline)];
76     let trait_def = TraitDef {
77         span: span,
78         attributes: Vec::new(),
79         path: path_std!(cx, core::clone::Clone),
80         additional_bounds: bounds,
81         generics: LifetimeBounds::empty(),
82         is_unsafe: false,
83         methods: vec![MethodDef {
84                           name: "clone",
85                           generics: LifetimeBounds::empty(),
86                           explicit_self: borrowed_explicit_self(),
87                           args: Vec::new(),
88                           ret_ty: Self_,
89                           attributes: attrs,
90                           is_unsafe: false,
91                           unify_fieldless_variants: unify_fieldless_variants,
92                           combine_substructure: substructure,
93                       }],
94         associated_types: Vec::new(),
95     };
96
97     trait_def.expand(cx, mitem, item, push)
98 }
99
100 fn cs_clone(name: &str,
101             cx: &mut ExtCtxt,
102             trait_span: Span,
103             substr: &Substructure,
104             mode: Mode)
105             -> P<Expr> {
106     let ctor_path;
107     let all_fields;
108     let fn_path = match mode {
109         Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]),
110         Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]),
111     };
112     let subcall = |field: &FieldInfo| {
113         let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
114
115         let span = if mode == Mode::Shallow {
116             // set the expn ID so we can call the unstable method
117             Span { expn_id: cx.backtrace(), ..trait_span }
118         } else {
119             field.span
120         };
121         cx.expr_call_global(span, fn_path.clone(), args)
122     };
123
124     let vdata;
125     match *substr.fields {
126         Struct(vdata_, ref af) => {
127             ctor_path = cx.path(trait_span, vec![substr.type_ident]);
128             all_fields = af;
129             vdata = vdata_;
130         }
131         EnumMatching(_, variant, ref af) => {
132             ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
133             all_fields = af;
134             vdata = &variant.node.data;
135         }
136         EnumNonMatchingCollapsed(..) => {
137             cx.span_bug(trait_span,
138                         &format!("non-matching enum variants in \
139                                  `derive({})`",
140                                  name))
141         }
142         StaticEnum(..) | StaticStruct(..) => {
143             cx.span_bug(trait_span, &format!("static method in `derive({})`", name))
144         }
145     }
146
147     match mode {
148         Mode::Shallow => {
149             let mut stmts: Vec<_> =
150                 all_fields.iter().map(subcall).map(|e| cx.stmt_expr(e)).collect();
151             stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
152             cx.expr_block(cx.block(trait_span, stmts))
153         }
154         Mode::Deep => {
155             match *vdata {
156                 VariantData::Struct(..) => {
157                     let fields = all_fields.iter()
158                         .map(|field| {
159                             let ident = match field.name {
160                                 Some(i) => i,
161                                 None => {
162                                     cx.span_bug(trait_span,
163                                                 &format!("unnamed field in normal struct in \
164                                                      `derive({})`",
165                                                          name))
166                                 }
167                             };
168                             cx.field_imm(field.span, ident, subcall(field))
169                         })
170                         .collect::<Vec<_>>();
171
172                     cx.expr_struct(trait_span, ctor_path, fields)
173                 }
174                 VariantData::Tuple(..) => {
175                     let subcalls = all_fields.iter().map(subcall).collect();
176                     let path = cx.expr_path(ctor_path);
177                     cx.expr_call(trait_span, path, subcalls)
178                 }
179                 VariantData::Unit(..) => cx.expr_path(ctor_path),
180             }
181         }
182     }
183 }