]>
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 | |
54a0048b | 13 | use syntax::ast::{MetaItem, MetaItemKind, self}; |
9cc50fc6 SL |
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::Span; | |
20 | use syntax::parse::token::{intern, intern_and_get_ident}; | |
54a0048b | 21 | use syntax::ptr::P; |
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)*) => ( | |
31 | ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) ) | |
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 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 | ||
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; |
970d7e83 | 64 | |
d9579d0f AL |
65 | #[path="cmp/partial_eq.rs"] |
66 | pub mod partial_eq; | |
970d7e83 LB |
67 | #[path="cmp/eq.rs"] |
68 | pub mod eq; | |
d9579d0f AL |
69 | #[path="cmp/partial_ord.rs"] |
70 | pub mod partial_ord; | |
970d7e83 LB |
71 | #[path="cmp/ord.rs"] |
72 | pub mod ord; | |
223e47cc | 73 | |
223e47cc | 74 | |
970d7e83 LB |
75 | pub mod generic; |
76 | ||
c34b1796 | 77 | fn 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 | ||
177 | macro_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 | |
213 | derive_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 | |
241 | fn 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 | |
256 | fn 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 | |
276 | fn 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 |