]>
Commit | Line | Data |
---|---|---|
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 | 13 | use syntax::ast::{self, MetaItem}; |
c30ab7b3 SL |
14 | use syntax::attr::HasAttrs; |
15 | use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension}; | |
9cc50fc6 SL |
16 | use syntax::ext::build::AstBuilder; |
17 | use syntax::feature_gate; | |
3157f602 | 18 | use syntax::codemap; |
9cc50fc6 | 19 | use syntax::parse::token::{intern, intern_and_get_ident}; |
54a0048b | 20 | use syntax::ptr::P; |
3157f602 | 21 | use syntax_pos::Span; |
223e47cc | 22 | |
85aaf69f SL |
23 | macro_rules! pathvec { |
24 | ($($x:ident)::+) => ( | |
25 | vec![ $( stringify!($x) ),+ ] | |
26 | ) | |
27 | } | |
28 | ||
29 | macro_rules! path { | |
30 | ($($x:tt)*) => ( | |
c30ab7b3 | 31 | ::ext::deriving::generic::ty::Path::new( pathvec![ $($x)* ] ) |
85aaf69f SL |
32 | ) |
33 | } | |
34 | ||
c34b1796 AL |
35 | macro_rules! path_local { |
36 | ($x:ident) => ( | |
9cc50fc6 | 37 | ::deriving::generic::ty::Path::new_local(stringify!($x)) |
c34b1796 AL |
38 | ) |
39 | } | |
40 | ||
85aaf69f | 41 | macro_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 | ||
51 | macro_rules! path_std { | |
52 | ($($x:tt)*) => ( | |
9cc50fc6 | 53 | ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) ) |
85aaf69f SL |
54 | ) |
55 | } | |
56 | ||
1a4d82fc | 57 | pub mod bounds; |
223e47cc | 58 | pub mod clone; |
970d7e83 LB |
59 | pub mod encodable; |
60 | pub mod decodable; | |
1a4d82fc | 61 | pub mod hash; |
b039eaaf | 62 | pub mod debug; |
1a4d82fc | 63 | pub mod default; |
9e0c209e | 64 | pub mod custom; |
970d7e83 | 65 | |
d9579d0f AL |
66 | #[path="cmp/partial_eq.rs"] |
67 | pub mod partial_eq; | |
970d7e83 LB |
68 | #[path="cmp/eq.rs"] |
69 | pub mod eq; | |
d9579d0f AL |
70 | #[path="cmp/partial_ord.rs"] |
71 | pub mod partial_ord; | |
970d7e83 LB |
72 | #[path="cmp/ord.rs"] |
73 | pub mod ord; | |
223e47cc | 74 | |
223e47cc | 75 | |
970d7e83 LB |
76 | pub mod generic; |
77 | ||
9e0c209e SL |
78 | fn 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 | ||
92 | pub 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 | ||
309 | macro_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 | |
337 | derive_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 | |
365 | fn 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 | |
382 | fn 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 | |
401 | fn 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 | } |