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.
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.
11 use deriving::generic::*;
12 use deriving::generic::ty::*;
14 use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData};
16 use syntax::ext::base::{Annotatable, ExtCtxt};
17 use syntax::ext::build::AstBuilder;
18 use syntax::parse::token::InternedString;
28 pub fn expand_deriving_clone(cx: &mut ExtCtxt,
32 push: &mut FnMut(Annotatable)) {
33 // check if we can use a short form
35 // the short form is `fn clone(&self) -> Self { *self }`
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.
44 let unify_fieldless_variants;
47 Annotatable::Item(ref annitem) => {
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") => {
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)
63 unify_fieldless_variants = false;
64 substructure = combine_substructure(Box::new(|c, s, sub| {
65 cs_clone("Clone", c, s, sub, Mode::Deep)
71 _ => cx.span_bug(span, "#[derive(Clone)] on trait item or impl item"),
74 let inline = cx.meta_word(span, InternedString::new("inline"));
75 let attrs = vec![cx.attribute(span, inline)];
76 let trait_def = TraitDef {
78 attributes: Vec::new(),
79 path: path_std!(cx, core::clone::Clone),
80 additional_bounds: bounds,
81 generics: LifetimeBounds::empty(),
83 methods: vec![MethodDef {
85 generics: LifetimeBounds::empty(),
86 explicit_self: borrowed_explicit_self(),
91 unify_fieldless_variants: unify_fieldless_variants,
92 combine_substructure: substructure,
94 associated_types: Vec::new(),
97 trait_def.expand(cx, mitem, item, push)
100 fn cs_clone(name: &str,
103 substr: &Substructure,
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"]),
112 let subcall = |field: &FieldInfo| {
113 let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
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 }
121 cx.expr_call_global(span, fn_path.clone(), args)
125 match *substr.fields {
126 Struct(vdata_, ref af) => {
127 ctor_path = cx.path(trait_span, vec![substr.type_ident]);
131 EnumMatching(_, variant, ref af) => {
132 ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
134 vdata = &variant.node.data;
136 EnumNonMatchingCollapsed(..) => {
137 cx.span_bug(trait_span,
138 &format!("non-matching enum variants in \
142 StaticEnum(..) | StaticStruct(..) => {
143 cx.span_bug(trait_span, &format!("static method in `derive({})`", name))
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))
156 VariantData::Struct(..) => {
157 let fields = all_fields.iter()
159 let ident = match field.name {
162 cx.span_bug(trait_span,
163 &format!("unnamed field in normal struct in \
168 cx.field_imm(field.span, ident, subcall(field))
170 .collect::<Vec<_>>();
172 cx.expr_struct(trait_span, ctor_path, fields)
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)
179 VariantData::Unit(..) => cx.expr_path(ctor_path),