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.
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.
11 //! The compiler code necessary to implement the `#[derive]` extensions.
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;
20 use syntax::parse::token::{intern, intern_and_get_ident};
24 macro_rules! pathvec {
26 vec![ $( stringify!($x) ),+ ]
32 ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
36 macro_rules! path_local {
38 ::deriving::generic::ty::Path::new_local(stringify!($x))
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 {
52 macro_rules! path_std {
54 ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
66 #[path="cmp/partial_eq.rs"]
70 #[path="cmp/partial_ord.rs"]
78 fn expand_derive(cx: &mut ExtCtxt,
81 annotatable: 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| {
88 if mitem.value_str().is_some() {
89 cx.span_err(mitem.span, "unexpected value in `derive`");
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`");
97 let mut found_partial_eq = false;
98 let mut eq_span = None;
100 for titem in traits.iter().rev() {
101 let tname = match titem.node {
102 MetaItemKind::Word(ref tname) => tname,
104 cx.span_err(titem.span, "malformed `derive` entry");
109 if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
110 feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
113 feature_gate::GateIssue::Language,
114 feature_gate::EXPLAIN_CUSTOM_DERIVE);
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,
129 if &tname[..] == "Eq" {
130 eq_span = Some(span);
131 } else if &tname[..] == "PartialEq" {
132 found_partial_eq = true;
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)))));
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,
154 cx.span_err(span, "`derive` can only be applied to items");
157 debug!("expand_derive: annotatable output = {:?}", annot);
161 macro_rules! derive_traits {
162 ($( $name:expr => $func:path, )+) => {
163 pub fn register_all(env: &mut SyntaxEnv) {
164 // Define the #[derive_*] extensions.
166 struct DeriveExtension;
168 impl MultiItemDecorator for DeriveExtension {
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 {
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| {
186 let mut w = ecx.parse_sess.span_diagnostic.struct_span_warn(
187 sp, feature_gate::EXPLAIN_DERIVE_UNDERSCORE,
189 if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_none() {
191 &format!("add #![feature(custom_derive)] to \
192 the crate attributes to enable")
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
208 warn_if_deprecated(ecx, sp, $name);
209 $func(ecx, sp, mitem, annotatable, push);
213 env.insert(intern(concat!("derive_", $name)),
214 MultiDecorator(Box::new(DeriveExtension)));
217 env.insert(intern("derive"),
218 MultiModifier(Box::new(expand_derive)));
221 fn is_builtin_trait(name: &str) -> bool {
223 $( $name )|+ => true,
231 "Clone" => clone::expand_deriving_clone,
233 "Hash" => hash::expand_deriving_hash,
235 "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
237 "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
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,
244 "Debug" => debug::expand_deriving_debug,
246 "Default" => default::expand_deriving_default,
248 "Send" => bounds::expand_deriving_unsafe_bound,
249 "Sync" => bounds::expand_deriving_unsafe_bound,
250 "Copy" => bounds::expand_deriving_copy,
253 "Encodable" => encodable::expand_deriving_encodable,
254 "Decodable" => decodable::expand_deriving_decodable,
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"),
264 ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
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 {
277 ast::ItemKind::Struct(_, ast::Generics { ref ty_params, .. }) |
278 ast::ItemKind::Enum(_, ast::Generics { ref ty_params, .. }) => {
280 for ty in ty_params.iter() {
281 typaram.push_str(&ty.ident.name.as_str());
292 /// Constructs an expression that calls an intrinsic
293 fn call_intrinsic(cx: &ExtCtxt,
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);
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),