diff options
Diffstat (limited to 'src/libsyntax_ext')
| -rw-r--r-- | src/libsyntax_ext/Cargo.toml | 3 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/clone.rs | 17 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/custom.rs | 97 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/mod.rs | 296 | ||||
| -rw-r--r-- | src/libsyntax_ext/lib.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax_ext/rustc_macro_registrar.rs | 280 |
6 files changed, 565 insertions, 133 deletions
diff --git a/src/libsyntax_ext/Cargo.toml b/src/libsyntax_ext/Cargo.toml index 040c6c8ebff..6910e6400d4 100644 --- a/src/libsyntax_ext/Cargo.toml +++ b/src/libsyntax_ext/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["dylib"] [dependencies] fmt_macros = { path = "../libfmt_macros" } log = { path = "../liblog" } +rustc_errors = { path = "../librustc_errors" } +rustc_macro = { path = "../librustc_macro" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -rustc_errors = { path = "../librustc_errors" } \ No newline at end of file diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index f1a3a1f41b1..c7afaaf4796 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -49,7 +49,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, ItemKind::Struct(_, Generics { ref ty_params, .. }) | ItemKind::Enum(_, Generics { ref ty_params, .. }) if ty_params.is_empty() && - attr::contains_name(&annitem.attrs, "derive_Copy") => { + attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => { bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; unify_fieldless_variants = true; @@ -110,12 +110,12 @@ fn cs_clone(name: &str, Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]), Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]), }; - let subcall = |field: &FieldInfo| { + let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; let span = if mode == Mode::Shallow { // set the expn ID so we can call the unstable method - Span { expn_id: cx.backtrace(), ..trait_span } + super::allow_unstable(cx, field.span, "derive(Clone)") } else { field.span }; @@ -147,8 +147,10 @@ fn cs_clone(name: &str, match mode { Mode::Shallow => { - let mut stmts: Vec<_> = - all_fields.iter().map(subcall).map(|e| cx.stmt_expr(e)).collect(); + let mut stmts = all_fields.iter().map(|f| { + let call = subcall(cx, f); + cx.stmt_expr(call) + }).collect::<Vec<_>>(); stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); cx.expr_block(cx.block(trait_span, stmts)) } @@ -166,14 +168,15 @@ fn cs_clone(name: &str, name)) } }; - cx.field_imm(field.span, ident, subcall(field)) + let call = subcall(cx, field); + cx.field_imm(field.span, ident, call) }) .collect::<Vec<_>>(); cx.expr_struct(trait_span, ctor_path, fields) } VariantData::Tuple(..) => { - let subcalls = all_fields.iter().map(subcall).collect(); + let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); let path = cx.expr_path(ctor_path); cx.expr_call(trait_span, path, subcalls) } diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs new file mode 100644 index 00000000000..1f9c24a0dcd --- /dev/null +++ b/src/libsyntax_ext/deriving/custom.rs @@ -0,0 +1,97 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::panic; + +use rustc_macro::{TokenStream, __internal}; +use syntax::ast::{self, ItemKind}; +use syntax::codemap::Span; +use syntax::ext::base::*; +use syntax::fold::{self, Folder}; +use errors::FatalError; + +pub struct CustomDerive { + inner: fn(TokenStream) -> TokenStream, +} + +impl CustomDerive { + pub fn new(inner: fn(TokenStream) -> TokenStream) -> CustomDerive { + CustomDerive { inner: inner } + } +} + +impl MultiItemModifier for CustomDerive { + fn expand(&self, + ecx: &mut ExtCtxt, + span: Span, + _meta_item: &ast::MetaItem, + item: Annotatable) + -> Vec<Annotatable> { + let item = match item { + Annotatable::Item(item) => item, + Annotatable::ImplItem(_) | + Annotatable::TraitItem(_) => { + ecx.span_err(span, "custom derive attributes may only be \ + applied to struct/enum items"); + return Vec::new() + } + }; + match item.node { + ItemKind::Struct(..) | + ItemKind::Enum(..) => {} + _ => { + ecx.span_err(span, "custom derive attributes may only be \ + applied to struct/enum items"); + return Vec::new() + } + } + + let input = __internal::new_token_stream(item); + let res = __internal::set_parse_sess(&ecx.parse_sess, || { + let inner = self.inner; + panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input))) + }); + let item = match res { + Ok(stream) => __internal::token_stream_items(stream), + Err(e) => { + let msg = "custom derive attribute panicked"; + let mut err = ecx.struct_span_fatal(span, msg); + if let Some(s) = e.downcast_ref::<String>() { + err.help(&format!("message: {}", s)); + } + if let Some(s) = e.downcast_ref::<&'static str>() { + err.help(&format!("message: {}", s)); + } + + err.emit(); + panic!(FatalError); + } + }; + + // Right now we have no knowledge of spans at all in custom derive + // macros, everything is just parsed as a string. Reassign all spans to + // the #[derive] attribute for better errors here. + item.into_iter().flat_map(|item| { + ChangeSpan { span: span }.fold_item(item) + }).map(Annotatable::Item).collect() + } +} + +struct ChangeSpan { span: Span } + +impl Folder for ChangeSpan { + fn new_span(&mut self, _sp: Span) -> Span { + self.span + } + + fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { + fold::noop_fold_mac(mac, self) + } +} diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 81085122e87..5582166c12e 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -12,7 +12,7 @@ use syntax::ast::{self, MetaItem}; use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv}; -use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier}; +use syntax::ext::base::MultiModifier; use syntax::ext::build::AstBuilder; use syntax::feature_gate; use syntax::codemap; @@ -61,6 +61,7 @@ pub mod decodable; pub mod hash; pub mod debug; pub mod default; +pub mod custom; #[path="cmp/partial_eq.rs"] pub mod partial_eq; @@ -74,156 +75,201 @@ pub mod ord; pub mod generic; +fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { + Span { + expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { + call_site: span, + callee: codemap::NameAndSpan { + format: codemap::MacroAttribute(intern(attr_name)), + span: Some(span), + allow_internal_unstable: true, + }, + }), + ..span + } +} + fn expand_derive(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, annotatable: Annotatable) - -> Annotatable { + -> Vec<Annotatable> { debug!("expand_derive: span = {:?}", span); debug!("expand_derive: mitem = {:?}", mitem); debug!("expand_derive: annotatable input = {:?}", annotatable); - let annot = annotatable.map_item_or(|item| { - item.map(|mut item| { - if mitem.value_str().is_some() { - cx.span_err(mitem.span, "unexpected value in `derive`"); - } + let mut item = match annotatable { + Annotatable::Item(item) => item, + other => { + cx.span_err(span, "`derive` can only be applied to items"); + return vec![other] + } + }; - let traits = mitem.meta_item_list().unwrap_or(&[]); - if traits.is_empty() { - cx.span_warn(mitem.span, "empty trait list in `derive`"); - } + if mitem.value_str().is_some() { + cx.span_err(mitem.span, "unexpected value in `derive`"); + } - let mut found_partial_eq = false; - let mut eq_span = None; - - for titem in traits.iter().rev() { - let tname = if let Some(word) = titem.word() { - word.name() - } else { - cx.span_err(titem.span, "malformed `derive` entry"); - continue; - }; - - if !(is_builtin_trait(&tname) || cx.ecfg.enable_custom_derive()) { - feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, - "custom_derive", - titem.span, - feature_gate::GateIssue::Language, - feature_gate::EXPLAIN_CUSTOM_DERIVE); - continue; - } + let traits = mitem.meta_item_list().unwrap_or(&[]); + if traits.is_empty() { + cx.span_warn(mitem.span, "empty trait list in `derive`"); + } - let span = Span { - expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { - call_site: titem.span, - callee: codemap::NameAndSpan { - format: codemap::MacroAttribute(intern(&format!("derive({})", tname))), - span: Some(titem.span), - allow_internal_unstable: true, - }, - }), - ..titem.span - }; - - if &tname[..] == "Eq" { - eq_span = Some(span); - } else if &tname[..] == "PartialEq" { - found_partial_eq = true; - } + // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted) + // `#[structural_match]` attribute. + if traits.iter().filter_map(|t| t.name()).any(|t| t == "PartialEq") && + traits.iter().filter_map(|t| t.name()).any(|t| t == "Eq") { + let structural_match = intern_and_get_ident("structural_match"); + let span = allow_unstable(cx, span, "derive(PartialEq, Eq)"); + let meta = cx.meta_word(span, structural_match); + item = item.map(|mut i| { + i.attrs.push(cx.attribute(span, meta)); + i + }); + } - // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar] - item.attrs.push(cx.attribute(span, - cx.meta_word(titem.span, - intern_and_get_ident(&format!("derive_{}", tname))))); - } + // RFC #1521. `Clone` can assume that `Copy` types' clone implementation is + // the same as the copy implementation. + // + // Add a marker attribute here picked up during #[derive(Clone)] + if traits.iter().filter_map(|t| t.name()).any(|t| t == "Clone") && + traits.iter().filter_map(|t| t.name()).any(|t| t == "Copy") { + let marker = intern_and_get_ident("rustc_copy_clone_marker"); + let span = allow_unstable(cx, span, "derive(Copy, Clone)"); + let meta = cx.meta_word(span, marker); + item = item.map(|mut i| { + i.attrs.push(cx.attribute(span, meta)); + i + }); + } - // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted) - // `#[structural_match]` attribute. - if let Some(eq_span) = eq_span { - if found_partial_eq { - let structural_match = intern_and_get_ident("structural_match"); - item.attrs.push(cx.attribute(eq_span, cx.meta_word(eq_span, structural_match))); - } + let mut other_items = Vec::new(); + + let mut iter = traits.iter(); + while let Some(titem) = iter.next() { + + let tword = match titem.word() { + Some(name) => name, + None => { + cx.span_err(titem.span, "malformed `derive` entry"); + continue } + }; + let tname = tword.name(); + + // If this is a built-in derive mode, then we expand it immediately + // here. + if is_builtin_trait(&tname) { + let name = intern_and_get_ident(&format!("derive({})", tname)); + let mitem = cx.meta_word(titem.span, name); + + let span = Span { + expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { + call_site: titem.span, + callee: codemap::NameAndSpan { + format: codemap::MacroAttribute(intern(&format!("derive({})", tname))), + span: Some(titem.span), + allow_internal_unstable: true, + }, + }), + ..titem.span + }; + + let my_item = Annotatable::Item(item); + expand_builtin(&tname, cx, span, &mitem, &my_item, &mut |a| { + other_items.push(a); + }); + item = my_item.expect_item(); + + // Otherwise if this is a `rustc_macro`-style derive mode, we process it + // here. The logic here is to: + // + // 1. Collect the remaining `#[derive]` annotations into a list. If + // there are any left, attach a `#[derive]` attribute to the item + // that we're currently expanding with the remaining derive modes. + // 2. Manufacture a `#[derive(Foo)]` attribute to pass to the expander. + // 3. Expand the current item we're expanding, getting back a list of + // items that replace it. + // 4. Extend the returned list with the current list of items we've + // collected so far. + // 5. Return everything! + // + // If custom derive extensions end up threading through the `#[derive]` + // attribute, we'll get called again later on to continue expanding + // those modes. + } else if let Some(ext) = cx.derive_modes.remove(&tname) { + let remaining_derives = iter.cloned().collect::<Vec<_>>(); + if remaining_derives.len() > 0 { + let list = cx.meta_list(titem.span, + intern_and_get_ident("derive"), + remaining_derives); + let attr = cx.attribute(titem.span, list); + item = item.map(|mut i| { + i.attrs.push(attr); + i + }); + } + let titem = cx.meta_list_item_word(titem.span, tname.clone()); + let mitem = cx.meta_list(titem.span, + intern_and_get_ident("derive"), + vec![titem]); + let item = Annotatable::Item(item); + let mut items = ext.expand(cx, mitem.span, &mitem, item); + items.extend(other_items); + cx.derive_modes.insert(tname.clone(), ext); + return items + + // If we've gotten this far then it means that we're in the territory of + // the old custom derive mechanism. If the feature isn't enabled, we + // issue an error, otherwise manufacture the `derive_Foo` attribute. + } else if !cx.ecfg.enable_custom_derive() { + feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, + "custom_derive", + titem.span, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_CUSTOM_DERIVE); + } else { + let name = intern_and_get_ident(&format!("derive_{}", tname)); + let mitem = cx.meta_word(titem.span, name); + item = item.map(|mut i| { + i.attrs.push(cx.attribute(mitem.span, mitem)); + i + }); + } + } - item - }) - }, - |a| { - cx.span_err(span, - "`derive` can only be applied to items"); - a - }); - debug!("expand_derive: annotatable output = {:?}", annot); - annot + other_items.insert(0, Annotatable::Item(item)); + return other_items } macro_rules! derive_traits { ($( $name:expr => $func:path, )+) => { pub fn register_all(env: &mut SyntaxEnv) { - // Define the #[derive_*] extensions. - $({ - struct DeriveExtension; - - impl MultiItemDecorator for DeriveExtension { - fn expand(&self, - ecx: &mut ExtCtxt, - sp: Span, - mitem: &MetaItem, - annotatable: &Annotatable, - push: &mut FnMut(Annotatable)) { - if !ecx.parse_sess.codemap().span_allows_unstable(sp) - && !ecx.ecfg.features.unwrap().custom_derive { - // FIXME: - // https://github.com/rust-lang/rust/pull/32671#issuecomment-206245303 - // This is just to avoid breakage with syntex. - // Remove that to spawn an error instead. - let cm = ecx.parse_sess.codemap(); - let parent = cm.with_expn_info(ecx.backtrace(), - |info| info.unwrap().call_site.expn_id); - cm.with_expn_info(parent, |info| { - if info.is_some() { - let mut w = ecx.parse_sess.span_diagnostic.struct_span_warn( - sp, feature_gate::EXPLAIN_DERIVE_UNDERSCORE, - ); - if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_none() { - w.help( - &format!("add #![feature(custom_derive)] to \ - the crate attributes to enable") - ); - } - w.emit(); - } else { - feature_gate::emit_feature_err( - &ecx.parse_sess.span_diagnostic, - "custom_derive", sp, feature_gate::GateIssue::Language, - feature_gate::EXPLAIN_DERIVE_UNDERSCORE - ); - - return; - } - }) - } - - warn_if_deprecated(ecx, sp, $name); - $func(ecx, sp, mitem, annotatable, push); - } - } - - env.insert(intern(concat!("derive_", $name)), - MultiDecorator(Box::new(DeriveExtension))); - })+ - - env.insert(intern("derive"), - MultiModifier(Box::new(expand_derive))); + env.insert(intern("derive"), MultiModifier(Box::new(expand_derive))); } - fn is_builtin_trait(name: &str) -> bool { + pub fn is_builtin_trait(name: &str) -> bool { match name { $( $name )|+ => true, _ => false, } } + + fn expand_builtin(name: &str, + ecx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) { + match name { + $( + $name => { + warn_if_deprecated(ecx, span, $name); + $func(ecx, span, mitem, item, push); + } + )* + _ => panic!("not a builtin derive mode: {}", name), + } + } } } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 3aa62339b47..4bae9ec5a1a 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -19,6 +19,8 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![cfg_attr(not(stage0), deny(warnings))] +#![feature(rustc_macro_lib)] +#![feature(rustc_macro_internals)] #![feature(rustc_private)] #![feature(staged_api)] @@ -28,6 +30,7 @@ extern crate log; #[macro_use] extern crate syntax; extern crate syntax_pos; +extern crate rustc_macro; extern crate rustc_errors as errors; use syntax::ext::base::{MacroExpanderFn, NormalTT}; @@ -44,6 +47,8 @@ mod format; mod log_syntax; mod trace_macros; +pub mod rustc_macro_registrar; + // for custom_derive pub mod deriving; diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs new file mode 100644 index 00000000000..7693e2416f4 --- /dev/null +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -0,0 +1,280 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mem; + +use errors; +use syntax::ast::{self, Ident, NodeId}; +use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; +use syntax::ext::base::{ExtCtxt, DummyMacroLoader}; +use syntax::ext::build::AstBuilder; +use syntax::ext::expand::ExpansionConfig; +use syntax::parse::ParseSess; +use syntax::parse::token::{self, InternedString}; +use syntax::feature_gate::Features; +use syntax::ptr::P; +use syntax_pos::{Span, DUMMY_SP}; +use syntax::visit::{self, Visitor}; + +use deriving; + +struct CustomDerive { + trait_name: InternedString, + function_name: Ident, + span: Span, +} + +struct CollectCustomDerives<'a> { + derives: Vec<CustomDerive>, + in_root: bool, + handler: &'a errors::Handler, + is_rustc_macro_crate: bool, +} + +pub fn modify(sess: &ParseSess, + mut krate: ast::Crate, + is_rustc_macro_crate: bool, + num_crate_types: usize, + handler: &errors::Handler, + features: &Features) -> ast::Crate { + let mut loader = DummyMacroLoader; + let mut cx = ExtCtxt::new(sess, + Vec::new(), + ExpansionConfig::default("rustc_macro".to_string()), + &mut loader); + + let mut collect = CollectCustomDerives { + derives: Vec::new(), + in_root: true, + handler: handler, + is_rustc_macro_crate: is_rustc_macro_crate, + }; + visit::walk_crate(&mut collect, &krate); + + if !is_rustc_macro_crate { + return krate + } else if !features.rustc_macro { + let mut err = handler.struct_err("the `rustc-macro` crate type is \ + experimental"); + err.help("add #![feature(rustc_macro)] to the crate attributes to \ + enable"); + err.emit(); + } + + if num_crate_types > 1 { + handler.err("cannot mix `rustc-macro` crate type with others"); + } + + krate.module.items.push(mk_registrar(&mut cx, &collect.derives)); + + if krate.exported_macros.len() > 0 { + handler.err("cannot export macro_rules! macros from a `rustc-macro` \ + crate type currently"); + } + + return krate +} + +impl<'a> CollectCustomDerives<'a> { + fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { + if self.is_rustc_macro_crate && + self.in_root && + *vis == ast::Visibility::Public { + self.handler.span_err(sp, + "`rustc-macro` crate types cannot \ + export any items other than functions \ + tagged with `#[rustc_macro_derive]` \ + currently"); + } + } +} + +impl<'a> Visitor for CollectCustomDerives<'a> { + fn visit_item(&mut self, item: &ast::Item) { + // First up, make sure we're checking a bare function. If we're not then + // we're just not interested in this item. + // + // If we find one, try to locate a `#[rustc_macro_derive]` attribute on + // it. + match item.node { + ast::ItemKind::Fn(..) => {} + _ => { + self.check_not_pub_in_root(&item.vis, item.span); + return visit::walk_item(self, item) + } + } + + let mut attrs = item.attrs.iter() + .filter(|a| a.check_name("rustc_macro_derive")); + let attr = match attrs.next() { + Some(attr) => attr, + None => { + self.check_not_pub_in_root(&item.vis, item.span); + return visit::walk_item(self, item) + } + }; + + if let Some(a) = attrs.next() { + self.handler.span_err(a.span(), "multiple `#[rustc_macro_derive]` \ + attributes found"); + } + + if !self.is_rustc_macro_crate { + self.handler.span_err(attr.span(), + "the `#[rustc_macro_derive]` attribute is \ + only usable with crates of the `rustc-macro` \ + crate type"); + } + + // Once we've located the `#[rustc_macro_derive]` attribute, verify + // that it's of the form `#[rustc_macro_derive(Foo)]` + let list = match attr.meta_item_list() { + Some(list) => list, + None => { + self.handler.span_err(attr.span(), + "attribute must be of form: \ + #[rustc_macro_derive(TraitName)]"); + return + } + }; + if list.len() != 1 { + self.handler.span_err(attr.span(), + "attribute must only have one argument"); + return + } + let attr = &list[0]; + let trait_name = match attr.name() { + Some(name) => name, + _ => { + self.handler.span_err(attr.span(), "not a meta item"); + return + } + }; + if !attr.is_word() { + self.handler.span_err(attr.span(), "must only be one word"); + } + + if deriving::is_builtin_trait(&trait_name) { + self.handler.span_err(attr.span(), + "cannot override a built-in #[derive] mode"); + } + + if self.derives.iter().any(|d| d.trait_name == trait_name) { + self.handler.span_err(attr.span(), + "derive mode defined twice in this crate"); + } + + if self.in_root { + self.derives.push(CustomDerive { + span: item.span, + trait_name: trait_name, + function_name: item.ident, + }); + } else { + let msg = "functions tagged with `#[rustc_macro_derive]` must \ + currently reside in the root of the crate"; + self.handler.span_err(item.span, msg); + } + + visit::walk_item(self, item); + } + + fn visit_mod(&mut self, m: &ast::Mod, _s: Span, id: NodeId) { + let mut prev_in_root = self.in_root; + if id != ast::CRATE_NODE_ID { + prev_in_root = mem::replace(&mut self.in_root, false); + } + visit::walk_mod(self, m); + self.in_root = prev_in_root; + } + + fn visit_mac(&mut self, mac: &ast::Mac) { + visit::walk_mac(self, mac) + } +} + +// Creates a new module which looks like: +// +// mod $gensym { +// extern crate rustc_macro; +// +// use rustc_macro::__internal::Registry; +// +// #[plugin_registrar] +// fn registrar(registrar: &mut Registry) { +// registrar.register_custom_derive($name_trait1, ::$name1); +// registrar.register_custom_derive($name_trait2, ::$name2); +// // ... +// } +// } +fn mk_registrar(cx: &mut ExtCtxt, + custom_derives: &[CustomDerive]) -> P<ast::Item> { + let eid = cx.codemap().record_expansion(ExpnInfo { + call_site: DUMMY_SP, + callee: NameAndSpan { + format: MacroAttribute(token::intern("rustc_macro")), + span: None, + allow_internal_unstable: true, + } + }); + let span = Span { expn_id: eid, ..DUMMY_SP }; + + let rustc_macro = token::str_to_ident("rustc_macro"); + let krate = cx.item(span, + rustc_macro, + Vec::new(), + ast::ItemKind::ExternCrate(None)); + + let __internal = token::str_to_ident("__internal"); + let registry = token::str_to_ident("Registry"); + let registrar = token::str_to_ident("registrar"); + let register_custom_derive = token::str_to_ident("register_custom_derive"); + let stmts = custom_derives.iter().map(|cd| { + let path = cx.path_global(cd.span, vec![cd.function_name]); + let trait_name = cx.expr_str(cd.span, cd.trait_name.clone()); + (path, trait_name) + }).map(|(path, trait_name)| { + let registrar = cx.expr_ident(span, registrar); + let ufcs_path = cx.path(span, vec![rustc_macro, __internal, registry, + register_custom_derive]); + cx.expr_call(span, + cx.expr_path(ufcs_path), + vec![registrar, trait_name, cx.expr_path(path)]) + }).map(|expr| { + cx.stmt_expr(expr) + }).collect::<Vec<_>>(); + + let path = cx.path(span, vec![rustc_macro, __internal, registry]); + let registrar_path = cx.ty_path(path); + let arg_ty = cx.ty_rptr(span, registrar_path, None, ast::Mutability::Mutable); + let func = cx.item_fn(span, + registrar, + vec![cx.arg(span, registrar, arg_ty)], + cx.ty(span, ast::TyKind::Tup(Vec::new())), + cx.block(span, stmts)); + + let derive_registrar = token::intern_and_get_ident("rustc_derive_registrar"); + let derive_registrar = cx.meta_word(span, derive_registrar); + let derive_registrar = cx.attribute(span, derive_registrar); + let func = func.map(|mut i| { + i.attrs.push(derive_registrar); + i.vis = ast::Visibility::Public; + i + }); + let module = cx.item_mod(span, + span, + ast::Ident::with_empty_ctxt(token::gensym("registrar")), + Vec::new(), + vec![krate, func]); + module.map(|mut i| { + i.vis = ast::Visibility::Public; + i + }) +} |
