]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/clone.rs
hygiene: `ExpnInfo` -> `ExpnData`
[rust.git] / src / libsyntax_ext / deriving / clone.rs
1 use crate::deriving::path_std;
2 use crate::deriving::generic::*;
3 use crate::deriving::generic::ty::*;
4
5 use syntax::ast::{self, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData};
6 use syntax::ext::base::{Annotatable, ExtCtxt, SpecialDerives};
7 use syntax::ptr::P;
8 use syntax::symbol::{kw, sym, Symbol};
9 use syntax_pos::Span;
10
11 pub fn expand_deriving_clone(cx: &mut ExtCtxt<'_>,
12                              span: Span,
13                              mitem: &MetaItem,
14                              item: &Annotatable,
15                              push: &mut dyn FnMut(Annotatable)) {
16     // check if we can use a short form
17     //
18     // the short form is `fn clone(&self) -> Self { *self }`
19     //
20     // we can use the short form if:
21     // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
22     // - there are no generic parameters (after specialization this limitation can be removed)
23     //      if we used the short form with generics, we'd have to bound the generics with
24     //      Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
25     //      that is Clone but not Copy. and until specialization we can't write both impls.
26     // - the item is a union with Copy fields
27     //      Unions with generic parameters still can derive Clone because they require Copy
28     //      for deriving, Clone alone is not enough.
29     //      Whever Clone is implemented for fields is irrelevant so we don't assert it.
30     let bounds;
31     let substructure;
32     let is_shallow;
33     match *item {
34         Annotatable::Item(ref annitem) => {
35             match annitem.node {
36                 ItemKind::Struct(_, Generics { ref params, .. }) |
37                 ItemKind::Enum(_, Generics { ref params, .. }) => {
38                     let container_id = cx.current_expansion.id.expn_data().parent;
39                     if cx.resolver.has_derives(container_id, SpecialDerives::COPY) &&
40                         !params.iter().any(|param| match param.kind {
41                             ast::GenericParamKind::Type { .. } => true,
42                             _ => false,
43                         })
44                     {
45                         bounds = vec![];
46                         is_shallow = true;
47                         substructure = combine_substructure(Box::new(|c, s, sub| {
48                             cs_clone_shallow("Clone", c, s, sub, false)
49                         }));
50                     } else {
51                         bounds = vec![];
52                         is_shallow = false;
53                         substructure = combine_substructure(Box::new(|c, s, sub| {
54                             cs_clone("Clone", c, s, sub)
55                         }));
56                     }
57                 }
58                 ItemKind::Union(..) => {
59                     bounds = vec![Literal(path_std!(cx, marker::Copy))];
60                     is_shallow = true;
61                     substructure = combine_substructure(Box::new(|c, s, sub| {
62                         cs_clone_shallow("Clone", c, s, sub, true)
63                     }));
64                 }
65                 _ => {
66                     bounds = vec![];
67                     is_shallow = false;
68                     substructure = combine_substructure(Box::new(|c, s, sub| {
69                         cs_clone("Clone", c, s, sub)
70                     }));
71                 }
72             }
73         }
74
75         _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
76     }
77
78     let inline = cx.meta_word(span, sym::inline);
79     let attrs = vec![cx.attribute(inline)];
80     let trait_def = TraitDef {
81         span,
82         attributes: Vec::new(),
83         path: path_std!(cx, clone::Clone),
84         additional_bounds: bounds,
85         generics: LifetimeBounds::empty(),
86         is_unsafe: false,
87         supports_unions: true,
88         methods: vec![MethodDef {
89                           name: "clone",
90                           generics: LifetimeBounds::empty(),
91                           explicit_self: borrowed_explicit_self(),
92                           args: Vec::new(),
93                           ret_ty: Self_,
94                           attributes: attrs,
95                           is_unsafe: false,
96                           unify_fieldless_variants: false,
97                           combine_substructure: substructure,
98                       }],
99         associated_types: Vec::new(),
100     };
101
102     trait_def.expand_ext(cx, mitem, item, push, is_shallow)
103 }
104
105 fn cs_clone_shallow(name: &str,
106                     cx: &mut ExtCtxt<'_>,
107                     trait_span: Span,
108                     substr: &Substructure<'_>,
109                     is_union: bool)
110                     -> P<Expr> {
111     fn assert_ty_bounds(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>,
112                         ty: P<ast::Ty>, span: Span, helper_name: &str) {
113         // Generate statement `let _: helper_name<ty>;`,
114         // set the expn ID so we can use the unstable struct.
115         let span = span.with_ctxt(cx.backtrace());
116         let assert_path = cx.path_all(span, true,
117                                         cx.std_path(&[sym::clone, Symbol::intern(helper_name)]),
118                                         vec![GenericArg::Type(ty)], vec![]);
119         stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
120     }
121     fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
122         for field in variant.fields() {
123             // let _: AssertParamIsClone<FieldTy>;
124             assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
125         }
126     }
127
128     let mut stmts = Vec::new();
129     if is_union {
130         // let _: AssertParamIsCopy<Self>;
131         let self_ty =
132             cx.ty_path(cx.path_ident(trait_span, ast::Ident::with_dummy_span(kw::SelfUpper)));
133         assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy");
134     } else {
135         match *substr.fields {
136             StaticStruct(vdata, ..) => {
137                 process_variant(cx, &mut stmts, vdata);
138             }
139             StaticEnum(enum_def, ..) => {
140                 for variant in &enum_def.variants {
141                     process_variant(cx, &mut stmts, &variant.data);
142                 }
143             }
144             _ => cx.span_bug(trait_span, &format!("unexpected substructure in \
145                                                     shallow `derive({})`", name))
146         }
147     }
148     stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
149     cx.expr_block(cx.block(trait_span, stmts))
150 }
151
152 fn cs_clone(name: &str,
153             cx: &mut ExtCtxt<'_>,
154             trait_span: Span,
155             substr: &Substructure<'_>)
156             -> P<Expr> {
157     let ctor_path;
158     let all_fields;
159     let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
160     let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| {
161         let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
162         cx.expr_call_global(field.span, fn_path.clone(), args)
163     };
164
165     let vdata;
166     match *substr.fields {
167         Struct(vdata_, ref af) => {
168             ctor_path = cx.path(trait_span, vec![substr.type_ident]);
169             all_fields = af;
170             vdata = vdata_;
171         }
172         EnumMatching(.., variant, ref af) => {
173             ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]);
174             all_fields = af;
175             vdata = &variant.data;
176         }
177         EnumNonMatchingCollapsed(..) => {
178             cx.span_bug(trait_span,
179                         &format!("non-matching enum variants in \
180                                  `derive({})`",
181                                  name))
182         }
183         StaticEnum(..) | StaticStruct(..) => {
184             cx.span_bug(trait_span, &format!("static method in `derive({})`", name))
185         }
186     }
187
188     match *vdata {
189         VariantData::Struct(..) => {
190             let fields = all_fields.iter()
191                 .map(|field| {
192                     let ident = match field.name {
193                         Some(i) => i,
194                         None => {
195                             cx.span_bug(trait_span,
196                                         &format!("unnamed field in normal struct in \
197                                                 `derive({})`",
198                                                     name))
199                         }
200                     };
201                     let call = subcall(cx, field);
202                     cx.field_imm(field.span, ident, call)
203                 })
204                 .collect::<Vec<_>>();
205
206             cx.expr_struct(trait_span, ctor_path, fields)
207         }
208         VariantData::Tuple(..) => {
209             let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
210             let path = cx.expr_path(ctor_path);
211             cx.expr_call(trait_span, path, subcalls)
212         }
213         VariantData::Unit(..) => cx.expr_path(ctor_path),
214     }
215 }