]> git.proxmox.com Git - rustc.git/blame - src/libsyntax_ext/deriving/mod.rs
Imported Upstream version 1.9.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
54a0048b 13use syntax::ast::{MetaItem, MetaItemKind, self};
9cc50fc6
SL
14use syntax::attr::AttrMetaMethods;
15use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable};
16use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
17use syntax::ext::build::AstBuilder;
18use syntax::feature_gate;
19use syntax::codemap::Span;
20use syntax::parse::token::{intern, intern_and_get_ident};
54a0048b 21use syntax::ptr::P;
223e47cc 22
85aaf69f
SL
23macro_rules! pathvec {
24 ($($x:ident)::+) => (
25 vec![ $( stringify!($x) ),+ ]
26 )
27}
28
29macro_rules! path {
30 ($($x:tt)*) => (
31 ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
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
SL
42 ($cx:expr, $first:ident :: $($rest:ident)::+) => ({
43 let mut v = pathvec!($($rest)::+);
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;
970d7e83 64
d9579d0f
AL
65#[path="cmp/partial_eq.rs"]
66pub mod partial_eq;
970d7e83
LB
67#[path="cmp/eq.rs"]
68pub mod eq;
d9579d0f
AL
69#[path="cmp/partial_ord.rs"]
70pub mod partial_ord;
970d7e83
LB
71#[path="cmp/ord.rs"]
72pub mod ord;
223e47cc 73
223e47cc 74
970d7e83
LB
75pub mod generic;
76
c34b1796 77fn expand_derive(cx: &mut ExtCtxt,
d9579d0f 78 span: Span,
c34b1796 79 mitem: &MetaItem,
d9579d0f
AL
80 annotatable: Annotatable)
81 -> Annotatable {
54a0048b
SL
82 debug!("expand_derive: span = {:?}", span);
83 debug!("expand_derive: mitem = {:?}", mitem);
84 debug!("expand_derive: annotatable input = {:?}", annotatable);
85 let annot = annotatable.map_item_or(|item| {
d9579d0f
AL
86 item.map(|mut item| {
87 if mitem.value_str().is_some() {
88 cx.span_err(mitem.span, "unexpected value in `derive`");
89 }
c34b1796 90
d9579d0f
AL
91 let traits = mitem.meta_item_list().unwrap_or(&[]);
92 if traits.is_empty() {
93 cx.span_warn(mitem.span, "empty trait list in `derive`");
94 }
c34b1796 95
54a0048b
SL
96 let mut found_partial_eq = false;
97 let mut found_eq = false;
98
d9579d0f
AL
99 for titem in traits.iter().rev() {
100 let tname = match titem.node {
7453a54e 101 MetaItemKind::Word(ref tname) => tname,
d9579d0f
AL
102 _ => {
103 cx.span_err(titem.span, "malformed `derive` entry");
104 continue;
105 }
106 };
107
108 if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
109 feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
110 "custom_derive",
111 titem.span,
e9174d1e 112 feature_gate::GateIssue::Language,
d9579d0f 113 feature_gate::EXPLAIN_CUSTOM_DERIVE);
c34b1796
AL
114 continue;
115 }
c34b1796 116
54a0048b
SL
117 if &tname[..] == "Eq" {
118 found_eq = true;
119 } else if &tname[..] == "PartialEq" {
120 found_partial_eq = true;
121 }
122
d9579d0f
AL
123 // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
124 item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
125 intern_and_get_ident(&format!("derive_{}", tname)))));
126 }
c34b1796 127
54a0048b
SL
128 // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
129 // `#[structural_match]` attribute.
130 if found_partial_eq && found_eq {
131 // This span is **very** sensitive and crucial to
132 // getting the stability behavior we want. What we are
133 // doing is marking `#[structural_match]` with the
134 // span of the `#[deriving(...)]` attribute (the
135 // entire attribute, not just the `PartialEq` or `Eq`
136 // part), but with the current backtrace. The current
137 // backtrace will contain a topmost entry that IS this
138 // `#[deriving(...)]` attribute and with the
139 // "allow-unstable" flag set to true.
140 //
141 // Note that we do NOT use the span of the `Eq`
142 // text itself. You might think this is
143 // equivalent, because the `Eq` appears within the
144 // `#[deriving(Eq)]` attribute, and hence we would
145 // inherit the "allows unstable" from the
146 // backtrace. But in fact this is not always the
147 // case. The actual source text that led to
148 // deriving can be `#[$attr]`, for example, where
149 // `$attr == deriving(Eq)`. In that case, the
150 // "#[structural_match]" would be considered to
151 // originate not from the deriving call but from
152 // text outside the deriving call, and hence would
153 // be forbidden from using unstable
154 // content.
155 //
156 // See tests src/run-pass/rfc1445 for
157 // examples. --nmatsakis
158 let span = Span { expn_id: cx.backtrace(), .. span };
159 assert!(cx.parse_sess.codemap().span_allows_unstable(span));
160 debug!("inserting structural_match with span {:?}", span);
161 let structural_match = intern_and_get_ident("structural_match");
162 item.attrs.push(cx.attribute(span,
163 cx.meta_word(span,
164 structural_match)));
165 }
166
d9579d0f
AL
167 item
168 })
169 }, |a| {
170 cx.span_err(span, "`derive` can only be applied to items");
171 a
54a0048b
SL
172 });
173 debug!("expand_derive: annotatable output = {:?}", annot);
174 annot
c34b1796
AL
175}
176
177macro_rules! derive_traits {
d9579d0f 178 ($( $name:expr => $func:path, )+) => {
c34b1796
AL
179 pub fn register_all(env: &mut SyntaxEnv) {
180 // Define the #[derive_*] extensions.
181 $({
182 struct DeriveExtension;
183
d9579d0f 184 impl MultiItemDecorator for DeriveExtension {
c34b1796
AL
185 fn expand(&self,
186 ecx: &mut ExtCtxt,
187 sp: Span,
188 mitem: &MetaItem,
62682a34 189 annotatable: &Annotatable,
d9579d0f 190 push: &mut FnMut(Annotatable)) {
c34b1796 191 warn_if_deprecated(ecx, sp, $name);
d9579d0f 192 $func(ecx, sp, mitem, annotatable, push);
223e47cc 193 }
970d7e83 194 }
c34b1796
AL
195
196 env.insert(intern(concat!("derive_", $name)),
d9579d0f
AL
197 MultiDecorator(Box::new(DeriveExtension)));
198 })+
c34b1796
AL
199
200 env.insert(intern("derive"),
d9579d0f 201 MultiModifier(Box::new(expand_derive)));
c34b1796
AL
202 }
203
204 fn is_builtin_trait(name: &str) -> bool {
205 match name {
d9579d0f 206 $( $name )|+ => true,
c34b1796 207 _ => false,
970d7e83 208 }
223e47cc
LB
209 }
210 }
211}
c34b1796
AL
212
213derive_traits! {
214 "Clone" => clone::expand_deriving_clone,
215
216 "Hash" => hash::expand_deriving_hash,
217
218 "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
219
220 "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
221
d9579d0f
AL
222 "PartialEq" => partial_eq::expand_deriving_partial_eq,
223 "Eq" => eq::expand_deriving_eq,
224 "PartialOrd" => partial_ord::expand_deriving_partial_ord,
225 "Ord" => ord::expand_deriving_ord,
c34b1796 226
b039eaaf 227 "Debug" => debug::expand_deriving_debug,
c34b1796
AL
228
229 "Default" => default::expand_deriving_default,
230
c34b1796
AL
231 "Send" => bounds::expand_deriving_unsafe_bound,
232 "Sync" => bounds::expand_deriving_unsafe_bound,
233 "Copy" => bounds::expand_deriving_copy,
234
235 // deprecated
c34b1796
AL
236 "Encodable" => encodable::expand_deriving_encodable,
237 "Decodable" => decodable::expand_deriving_decodable,
238}
239
240#[inline] // because `name` is a compile-time constant
241fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
242 if let Some(replacement) = match name {
c34b1796
AL
243 "Encodable" => Some("RustcEncodable"),
244 "Decodable" => Some("RustcDecodable"),
245 _ => None,
246 } {
247 ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
248 name, replacement));
249 }
250}
54a0048b
SL
251
252/// Construct a name for the inner type parameter that can't collide with any type parameters of
253/// the item. This is achieved by starting with a base and then concatenating the names of all
254/// other type parameters.
255// FIXME(aburka): use real hygiene when that becomes possible
256fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
257 let mut typaram = String::from(base);
258 if let Annotatable::Item(ref item) = *item {
259 match item.node {
260 ast::ItemKind::Struct(_, ast::Generics { ref ty_params, .. }) |
261 ast::ItemKind::Enum(_, ast::Generics { ref ty_params, .. }) => {
262
263 for ty in ty_params.iter() {
264 typaram.push_str(&ty.ident.name.as_str());
265 }
266 }
267
268 _ => {}
269 }
270 }
271
272 typaram
273}
274
275/// Constructs an expression that calls an intrinsic
276fn call_intrinsic(cx: &ExtCtxt,
277 span: Span,
278 intrinsic: &str,
279 args: Vec<P<ast::Expr>>) -> P<ast::Expr> {
280 let path = cx.std_path(&["intrinsics", intrinsic]);
281 let call = cx.expr_call_global(span, path, args);
282
283 cx.expr_block(P(ast::Block {
284 stmts: vec![],
285 expr: Some(call),
286 id: ast::DUMMY_NODE_ID,
287 rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
288 span: span }))
289}
290