]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/proc_macro_registrar.rs
Auto merge of #38605 - estebank:fix-38371, r=nikomatsakis
[rust.git] / src / libsyntax_ext / proc_macro_registrar.rs
1 // Copyright 2016 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 use std::mem;
12
13 use errors;
14 use syntax::ast::{self, Ident, NodeId};
15 use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute};
16 use syntax::ext::base::ExtCtxt;
17 use syntax::ext::build::AstBuilder;
18 use syntax::ext::expand::ExpansionConfig;
19 use syntax::parse::ParseSess;
20 use syntax::fold::Folder;
21 use syntax::ptr::P;
22 use syntax::symbol::Symbol;
23 use syntax_pos::{Span, DUMMY_SP};
24 use syntax::visit::{self, Visitor};
25
26 use deriving;
27
28 struct CustomDerive {
29     trait_name: ast::Name,
30     function_name: Ident,
31     span: Span,
32     attrs: Vec<ast::Name>,
33 }
34
35 struct CollectCustomDerives<'a> {
36     derives: Vec<CustomDerive>,
37     in_root: bool,
38     handler: &'a errors::Handler,
39     is_proc_macro_crate: bool,
40     is_test_crate: bool,
41 }
42
43 pub fn modify(sess: &ParseSess,
44               resolver: &mut ::syntax::ext::base::Resolver,
45               mut krate: ast::Crate,
46               is_proc_macro_crate: bool,
47               is_test_crate: bool,
48               num_crate_types: usize,
49               handler: &errors::Handler) -> ast::Crate {
50     let ecfg = ExpansionConfig::default("proc_macro".to_string());
51     let mut cx = ExtCtxt::new(sess, ecfg, resolver);
52
53     let derives = {
54         let mut collect = CollectCustomDerives {
55             derives: Vec::new(),
56             in_root: true,
57             handler: handler,
58             is_proc_macro_crate: is_proc_macro_crate,
59             is_test_crate: is_test_crate,
60         };
61         visit::walk_crate(&mut collect, &krate);
62         collect.derives
63     };
64
65     if !is_proc_macro_crate {
66         return krate
67     }
68
69     if num_crate_types > 1 {
70         handler.err("cannot mix `proc-macro` crate type with others");
71     }
72
73     if is_test_crate {
74         return krate;
75     }
76
77     krate.module.items.push(mk_registrar(&mut cx, &derives));
78
79     if krate.exported_macros.len() > 0 {
80         handler.err("cannot export macro_rules! macros from a `proc-macro` \
81                      crate type currently");
82     }
83
84     return krate
85 }
86
87 impl<'a> CollectCustomDerives<'a> {
88     fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
89         if self.is_proc_macro_crate &&
90            self.in_root &&
91            *vis == ast::Visibility::Public {
92             self.handler.span_err(sp,
93                                   "`proc-macro` crate types cannot \
94                                    export any items other than functions \
95                                    tagged with `#[proc_macro_derive]` \
96                                    currently");
97         }
98     }
99 }
100
101 impl<'a> Visitor<'a> for CollectCustomDerives<'a> {
102     fn visit_item(&mut self, item: &'a ast::Item) {
103         let mut attrs = item.attrs.iter().filter(|a| a.check_name("proc_macro_derive"));
104
105         // First up, make sure we're checking a bare function. If we're not then
106         // we're just not interested in this item.
107         //
108         // If we find one, try to locate a `#[proc_macro_derive]` attribute on
109         // it.
110         match item.node {
111             ast::ItemKind::Fn(..) => {}
112             _ => {
113                 // Check for invalid use of proc_macro_derive
114                 if let Some(attr) = attrs.next() {
115                     self.handler.span_err(attr.span(),
116                                           "the `#[proc_macro_derive]` \
117                                           attribute may only be used \
118                                           on bare functions");
119                     return;
120                 }
121                 self.check_not_pub_in_root(&item.vis, item.span);
122                 return visit::walk_item(self, item)
123             }
124         }
125
126         let attr = match attrs.next() {
127             Some(attr) => attr,
128             None => {
129                 self.check_not_pub_in_root(&item.vis, item.span);
130                 return visit::walk_item(self, item)
131             }
132         };
133
134         if let Some(a) = attrs.next() {
135             self.handler.span_err(a.span(), "multiple `#[proc_macro_derive]` \
136                                              attributes found");
137         }
138
139         if self.is_test_crate {
140             return;
141         }
142
143         if !self.is_proc_macro_crate {
144             self.handler.span_err(attr.span(),
145                                   "the `#[proc_macro_derive]` attribute is \
146                                    only usable with crates of the `proc-macro` \
147                                    crate type");
148         }
149
150         // Once we've located the `#[proc_macro_derive]` attribute, verify
151         // that it's of the form `#[proc_macro_derive(Foo)]` or
152         // `#[proc_macro_derive(Foo, attributes(A, ..))]`
153         let list = match attr.meta_item_list() {
154             Some(list) => list,
155             None => {
156                 self.handler.span_err(attr.span(),
157                                       "attribute must be of form: \
158                                        #[proc_macro_derive(TraitName)]");
159                 return
160             }
161         };
162         if list.len() != 1 && list.len() != 2 {
163             self.handler.span_err(attr.span(),
164                                   "attribute must have either one or two arguments");
165             return
166         }
167         let trait_attr = &list[0];
168         let attributes_attr = list.get(1);
169         let trait_name = match trait_attr.name() {
170             Some(name) => name,
171             _ => {
172                 self.handler.span_err(trait_attr.span(), "not a meta item");
173                 return
174             }
175         };
176         if !trait_attr.is_word() {
177             self.handler.span_err(trait_attr.span(), "must only be one word");
178         }
179
180         if deriving::is_builtin_trait(trait_name) {
181             self.handler.span_err(trait_attr.span(),
182                                   "cannot override a built-in #[derive] mode");
183         }
184
185         if self.derives.iter().any(|d| d.trait_name == trait_name) {
186             self.handler.span_err(trait_attr.span(),
187                                   "derive mode defined twice in this crate");
188         }
189
190         let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
191             if !attr.check_name("attributes") {
192                 self.handler.span_err(attr.span(), "second argument must be `attributes`")
193             }
194             attr.meta_item_list().unwrap_or_else(|| {
195                 self.handler.span_err(attr.span(),
196                                       "attribute must be of form: \
197                                        `attributes(foo, bar)`");
198                 &[]
199             }).into_iter().filter_map(|attr| {
200                 let name = match attr.name() {
201                     Some(name) => name,
202                     _ => {
203                         self.handler.span_err(attr.span(), "not a meta item");
204                         return None;
205                     },
206                 };
207
208                 if !attr.is_word() {
209                     self.handler.span_err(attr.span(), "must only be one word");
210                     return None;
211                 }
212
213                 Some(name)
214             }).collect()
215         } else {
216             Vec::new()
217         };
218
219         if self.in_root && item.vis == ast::Visibility::Public {
220             self.derives.push(CustomDerive {
221                 span: item.span,
222                 trait_name: trait_name,
223                 function_name: item.ident,
224                 attrs: proc_attrs,
225             });
226         } else {
227             let msg = if !self.in_root {
228                 "functions tagged with `#[proc_macro_derive]` must \
229                  currently reside in the root of the crate"
230             } else {
231                 "functions tagged with `#[proc_macro_derive]` must be `pub`"
232             };
233             self.handler.span_err(item.span, msg);
234         }
235
236         visit::walk_item(self, item);
237     }
238
239     fn visit_mod(&mut self, m: &'a ast::Mod, _s: Span, id: NodeId) {
240         let mut prev_in_root = self.in_root;
241         if id != ast::CRATE_NODE_ID {
242             prev_in_root = mem::replace(&mut self.in_root, false);
243         }
244         visit::walk_mod(self, m);
245         self.in_root = prev_in_root;
246     }
247
248     fn visit_mac(&mut self, mac: &ast::Mac) {
249         visit::walk_mac(self, mac)
250     }
251 }
252
253 // Creates a new module which looks like:
254 //
255 //      mod $gensym {
256 //          extern crate proc_macro;
257 //
258 //          use proc_macro::__internal::Registry;
259 //
260 //          #[plugin_registrar]
261 //          fn registrar(registrar: &mut Registry) {
262 //              registrar.register_custom_derive($name_trait1, ::$name1, &[]);
263 //              registrar.register_custom_derive($name_trait2, ::$name2, &["attribute_name"]);
264 //              // ...
265 //          }
266 //      }
267 fn mk_registrar(cx: &mut ExtCtxt,
268                 custom_derives: &[CustomDerive]) -> P<ast::Item> {
269     let eid = cx.codemap().record_expansion(ExpnInfo {
270         call_site: DUMMY_SP,
271         callee: NameAndSpan {
272             format: MacroAttribute(Symbol::intern("proc_macro")),
273             span: None,
274             allow_internal_unstable: true,
275         }
276     });
277     let span = Span { expn_id: eid, ..DUMMY_SP };
278
279     let proc_macro = Ident::from_str("proc_macro");
280     let krate = cx.item(span,
281                         proc_macro,
282                         Vec::new(),
283                         ast::ItemKind::ExternCrate(None));
284
285     let __internal = Ident::from_str("__internal");
286     let registry = Ident::from_str("Registry");
287     let registrar = Ident::from_str("registrar");
288     let register_custom_derive = Ident::from_str("register_custom_derive");
289     let stmts = custom_derives.iter().map(|cd| {
290         let path = cx.path_global(cd.span, vec![cd.function_name]);
291         let trait_name = cx.expr_str(cd.span, cd.trait_name);
292         let attrs = cx.expr_vec_slice(
293             span,
294             cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::<Vec<_>>()
295         );
296         (path, trait_name, attrs)
297     }).map(|(path, trait_name, attrs)| {
298         let registrar = cx.expr_ident(span, registrar);
299         let ufcs_path = cx.path(span, vec![proc_macro, __internal, registry,
300                                            register_custom_derive]);
301         cx.expr_call(span,
302                      cx.expr_path(ufcs_path),
303                      vec![registrar, trait_name, cx.expr_path(path), attrs])
304     }).map(|expr| {
305         cx.stmt_expr(expr)
306     }).collect::<Vec<_>>();
307
308     let path = cx.path(span, vec![proc_macro, __internal, registry]);
309     let registrar_path = cx.ty_path(path);
310     let arg_ty = cx.ty_rptr(span, registrar_path, None, ast::Mutability::Mutable);
311     let func = cx.item_fn(span,
312                           registrar,
313                           vec![cx.arg(span, registrar, arg_ty)],
314                           cx.ty(span, ast::TyKind::Tup(Vec::new())),
315                           cx.block(span, stmts));
316
317     let derive_registrar = cx.meta_word(span, Symbol::intern("rustc_derive_registrar"));
318     let derive_registrar = cx.attribute(span, derive_registrar);
319     let func = func.map(|mut i| {
320         i.attrs.push(derive_registrar);
321         i.vis = ast::Visibility::Public;
322         i
323     });
324     let ident = ast::Ident::with_empty_ctxt(Symbol::gensym("registrar"));
325     let module = cx.item_mod(span, span, ident, Vec::new(), vec![krate, func]).map(|mut i| {
326         i.vis = ast::Visibility::Public;
327         i
328     });
329
330     cx.monotonic_expander().fold_item(module).pop().unwrap()
331 }