]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/mod.rs
Rollup merge of #34436 - jseyfried:no_block_expr, r=eddyb
[rust.git] / src / libsyntax_ext / deriving / mod.rs
1 // Copyright 2012-2015 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.
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
11 //! The compiler code necessary to implement the `#[derive]` extensions.
12
13 use syntax::ast::{MetaItem, MetaItemKind, self};
14 use syntax::attr::AttrMetaMethods;
15 use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable};
16 use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
17 use syntax::ext::build::AstBuilder;
18 use syntax::feature_gate;
19 use syntax::codemap;
20 use syntax::parse::token::{intern, intern_and_get_ident};
21 use syntax::ptr::P;
22 use syntax_pos::Span;
23
24 macro_rules! pathvec {
25     ($($x:ident)::+) => (
26         vec![ $( stringify!($x) ),+ ]
27     )
28 }
29
30 macro_rules! path {
31     ($($x:tt)*) => (
32         ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
33     )
34 }
35
36 macro_rules! path_local {
37     ($x:ident) => (
38         ::deriving::generic::ty::Path::new_local(stringify!($x))
39     )
40 }
41
42 macro_rules! pathvec_std {
43     ($cx:expr, $first:ident :: $($rest:ident)::+) => ({
44         let mut v = pathvec!($($rest)::+);
45         if let Some(s) = $cx.crate_root {
46             v.insert(0, s);
47         }
48         v
49     })
50 }
51
52 macro_rules! path_std {
53     ($($x:tt)*) => (
54         ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
55     )
56 }
57
58 pub mod bounds;
59 pub mod clone;
60 pub mod encodable;
61 pub mod decodable;
62 pub mod hash;
63 pub mod debug;
64 pub mod default;
65
66 #[path="cmp/partial_eq.rs"]
67 pub mod partial_eq;
68 #[path="cmp/eq.rs"]
69 pub mod eq;
70 #[path="cmp/partial_ord.rs"]
71 pub mod partial_ord;
72 #[path="cmp/ord.rs"]
73 pub mod ord;
74
75
76 pub mod generic;
77
78 fn expand_derive(cx: &mut ExtCtxt,
79                  span: Span,
80                  mitem: &MetaItem,
81                  annotatable: Annotatable)
82                  -> Annotatable {
83     debug!("expand_derive: span = {:?}", span);
84     debug!("expand_derive: mitem = {:?}", mitem);
85     debug!("expand_derive: annotatable input  = {:?}", annotatable);
86     let annot = annotatable.map_item_or(|item| {
87         item.map(|mut item| {
88             if mitem.value_str().is_some() {
89                 cx.span_err(mitem.span, "unexpected value in `derive`");
90             }
91
92             let traits = mitem.meta_item_list().unwrap_or(&[]);
93             if traits.is_empty() {
94                 cx.span_warn(mitem.span, "empty trait list in `derive`");
95             }
96
97             let mut found_partial_eq = false;
98             let mut eq_span = None;
99
100             for titem in traits.iter().rev() {
101                 let tname = match titem.node {
102                     MetaItemKind::Word(ref tname) => tname,
103                     _ => {
104                         cx.span_err(titem.span, "malformed `derive` entry");
105                         continue;
106                     }
107                 };
108
109                 if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
110                     feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
111                                                    "custom_derive",
112                                                    titem.span,
113                                                    feature_gate::GateIssue::Language,
114                                                    feature_gate::EXPLAIN_CUSTOM_DERIVE);
115                     continue;
116                 }
117
118                 let span = Span {
119                     expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
120                         call_site: titem.span,
121                         callee: codemap::NameAndSpan {
122                             format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
123                             span: Some(titem.span),
124                             allow_internal_unstable: true,
125                         },
126                     }), ..titem.span
127                 };
128
129                 if &tname[..] == "Eq" {
130                     eq_span = Some(span);
131                 } else if &tname[..] == "PartialEq" {
132                     found_partial_eq = true;
133                 }
134
135                 // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
136                 item.attrs.push(cx.attribute(span, cx.meta_word(titem.span,
137                     intern_and_get_ident(&format!("derive_{}", tname)))));
138             }
139
140             // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
141             // `#[structural_match]` attribute.
142             if let Some(eq_span) = eq_span {
143                 if found_partial_eq {
144                     let structural_match = intern_and_get_ident("structural_match");
145                     item.attrs.push(cx.attribute(eq_span,
146                                                  cx.meta_word(eq_span,
147                                                               structural_match)));
148                 }
149             }
150
151             item
152         })
153     }, |a| {
154         cx.span_err(span, "`derive` can only be applied to items");
155         a
156     });
157     debug!("expand_derive: annotatable output = {:?}", annot);
158     annot
159 }
160
161 macro_rules! derive_traits {
162     ($( $name:expr => $func:path, )+) => {
163         pub fn register_all(env: &mut SyntaxEnv) {
164             // Define the #[derive_*] extensions.
165             $({
166                 struct DeriveExtension;
167
168                 impl MultiItemDecorator for DeriveExtension {
169                     fn expand(&self,
170                               ecx: &mut ExtCtxt,
171                               sp: Span,
172                               mitem: &MetaItem,
173                               annotatable: &Annotatable,
174                               push: &mut FnMut(Annotatable)) {
175                         if !ecx.parse_sess.codemap().span_allows_unstable(sp)
176                             && !ecx.ecfg.features.unwrap().custom_derive {
177                             // FIXME:
178                             // https://github.com/rust-lang/rust/pull/32671#issuecomment-206245303
179                             // This is just to avoid breakage with syntex.
180                             // Remove that to spawn an error instead.
181                             let cm = ecx.parse_sess.codemap();
182                             let parent = cm.with_expn_info(ecx.backtrace(),
183                                                            |info| info.unwrap().call_site.expn_id);
184                             cm.with_expn_info(parent, |info| {
185                                 if info.is_some() {
186                                     let mut w = ecx.parse_sess.span_diagnostic.struct_span_warn(
187                                         sp, feature_gate::EXPLAIN_DERIVE_UNDERSCORE,
188                                     );
189                                     if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_none() {
190                                         w.help(
191                                             &format!("add #![feature(custom_derive)] to \
192                                                       the crate attributes to enable")
193                                         );
194                                     }
195                                     w.emit();
196                                 } else {
197                                     feature_gate::emit_feature_err(
198                                         &ecx.parse_sess.span_diagnostic,
199                                         "custom_derive", sp, feature_gate::GateIssue::Language,
200                                         feature_gate::EXPLAIN_DERIVE_UNDERSCORE
201                                     );
202
203                                     return;
204                                 }
205                             })
206                         }
207
208                         warn_if_deprecated(ecx, sp, $name);
209                         $func(ecx, sp, mitem, annotatable, push);
210                     }
211                 }
212
213                 env.insert(intern(concat!("derive_", $name)),
214                            MultiDecorator(Box::new(DeriveExtension)));
215             })+
216
217             env.insert(intern("derive"),
218                        MultiModifier(Box::new(expand_derive)));
219         }
220
221         fn is_builtin_trait(name: &str) -> bool {
222             match name {
223                 $( $name )|+ => true,
224                 _ => false,
225             }
226         }
227     }
228 }
229
230 derive_traits! {
231     "Clone" => clone::expand_deriving_clone,
232
233     "Hash" => hash::expand_deriving_hash,
234
235     "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
236
237     "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
238
239     "PartialEq" => partial_eq::expand_deriving_partial_eq,
240     "Eq" => eq::expand_deriving_eq,
241     "PartialOrd" => partial_ord::expand_deriving_partial_ord,
242     "Ord" => ord::expand_deriving_ord,
243
244     "Debug" => debug::expand_deriving_debug,
245
246     "Default" => default::expand_deriving_default,
247
248     "Send" => bounds::expand_deriving_unsafe_bound,
249     "Sync" => bounds::expand_deriving_unsafe_bound,
250     "Copy" => bounds::expand_deriving_copy,
251
252     // deprecated
253     "Encodable" => encodable::expand_deriving_encodable,
254     "Decodable" => decodable::expand_deriving_decodable,
255 }
256
257 #[inline] // because `name` is a compile-time constant
258 fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
259     if let Some(replacement) = match name {
260         "Encodable" => Some("RustcEncodable"),
261         "Decodable" => Some("RustcDecodable"),
262         _ => None,
263     } {
264         ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
265                                    name, replacement));
266     }
267 }
268
269 /// Construct a name for the inner type parameter that can't collide with any type parameters of
270 /// the item. This is achieved by starting with a base and then concatenating the names of all
271 /// other type parameters.
272 // FIXME(aburka): use real hygiene when that becomes possible
273 fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
274     let mut typaram = String::from(base);
275     if let Annotatable::Item(ref item) = *item {
276         match item.node {
277             ast::ItemKind::Struct(_, ast::Generics { ref ty_params, .. }) |
278                 ast::ItemKind::Enum(_, ast::Generics { ref ty_params, .. }) => {
279
280                 for ty in ty_params.iter() {
281                     typaram.push_str(&ty.ident.name.as_str());
282                 }
283             }
284
285             _ => {}
286         }
287     }
288
289     typaram
290 }
291
292 /// Constructs an expression that calls an intrinsic
293 fn call_intrinsic(cx: &ExtCtxt,
294                   span: Span,
295                   intrinsic: &str,
296                   args: Vec<P<ast::Expr>>) -> P<ast::Expr> {
297     let path = cx.std_path(&["intrinsics", intrinsic]);
298     let call = cx.expr_call_global(span, path, args);
299
300     cx.expr_block(P(ast::Block {
301         stmts: vec![cx.stmt_expr(call)],
302         id: ast::DUMMY_NODE_ID,
303         rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
304         span: span }))
305 }
306