]>
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 JJ |
11 | //! The compiler code necessary to implement the `#[derive]` extensions. |
12 | //! | |
13 | //! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is | |
14 | //! the standard library, and "std" is the core library. | |
223e47cc | 15 | |
c34b1796 AL |
16 | use ast::{Item, MetaItem, MetaWord}; |
17 | use attr::AttrMetaMethods; | |
18 | use ext::base::{ExtCtxt, SyntaxEnv, Decorator, ItemDecorator, Modifier}; | |
19 | use ext::build::AstBuilder; | |
20 | use feature_gate; | |
1a4d82fc | 21 | use codemap::Span; |
c34b1796 | 22 | use parse::token::{intern, intern_and_get_ident}; |
1a4d82fc | 23 | use ptr::P; |
223e47cc | 24 | |
85aaf69f SL |
25 | macro_rules! pathvec { |
26 | ($($x:ident)::+) => ( | |
27 | vec![ $( stringify!($x) ),+ ] | |
28 | ) | |
29 | } | |
30 | ||
31 | macro_rules! path { | |
32 | ($($x:tt)*) => ( | |
33 | ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) ) | |
34 | ) | |
35 | } | |
36 | ||
c34b1796 AL |
37 | macro_rules! path_local { |
38 | ($x:ident) => ( | |
39 | ::ext::deriving::generic::ty::Path::new_local(stringify!($x)) | |
40 | ) | |
41 | } | |
42 | ||
85aaf69f SL |
43 | macro_rules! pathvec_std { |
44 | ($cx:expr, $first:ident :: $($rest:ident)::+) => ( | |
45 | if $cx.use_std { | |
46 | pathvec!(std :: $($rest)::+) | |
47 | } else { | |
48 | pathvec!($first :: $($rest)::+) | |
49 | } | |
50 | ) | |
51 | } | |
52 | ||
53 | macro_rules! path_std { | |
54 | ($($x:tt)*) => ( | |
55 | ::ext::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) ) | |
56 | ) | |
57 | } | |
58 | ||
1a4d82fc | 59 | pub mod bounds; |
223e47cc | 60 | pub mod clone; |
970d7e83 LB |
61 | pub mod encodable; |
62 | pub mod decodable; | |
1a4d82fc | 63 | pub mod hash; |
970d7e83 | 64 | pub mod rand; |
1a4d82fc JJ |
65 | pub mod show; |
66 | pub mod default; | |
67 | pub mod primitive; | |
970d7e83 LB |
68 | |
69 | #[path="cmp/eq.rs"] | |
70 | pub mod eq; | |
71 | #[path="cmp/totaleq.rs"] | |
72 | pub mod totaleq; | |
73 | #[path="cmp/ord.rs"] | |
74 | pub mod ord; | |
75 | #[path="cmp/totalord.rs"] | |
76 | pub mod totalord; | |
223e47cc | 77 | |
223e47cc | 78 | |
970d7e83 LB |
79 | pub mod generic; |
80 | ||
c34b1796 AL |
81 | fn expand_derive(cx: &mut ExtCtxt, |
82 | _: Span, | |
83 | mitem: &MetaItem, | |
84 | item: P<Item>) -> P<Item> { | |
85 | item.map(|mut item| { | |
86 | if mitem.value_str().is_some() { | |
87 | cx.span_err(mitem.span, "unexpected value in `derive`"); | |
1a4d82fc | 88 | } |
c34b1796 AL |
89 | |
90 | let traits = mitem.meta_item_list().unwrap_or(&[]); | |
91 | if traits.is_empty() { | |
1a4d82fc | 92 | cx.span_warn(mitem.span, "empty trait list in `derive`"); |
223e47cc | 93 | } |
c34b1796 AL |
94 | |
95 | for titem in traits.iter().rev() { | |
96 | let tname = match titem.node { | |
97 | MetaWord(ref tname) => tname, | |
98 | _ => { | |
99 | cx.span_err(titem.span, "malformed `derive` entry"); | |
100 | continue; | |
101 | } | |
102 | }; | |
103 | ||
104 | if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) { | |
105 | feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, | |
106 | "custom_derive", | |
107 | titem.span, | |
108 | feature_gate::EXPLAIN_CUSTOM_DERIVE); | |
109 | continue; | |
110 | } | |
111 | ||
112 | // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar] | |
113 | item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span, | |
114 | intern_and_get_ident(&format!("derive_{}", tname))))); | |
223e47cc | 115 | } |
c34b1796 AL |
116 | |
117 | item | |
118 | }) | |
119 | } | |
120 | ||
121 | macro_rules! derive_traits { | |
122 | ($( $name:expr => $func:path, )*) => { | |
123 | pub fn register_all(env: &mut SyntaxEnv) { | |
124 | // Define the #[derive_*] extensions. | |
125 | $({ | |
126 | struct DeriveExtension; | |
127 | ||
128 | impl ItemDecorator for DeriveExtension { | |
129 | fn expand(&self, | |
130 | ecx: &mut ExtCtxt, | |
131 | sp: Span, | |
132 | mitem: &MetaItem, | |
133 | item: &Item, | |
134 | push: &mut FnMut(P<Item>)) { | |
135 | warn_if_deprecated(ecx, sp, $name); | |
136 | $func(ecx, sp, mitem, item, |i| push(i)); | |
223e47cc | 137 | } |
970d7e83 | 138 | } |
c34b1796 AL |
139 | |
140 | env.insert(intern(concat!("derive_", $name)), | |
141 | Decorator(Box::new(DeriveExtension))); | |
142 | })* | |
143 | ||
144 | env.insert(intern("derive"), | |
145 | Modifier(Box::new(expand_derive))); | |
146 | } | |
147 | ||
148 | fn is_builtin_trait(name: &str) -> bool { | |
149 | match name { | |
150 | $( $name )|* => true, | |
151 | _ => false, | |
970d7e83 | 152 | } |
223e47cc LB |
153 | } |
154 | } | |
155 | } | |
c34b1796 AL |
156 | |
157 | derive_traits! { | |
158 | "Clone" => clone::expand_deriving_clone, | |
159 | ||
160 | "Hash" => hash::expand_deriving_hash, | |
161 | ||
162 | "RustcEncodable" => encodable::expand_deriving_rustc_encodable, | |
163 | ||
164 | "RustcDecodable" => decodable::expand_deriving_rustc_decodable, | |
165 | ||
166 | "PartialEq" => eq::expand_deriving_eq, | |
167 | "Eq" => totaleq::expand_deriving_totaleq, | |
168 | "PartialOrd" => ord::expand_deriving_ord, | |
169 | "Ord" => totalord::expand_deriving_totalord, | |
170 | ||
171 | "Rand" => rand::expand_deriving_rand, | |
172 | ||
173 | "Debug" => show::expand_deriving_show, | |
174 | ||
175 | "Default" => default::expand_deriving_default, | |
176 | ||
177 | "FromPrimitive" => primitive::expand_deriving_from_primitive, | |
178 | ||
179 | "Send" => bounds::expand_deriving_unsafe_bound, | |
180 | "Sync" => bounds::expand_deriving_unsafe_bound, | |
181 | "Copy" => bounds::expand_deriving_copy, | |
182 | ||
183 | // deprecated | |
184 | "Show" => show::expand_deriving_show, | |
185 | "Encodable" => encodable::expand_deriving_encodable, | |
186 | "Decodable" => decodable::expand_deriving_decodable, | |
187 | } | |
188 | ||
189 | #[inline] // because `name` is a compile-time constant | |
190 | fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) { | |
191 | if let Some(replacement) = match name { | |
192 | "Show" => Some("Debug"), | |
193 | "Encodable" => Some("RustcEncodable"), | |
194 | "Decodable" => Some("RustcDecodable"), | |
195 | _ => None, | |
196 | } { | |
197 | ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})", | |
198 | name, replacement)); | |
199 | } | |
200 | } |