]> git.proxmox.com Git - rustc.git/blame - src/libsyntax_ext/deriving/mod.rs
New upstream version 1.14.0+dfsg1
[rustc.git] / src / libsyntax_ext / deriving / mod.rs
CommitLineData
c34b1796 1// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
223e47cc
LB
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
1a4d82fc 11//! The compiler code necessary to implement the `#[derive]` extensions.
223e47cc 12
9e0c209e 13use syntax::ast::{self, MetaItem};
c30ab7b3
SL
14use syntax::attr::HasAttrs;
15use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension};
9cc50fc6
SL
16use syntax::ext::build::AstBuilder;
17use syntax::feature_gate;
3157f602 18use syntax::codemap;
9cc50fc6 19use syntax::parse::token::{intern, intern_and_get_ident};
54a0048b 20use syntax::ptr::P;
3157f602 21use syntax_pos::Span;
223e47cc 22
85aaf69f
SL
23macro_rules! pathvec {
24 ($($x:ident)::+) => (
25 vec![ $( stringify!($x) ),+ ]
26 )
27}
28
29macro_rules! path {
30 ($($x:tt)*) => (
c30ab7b3 31 ::ext::deriving::generic::ty::Path::new( pathvec![ $($x)* ] )
85aaf69f
SL
32 )
33}
34
c34b1796
AL
35macro_rules! path_local {
36 ($x:ident) => (
9cc50fc6 37 ::deriving::generic::ty::Path::new_local(stringify!($x))
c34b1796
AL
38 )
39}
40
85aaf69f 41macro_rules! pathvec_std {
e9174d1e 42 ($cx:expr, $first:ident :: $($rest:ident)::+) => ({
c30ab7b3 43 let mut v = pathvec![$($rest)::+];
e9174d1e
SL
44 if let Some(s) = $cx.crate_root {
45 v.insert(0, s);
85aaf69f 46 }
e9174d1e
SL
47 v
48 })
85aaf69f
SL
49}
50
51macro_rules! path_std {
52 ($($x:tt)*) => (
9cc50fc6 53 ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
85aaf69f
SL
54 )
55}
56
1a4d82fc 57pub mod bounds;
223e47cc 58pub mod clone;
970d7e83
LB
59pub mod encodable;
60pub mod decodable;
1a4d82fc 61pub mod hash;
b039eaaf 62pub mod debug;
1a4d82fc 63pub mod default;
9e0c209e 64pub mod custom;
970d7e83 65
d9579d0f
AL
66#[path="cmp/partial_eq.rs"]
67pub mod partial_eq;
970d7e83
LB
68#[path="cmp/eq.rs"]
69pub mod eq;
d9579d0f
AL
70#[path="cmp/partial_ord.rs"]
71pub mod partial_ord;
970d7e83
LB
72#[path="cmp/ord.rs"]
73pub mod ord;
223e47cc 74
223e47cc 75
970d7e83
LB
76pub mod generic;
77
9e0c209e
SL
78fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
79 Span {
80 expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
81 call_site: span,
82 callee: codemap::NameAndSpan {
83 format: codemap::MacroAttribute(intern(attr_name)),
84 span: Some(span),
85 allow_internal_unstable: true,
86 },
87 }),
88 ..span
89 }
90}
91
92pub fn expand_derive(cx: &mut ExtCtxt,
d9579d0f 93 span: Span,
c34b1796 94 mitem: &MetaItem,
d9579d0f 95 annotatable: Annotatable)
9e0c209e 96 -> Vec<Annotatable> {
54a0048b
SL
97 debug!("expand_derive: span = {:?}", span);
98 debug!("expand_derive: mitem = {:?}", mitem);
99 debug!("expand_derive: annotatable input = {:?}", annotatable);
9e0c209e
SL
100 let mut item = match annotatable {
101 Annotatable::Item(item) => item,
102 other => {
103 cx.span_err(span, "`derive` can only be applied to items");
104 return vec![other]
105 }
106 };
c34b1796 107
c30ab7b3
SL
108 let mut derive_attrs = Vec::new();
109 item = item.map_attrs(|attrs| {
110 let partition = attrs.into_iter().partition(|attr| &attr.name() == "derive");
111 derive_attrs = partition.0;
112 partition.1
113 });
114
115 // Expand `#[derive]`s after other attribute macro invocations.
116 if cx.resolver.find_attr_invoc(&mut item.attrs.clone()).is_some() {
117 return vec![Annotatable::Item(item.map_attrs(|mut attrs| {
118 attrs.push(cx.attribute(span, P(mitem.clone())));
119 attrs.extend(derive_attrs);
120 attrs
121 }))];
9e0c209e 122 }
c34b1796 123
c30ab7b3
SL
124 let get_traits = |mitem: &MetaItem, cx: &ExtCtxt| {
125 if mitem.value_str().is_some() {
126 cx.span_err(mitem.span, "unexpected value in `derive`");
127 }
128
129 let traits = mitem.meta_item_list().unwrap_or(&[]).to_owned();
130 if traits.is_empty() {
131 cx.span_warn(mitem.span, "empty trait list in `derive`");
132 }
133 traits
134 };
135
136 let mut traits = get_traits(mitem, cx);
137 for derive_attr in derive_attrs {
138 traits.extend(get_traits(&derive_attr.node.value, cx));
9e0c209e 139 }
c34b1796 140
c30ab7b3
SL
141 // First, weed out malformed #[derive]
142 traits.retain(|titem| {
143 if titem.word().is_none() {
144 cx.span_err(titem.span, "malformed `derive` entry");
145 false
146 } else {
147 true
148 }
149 });
150
151 // Next, check for old-style #[derive(Foo)]
152 //
153 // These all get expanded to `#[derive_Foo]` and will get expanded first. If
154 // we actually add any attributes here then we return to get those expanded
155 // and then eventually we'll come back to finish off the other derive modes.
156 let mut new_attributes = Vec::new();
157 traits.retain(|titem| {
158 let tword = titem.word().unwrap();
159 let tname = tword.name();
160
161 if is_builtin_trait(&tname) || {
162 let derive_mode =
163 ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(intern(&tname)));
164 cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| {
165 if let SyntaxExtension::CustomDerive(_) = *ext { true } else { false }
166 }).unwrap_or(false)
167 } {
168 return true;
169 }
170
171 if !cx.ecfg.enable_custom_derive() {
172 feature_gate::emit_feature_err(&cx.parse_sess,
173 "custom_derive",
174 titem.span,
175 feature_gate::GateIssue::Language,
176 feature_gate::EXPLAIN_CUSTOM_DERIVE);
177 } else {
178 cx.span_warn(titem.span, feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE);
179 let name = intern_and_get_ident(&format!("derive_{}", tname));
180 let mitem = cx.meta_word(titem.span, name);
181 new_attributes.push(cx.attribute(mitem.span, mitem));
182 }
183 false
184 });
185 if new_attributes.len() > 0 {
186 item = item.map(|mut i| {
187 i.attrs.extend(new_attributes);
188 if traits.len() > 0 {
189 let list = cx.meta_list(mitem.span,
190 intern_and_get_ident("derive"),
191 traits);
192 i.attrs.push(cx.attribute(mitem.span, list));
193 }
194 i
195 });
196 return vec![Annotatable::Item(item)]
197 }
198
199 // Now check for macros-1.1 style custom #[derive].
200 //
201 // Expand each of them in order given, but *before* we expand any built-in
202 // derive modes. The logic here is to:
203 //
204 // 1. Collect the remaining `#[derive]` annotations into a list. If
205 // there are any left, attach a `#[derive]` attribute to the item
206 // that we're currently expanding with the remaining derive modes.
207 // 2. Manufacture a `#[derive(Foo)]` attribute to pass to the expander.
208 // 3. Expand the current item we're expanding, getting back a list of
209 // items that replace it.
210 // 4. Extend the returned list with the current list of items we've
211 // collected so far.
212 // 5. Return everything!
213 //
214 // If custom derive extensions end up threading through the `#[derive]`
215 // attribute, we'll get called again later on to continue expanding
216 // those modes.
217 let macros_11_derive = traits.iter()
218 .cloned()
219 .enumerate()
220 .filter(|&(_, ref name)| !is_builtin_trait(&name.name().unwrap()))
221 .next();
222 if let Some((i, titem)) = macros_11_derive {
223 let tname = ast::Ident::with_empty_ctxt(intern(&titem.name().unwrap()));
224 let path = ast::Path::from_ident(titem.span, tname);
225 let ext = cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).unwrap();
226
227 traits.remove(i);
228 if traits.len() > 0 {
229 item = item.map(|mut i| {
230 let list = cx.meta_list(mitem.span,
231 intern_and_get_ident("derive"),
232 traits);
233 i.attrs.push(cx.attribute(mitem.span, list));
234 i
235 });
236 }
237 let titem = cx.meta_list_item_word(titem.span, titem.name().unwrap());
238 let mitem = cx.meta_list(titem.span,
239 intern_and_get_ident("derive"),
240 vec![titem]);
241 let item = Annotatable::Item(item);
242 if let SyntaxExtension::CustomDerive(ref ext) = *ext {
243 return ext.expand(cx, mitem.span, &mitem, item);
244 } else {
245 unreachable!()
246 }
247 }
248
249 // Ok, at this point we know that there are no old-style `#[derive_Foo]` nor
250 // any macros-1.1 style `#[derive(Foo)]`. Expand all built-in traits here.
251
9e0c209e
SL
252 // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
253 // `#[structural_match]` attribute.
254 if traits.iter().filter_map(|t| t.name()).any(|t| t == "PartialEq") &&
255 traits.iter().filter_map(|t| t.name()).any(|t| t == "Eq") {
256 let structural_match = intern_and_get_ident("structural_match");
257 let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");
258 let meta = cx.meta_word(span, structural_match);
259 item = item.map(|mut i| {
260 i.attrs.push(cx.attribute(span, meta));
261 i
262 });
263 }
54a0048b 264
9e0c209e
SL
265 // RFC #1521. `Clone` can assume that `Copy` types' clone implementation is
266 // the same as the copy implementation.
267 //
268 // Add a marker attribute here picked up during #[derive(Clone)]
269 if traits.iter().filter_map(|t| t.name()).any(|t| t == "Clone") &&
270 traits.iter().filter_map(|t| t.name()).any(|t| t == "Copy") {
271 let marker = intern_and_get_ident("rustc_copy_clone_marker");
272 let span = allow_unstable(cx, span, "derive(Copy, Clone)");
273 let meta = cx.meta_word(span, marker);
274 item = item.map(|mut i| {
275 i.attrs.push(cx.attribute(span, meta));
276 i
277 });
278 }
c34b1796 279
c30ab7b3
SL
280 let mut items = Vec::new();
281 for titem in traits.iter() {
282 let tname = titem.word().unwrap().name();
283 let name = intern_and_get_ident(&format!("derive({})", tname));
284 let mitem = cx.meta_word(titem.span, name);
9e0c209e 285
c30ab7b3
SL
286 let span = Span {
287 expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
288 call_site: titem.span,
289 callee: codemap::NameAndSpan {
290 format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
291 span: Some(titem.span),
292 allow_internal_unstable: true,
293 },
294 }),
295 ..titem.span
9e0c209e 296 };
9e0c209e 297
c30ab7b3
SL
298 let my_item = Annotatable::Item(item);
299 expand_builtin(&tname, cx, span, &mitem, &my_item, &mut |a| {
300 items.push(a);
301 });
302 item = my_item.expect_item();
9e0c209e 303 }
54a0048b 304
c30ab7b3
SL
305 items.insert(0, Annotatable::Item(item));
306 return items
c34b1796
AL
307}
308
309macro_rules! derive_traits {
d9579d0f 310 ($( $name:expr => $func:path, )+) => {
9e0c209e 311 pub fn is_builtin_trait(name: &str) -> bool {
c34b1796 312 match name {
d9579d0f 313 $( $name )|+ => true,
c34b1796 314 _ => false,
970d7e83 315 }
223e47cc 316 }
9e0c209e
SL
317
318 fn expand_builtin(name: &str,
319 ecx: &mut ExtCtxt,
320 span: Span,
321 mitem: &MetaItem,
322 item: &Annotatable,
323 push: &mut FnMut(Annotatable)) {
324 match name {
325 $(
326 $name => {
327 warn_if_deprecated(ecx, span, $name);
328 $func(ecx, span, mitem, item, push);
329 }
330 )*
331 _ => panic!("not a builtin derive mode: {}", name),
332 }
333 }
223e47cc
LB
334 }
335}
c34b1796
AL
336
337derive_traits! {
338 "Clone" => clone::expand_deriving_clone,
339
340 "Hash" => hash::expand_deriving_hash,
341
342 "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
343
344 "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
345
d9579d0f
AL
346 "PartialEq" => partial_eq::expand_deriving_partial_eq,
347 "Eq" => eq::expand_deriving_eq,
348 "PartialOrd" => partial_ord::expand_deriving_partial_ord,
349 "Ord" => ord::expand_deriving_ord,
c34b1796 350
b039eaaf 351 "Debug" => debug::expand_deriving_debug,
c34b1796
AL
352
353 "Default" => default::expand_deriving_default,
354
c34b1796
AL
355 "Send" => bounds::expand_deriving_unsafe_bound,
356 "Sync" => bounds::expand_deriving_unsafe_bound,
357 "Copy" => bounds::expand_deriving_copy,
358
359 // deprecated
c34b1796
AL
360 "Encodable" => encodable::expand_deriving_encodable,
361 "Decodable" => decodable::expand_deriving_decodable,
362}
363
364#[inline] // because `name` is a compile-time constant
365fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
366 if let Some(replacement) = match name {
c34b1796
AL
367 "Encodable" => Some("RustcEncodable"),
368 "Decodable" => Some("RustcDecodable"),
369 _ => None,
370 } {
5bcae85e
SL
371 ecx.span_warn(sp,
372 &format!("derive({}) is deprecated in favor of derive({})",
373 name,
374 replacement));
c34b1796
AL
375 }
376}
54a0048b
SL
377
378/// Construct a name for the inner type parameter that can't collide with any type parameters of
379/// the item. This is achieved by starting with a base and then concatenating the names of all
380/// other type parameters.
381// FIXME(aburka): use real hygiene when that becomes possible
382fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
383 let mut typaram = String::from(base);
384 if let Annotatable::Item(ref item) = *item {
385 match item.node {
386 ast::ItemKind::Struct(_, ast::Generics { ref ty_params, .. }) |
5bcae85e 387 ast::ItemKind::Enum(_, ast::Generics { ref ty_params, .. }) => {
54a0048b
SL
388 for ty in ty_params.iter() {
389 typaram.push_str(&ty.ident.name.as_str());
390 }
391 }
392
393 _ => {}
394 }
395 }
396
397 typaram
398}
399
400/// Constructs an expression that calls an intrinsic
401fn call_intrinsic(cx: &ExtCtxt,
9e0c209e 402 mut span: Span,
54a0048b 403 intrinsic: &str,
5bcae85e
SL
404 args: Vec<P<ast::Expr>>)
405 -> P<ast::Expr> {
9e0c209e
SL
406 span.expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
407 call_site: span,
408 callee: codemap::NameAndSpan {
409 format: codemap::MacroAttribute(intern("derive")),
410 span: Some(span),
411 allow_internal_unstable: true,
412 },
413 });
414
54a0048b
SL
415 let path = cx.std_path(&["intrinsics", intrinsic]);
416 let call = cx.expr_call_global(span, path, args);
417
418 cx.expr_block(P(ast::Block {
3157f602 419 stmts: vec![cx.stmt_expr(call)],
54a0048b
SL
420 id: ast::DUMMY_NODE_ID,
421 rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
5bcae85e
SL
422 span: span,
423 }))
54a0048b 424}