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.
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.
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;
22 use syntax::symbol::Symbol;
23 use syntax_pos::{Span, DUMMY_SP};
24 use syntax::visit::{self, Visitor};
29 trait_name: ast::Name,
32 attrs: Vec<ast::Name>,
35 struct CollectCustomDerives<'a> {
36 derives: Vec<CustomDerive>,
38 handler: &'a errors::Handler,
39 is_proc_macro_crate: bool,
43 pub fn modify(sess: &ParseSess,
44 resolver: &mut ::syntax::ext::base::Resolver,
45 mut krate: ast::Crate,
46 is_proc_macro_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);
54 let mut collect = CollectCustomDerives {
58 is_proc_macro_crate: is_proc_macro_crate,
59 is_test_crate: is_test_crate,
61 visit::walk_crate(&mut collect, &krate);
65 if !is_proc_macro_crate {
69 if num_crate_types > 1 {
70 handler.err("cannot mix `proc-macro` crate type with others");
77 krate.module.items.push(mk_registrar(&mut cx, &derives));
79 if krate.exported_macros.len() > 0 {
80 handler.err("cannot export macro_rules! macros from a `proc-macro` \
81 crate type currently");
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 &&
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]` \
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"));
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.
108 // If we find one, try to locate a `#[proc_macro_derive]` attribute on
111 ast::ItemKind::Fn(..) => {}
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 \
121 self.check_not_pub_in_root(&item.vis, item.span);
122 return visit::walk_item(self, item)
126 let attr = match attrs.next() {
129 self.check_not_pub_in_root(&item.vis, item.span);
130 return visit::walk_item(self, item)
134 if let Some(a) = attrs.next() {
135 self.handler.span_err(a.span(), "multiple `#[proc_macro_derive]` \
139 if self.is_test_crate {
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` \
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() {
156 self.handler.span_err(attr.span(),
157 "attribute must be of form: \
158 #[proc_macro_derive(TraitName)]");
162 if list.len() != 1 && list.len() != 2 {
163 self.handler.span_err(attr.span(),
164 "attribute must have either one or two arguments");
167 let trait_attr = &list[0];
168 let attributes_attr = list.get(1);
169 let trait_name = match trait_attr.name() {
172 self.handler.span_err(trait_attr.span(), "not a meta item");
176 if !trait_attr.is_word() {
177 self.handler.span_err(trait_attr.span(), "must only be one word");
180 if deriving::is_builtin_trait(trait_name) {
181 self.handler.span_err(trait_attr.span(),
182 "cannot override a built-in #[derive] mode");
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");
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`")
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)`");
199 }).into_iter().filter_map(|attr| {
200 let name = match attr.name() {
203 self.handler.span_err(attr.span(), "not a meta item");
209 self.handler.span_err(attr.span(), "must only be one word");
219 if self.in_root && item.vis == ast::Visibility::Public {
220 self.derives.push(CustomDerive {
222 trait_name: trait_name,
223 function_name: item.ident,
227 let msg = if !self.in_root {
228 "functions tagged with `#[proc_macro_derive]` must \
229 currently reside in the root of the crate"
231 "functions tagged with `#[proc_macro_derive]` must be `pub`"
233 self.handler.span_err(item.span, msg);
236 visit::walk_item(self, item);
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);
244 visit::walk_mod(self, m);
245 self.in_root = prev_in_root;
248 fn visit_mac(&mut self, mac: &ast::Mac) {
249 visit::walk_mac(self, mac)
253 // Creates a new module which looks like:
256 // extern crate proc_macro;
258 // use proc_macro::__internal::Registry;
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"]);
267 fn mk_registrar(cx: &mut ExtCtxt,
268 custom_derives: &[CustomDerive]) -> P<ast::Item> {
269 let eid = cx.codemap().record_expansion(ExpnInfo {
271 callee: NameAndSpan {
272 format: MacroAttribute(Symbol::intern("proc_macro")),
274 allow_internal_unstable: true,
277 let span = Span { expn_id: eid, ..DUMMY_SP };
279 let proc_macro = Ident::from_str("proc_macro");
280 let krate = cx.item(span,
283 ast::ItemKind::ExternCrate(None));
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(
294 cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::<Vec<_>>()
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]);
302 cx.expr_path(ufcs_path),
303 vec![registrar, trait_name, cx.expr_path(path), attrs])
306 }).collect::<Vec<_>>();
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,
313 vec![cx.arg(span, registrar, arg_ty)],
314 cx.ty(span, ast::TyKind::Tup(Vec::new())),
315 cx.block(span, stmts));
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;
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;
330 cx.monotonic_expander().fold_item(module).pop().unwrap()