]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/proc_macro_registrar.rs
add inline attributes to stage 0 methods
[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
15 use syntax::ast::{self, Ident, NodeId};
16 use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute};
17 use syntax::ext::base::ExtCtxt;
18 use syntax::ext::build::AstBuilder;
19 use syntax::ext::expand::ExpansionConfig;
20 use syntax::fold::Folder;
21 use syntax::parse::ParseSess;
22 use syntax::ptr::P;
23 use syntax::symbol::Symbol;
24 use syntax::visit::{self, Visitor};
25
26 use syntax_pos::{Span, DUMMY_SP};
27
28 use deriving;
29
30 const PROC_MACRO_KINDS: [&'static str; 3] =
31     ["proc_macro_derive", "proc_macro_attribute", "proc_macro"];
32
33 struct ProcMacroDerive {
34     trait_name: ast::Name,
35     function_name: Ident,
36     span: Span,
37     attrs: Vec<ast::Name>,
38 }
39
40 struct ProcMacroDef {
41     function_name: Ident,
42     span: Span,
43 }
44
45 struct CollectProcMacros<'a> {
46     derives: Vec<ProcMacroDerive>,
47     attr_macros: Vec<ProcMacroDef>,
48     bang_macros: Vec<ProcMacroDef>,
49     in_root: bool,
50     handler: &'a errors::Handler,
51     is_proc_macro_crate: bool,
52     is_test_crate: bool,
53 }
54
55 pub fn modify(sess: &ParseSess,
56               resolver: &mut ::syntax::ext::base::Resolver,
57               mut krate: ast::Crate,
58               is_proc_macro_crate: bool,
59               is_test_crate: bool,
60               num_crate_types: usize,
61               handler: &errors::Handler) -> ast::Crate {
62     let ecfg = ExpansionConfig::default("proc_macro".to_string());
63     let mut cx = ExtCtxt::new(sess, ecfg, resolver);
64
65     let (derives, attr_macros, bang_macros) = {
66         let mut collect = CollectProcMacros {
67             derives: Vec::new(),
68             attr_macros: Vec::new(),
69             bang_macros: Vec::new(),
70             in_root: true,
71             handler: handler,
72             is_proc_macro_crate: is_proc_macro_crate,
73             is_test_crate: is_test_crate,
74         };
75         visit::walk_crate(&mut collect, &krate);
76         (collect.derives, collect.attr_macros, collect.bang_macros)
77     };
78
79     if !is_proc_macro_crate {
80         return krate
81     }
82
83     if num_crate_types > 1 {
84         handler.err("cannot mix `proc-macro` crate type with others");
85     }
86
87     if is_test_crate {
88         return krate;
89     }
90
91     krate.module.items.push(mk_registrar(&mut cx, &derives, &attr_macros, &bang_macros));
92
93     krate
94 }
95
96 fn is_proc_macro_attr(attr: &ast::Attribute) -> bool {
97     PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(kind))
98 }
99
100 impl<'a> CollectProcMacros<'a> {
101     fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
102         if self.is_proc_macro_crate &&
103            self.in_root &&
104            *vis == ast::Visibility::Public {
105             self.handler.span_err(sp,
106                                   "`proc-macro` crate types cannot \
107                                    export any items other than functions \
108                                    tagged with `#[proc_macro_derive]` currently");
109         }
110     }
111
112     fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
113         // Once we've located the `#[proc_macro_derive]` attribute, verify
114         // that it's of the form `#[proc_macro_derive(Foo)]` or
115         // `#[proc_macro_derive(Foo, attributes(A, ..))]`
116         let list = match attr.meta_item_list() {
117             Some(list) => list,
118             None => {
119                 self.handler.span_err(attr.span(),
120                                       "attribute must be of form: \
121                                        #[proc_macro_derive(TraitName)]");
122                 return
123             }
124         };
125         if list.len() != 1 && list.len() != 2 {
126             self.handler.span_err(attr.span(),
127                                   "attribute must have either one or two arguments");
128             return
129         }
130         let trait_attr = &list[0];
131         let attributes_attr = list.get(1);
132         let trait_name = match trait_attr.name() {
133             Some(name) => name,
134             _ => {
135                 self.handler.span_err(trait_attr.span(), "not a meta item");
136                 return
137             }
138         };
139         if !trait_attr.is_word() {
140             self.handler.span_err(trait_attr.span(), "must only be one word");
141         }
142
143         if deriving::is_builtin_trait(trait_name) {
144             self.handler.span_err(trait_attr.span(),
145                                   "cannot override a built-in #[derive] mode");
146         }
147
148         if self.derives.iter().any(|d| d.trait_name == trait_name) {
149             self.handler.span_err(trait_attr.span(),
150                                   "derive mode defined twice in this crate");
151         }
152
153         let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
154             if !attr.check_name("attributes") {
155                 self.handler.span_err(attr.span(), "second argument must be `attributes`")
156             }
157             attr.meta_item_list().unwrap_or_else(|| {
158                 self.handler.span_err(attr.span(),
159                                       "attribute must be of form: \
160                                        `attributes(foo, bar)`");
161                 &[]
162             }).into_iter().filter_map(|attr| {
163                 let name = match attr.name() {
164                     Some(name) => name,
165                     _ => {
166                         self.handler.span_err(attr.span(), "not a meta item");
167                         return None;
168                     },
169                 };
170
171                 if !attr.is_word() {
172                     self.handler.span_err(attr.span(), "must only be one word");
173                     return None;
174                 }
175
176                 Some(name)
177             }).collect()
178         } else {
179             Vec::new()
180         };
181
182         if self.in_root && item.vis == ast::Visibility::Public {
183             self.derives.push(ProcMacroDerive {
184                 span: item.span,
185                 trait_name: trait_name,
186                 function_name: item.ident,
187                 attrs: proc_attrs,
188             });
189         } else {
190             let msg = if !self.in_root {
191                 "functions tagged with `#[proc_macro_derive]` must \
192                  currently reside in the root of the crate"
193             } else {
194                 "functions tagged with `#[proc_macro_derive]` must be `pub`"
195             };
196             self.handler.span_err(item.span, msg);
197         }
198     }
199
200     fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
201         if let Some(_) = attr.meta_item_list() {
202             self.handler.span_err(attr.span, "`#[proc_macro_attribute]` attribute
203                 does not take any arguments");
204             return;
205         }
206
207         if self.in_root && item.vis == ast::Visibility::Public {
208             self.attr_macros.push(ProcMacroDef {
209                 span: item.span,
210                 function_name: item.ident,
211             });
212         } else {
213             let msg = if !self.in_root {
214                 "functions tagged with `#[proc_macro_attribute]` must \
215                  currently reside in the root of the crate"
216             } else {
217                 "functions tagged with `#[proc_macro_attribute]` must be `pub`"
218             };
219             self.handler.span_err(item.span, msg);
220         }
221     }
222
223     fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
224         if let Some(_) = attr.meta_item_list() {
225             self.handler.span_err(attr.span, "`#[proc_macro]` attribute
226                 does not take any arguments");
227             return;
228         }
229
230         if self.in_root && item.vis == ast::Visibility::Public {
231             self.bang_macros.push(ProcMacroDef {
232                 span: item.span,
233                 function_name: item.ident,
234             });
235         } else {
236             let msg = if !self.in_root {
237                 "functions tagged with `#[proc_macro]` must \
238                  currently reside in the root of the crate"
239             } else {
240                 "functions tagged with `#[proc_macro]` must be `pub`"
241             };
242             self.handler.span_err(item.span, msg);
243         }
244     }
245 }
246
247 impl<'a> Visitor<'a> for CollectProcMacros<'a> {
248     fn visit_item(&mut self, item: &'a ast::Item) {
249         if let ast::ItemKind::MacroDef(..) = item.node {
250             if self.is_proc_macro_crate &&
251                item.attrs.iter().any(|attr| attr.name() == "macro_export") {
252                 let msg =
253                     "cannot export macro_rules! macros from a `proc-macro` crate type currently";
254                 self.handler.span_err(item.span, msg);
255             }
256         }
257
258         // First up, make sure we're checking a bare function. If we're not then
259         // we're just not interested in this item.
260         //
261         // If we find one, try to locate a `#[proc_macro_derive]` attribute on
262         // it.
263         let is_fn = match item.node {
264             ast::ItemKind::Fn(..) => true,
265             _ => false,
266         };
267
268         let mut found_attr: Option<&'a ast::Attribute> = None;
269
270         for attr in &item.attrs {
271             if is_proc_macro_attr(&attr) {
272                 if let Some(prev_attr) = found_attr {
273                     let msg = if attr.name() == prev_attr.name() {
274                         format!("Only one `#[{}]` attribute is allowed on any given function",
275                                 attr.name())
276                     } else {
277                         format!("`#[{}]` and `#[{}]` attributes cannot both be applied \
278                                 to the same function", attr.name(), prev_attr.name())
279                     };
280
281                     self.handler.struct_span_err(attr.span(), &msg)
282                         .span_note(prev_attr.span(), "Previous attribute here")
283                         .emit();
284
285                     return;
286                 }
287
288                 found_attr = Some(attr);
289             }
290         }
291
292         let attr = match found_attr {
293             None => {
294                 self.check_not_pub_in_root(&item.vis, item.span);
295                 return visit::walk_item(self, item);
296             },
297             Some(attr) => attr,
298         };
299
300         if !is_fn {
301             let msg = format!("the `#[{}]` attribute may only be used on bare functions",
302                               attr.name());
303
304             self.handler.span_err(attr.span(), &msg);
305             return;
306         }
307
308         if self.is_test_crate {
309             return;
310         }
311
312         if !self.is_proc_macro_crate {
313             let msg = format!("the `#[{}]` attribute is only usable with crates of the \
314                               `proc-macro` crate type", attr.name());
315
316             self.handler.span_err(attr.span(), &msg);
317             return;
318         }
319
320         if attr.check_name("proc_macro_derive") {
321             self.collect_custom_derive(item, attr);
322         } else if attr.check_name("proc_macro_attribute") {
323             self.collect_attr_proc_macro(item, attr);
324         } else if attr.check_name("proc_macro") {
325             self.collect_bang_proc_macro(item, attr);
326         };
327
328         visit::walk_item(self, item);
329     }
330
331     fn visit_mod(&mut self, m: &'a ast::Mod, _s: Span, id: NodeId) {
332         let mut prev_in_root = self.in_root;
333         if id != ast::CRATE_NODE_ID {
334             prev_in_root = mem::replace(&mut self.in_root, false);
335         }
336         visit::walk_mod(self, m);
337         self.in_root = prev_in_root;
338     }
339
340     fn visit_mac(&mut self, mac: &ast::Mac) {
341         visit::walk_mac(self, mac)
342     }
343 }
344
345 // Creates a new module which looks like:
346 //
347 //      mod $gensym {
348 //          extern crate proc_macro;
349 //
350 //          use proc_macro::__internal::Registry;
351 //
352 //          #[plugin_registrar]
353 //          fn registrar(registrar: &mut Registry) {
354 //              registrar.register_custom_derive($name_trait1, ::$name1, &[]);
355 //              registrar.register_custom_derive($name_trait2, ::$name2, &["attribute_name"]);
356 //              // ...
357 //          }
358 //      }
359 fn mk_registrar(cx: &mut ExtCtxt,
360                 custom_derives: &[ProcMacroDerive],
361                 custom_attrs: &[ProcMacroDef],
362                 custom_macros: &[ProcMacroDef]) -> P<ast::Item> {
363     let eid = cx.codemap().record_expansion(ExpnInfo {
364         call_site: DUMMY_SP,
365         callee: NameAndSpan {
366             format: MacroAttribute(Symbol::intern("proc_macro")),
367             span: None,
368             allow_internal_unstable: true,
369         }
370     });
371     let span = Span { expn_id: eid, ..DUMMY_SP };
372
373     let proc_macro = Ident::from_str("proc_macro");
374     let krate = cx.item(span,
375                         proc_macro,
376                         Vec::new(),
377                         ast::ItemKind::ExternCrate(None));
378
379     let __internal = Ident::from_str("__internal");
380     let registry = Ident::from_str("Registry");
381     let registrar = Ident::from_str("registrar");
382     let register_custom_derive = Ident::from_str("register_custom_derive");
383     let register_attr_proc_macro = Ident::from_str("register_attr_proc_macro");
384     let register_bang_proc_macro = Ident::from_str("register_bang_proc_macro");
385
386     let mut stmts = custom_derives.iter().map(|cd| {
387         let path = cx.path_global(cd.span, vec![cd.function_name]);
388         let trait_name = cx.expr_str(cd.span, cd.trait_name);
389         let attrs = cx.expr_vec_slice(
390             span,
391             cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::<Vec<_>>()
392         );
393         let registrar = cx.expr_ident(span, registrar);
394         let ufcs_path = cx.path(span, vec![proc_macro, __internal, registry,
395                                            register_custom_derive]);
396
397         cx.stmt_expr(cx.expr_call(span, cx.expr_path(ufcs_path),
398                                   vec![registrar, trait_name, cx.expr_path(path), attrs]))
399
400     }).collect::<Vec<_>>();
401
402     stmts.extend(custom_attrs.iter().map(|ca| {
403         let name = cx.expr_str(ca.span, ca.function_name.name);
404         let path = cx.path_global(ca.span, vec![ca.function_name]);
405         let registrar = cx.expr_ident(ca.span, registrar);
406
407         let ufcs_path = cx.path(span,
408                                 vec![proc_macro, __internal, registry, register_attr_proc_macro]);
409
410         cx.stmt_expr(cx.expr_call(span, cx.expr_path(ufcs_path),
411                                   vec![registrar, name, cx.expr_path(path)]))
412     }));
413
414     stmts.extend(custom_macros.iter().map(|cm| {
415         let name = cx.expr_str(cm.span, cm.function_name.name);
416         let path = cx.path_global(cm.span, vec![cm.function_name]);
417         let registrar = cx.expr_ident(cm.span, registrar);
418
419         let ufcs_path = cx.path(span,
420                                 vec![proc_macro, __internal, registry, register_bang_proc_macro]);
421
422         cx.stmt_expr(cx.expr_call(span, cx.expr_path(ufcs_path),
423                                   vec![registrar, name, cx.expr_path(path)]))
424     }));
425
426     let path = cx.path(span, vec![proc_macro, __internal, registry]);
427     let registrar_path = cx.ty_path(path);
428     let arg_ty = cx.ty_rptr(span, registrar_path, None, ast::Mutability::Mutable);
429     let func = cx.item_fn(span,
430                           registrar,
431                           vec![cx.arg(span, registrar, arg_ty)],
432                           cx.ty(span, ast::TyKind::Tup(Vec::new())),
433                           cx.block(span, stmts));
434
435     let derive_registrar = cx.meta_word(span, Symbol::intern("rustc_derive_registrar"));
436     let derive_registrar = cx.attribute(span, derive_registrar);
437     let func = func.map(|mut i| {
438         i.attrs.push(derive_registrar);
439         i.vis = ast::Visibility::Public;
440         i
441     });
442     let ident = ast::Ident::with_empty_ctxt(Symbol::gensym("registrar"));
443     let module = cx.item_mod(span, span, ident, Vec::new(), vec![krate, func]).map(|mut i| {
444         i.vis = ast::Visibility::Public;
445         i
446     });
447
448     cx.monotonic_expander().fold_item(module).pop().unwrap()
449 }