From 4d535bdf59136f69b55107caaa0f5492b5e84d2d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 Jul 2019 22:29:07 +0300 Subject: Move standard library injection into libsyntax_ext --- src/libsyntax/parse/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 225065c1cf1..1aac8bbb7aa 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -10,9 +10,10 @@ use crate::parse::token::TokenKind; use crate::tokenstream::{TokenStream, TokenTree}; use crate::diagnostics::plugin::ErrorMap; use crate::print::pprust; +use crate::symbol::Symbol; use errors::{Applicability, FatalError, Level, Handler, ColorConfig, Diagnostic, DiagnosticBuilder}; -use rustc_data_structures::sync::{Lrc, Lock}; +use rustc_data_structures::sync::{Lrc, Lock, Once}; use syntax_pos::{Span, SourceFile, FileName, MultiSpan}; use syntax_pos::edition::Edition; @@ -58,6 +59,7 @@ pub struct ParseSess { pub let_chains_spans: Lock>, // Places where `async || ..` exprs were used and should be feature gated. pub async_closure_spans: Lock>, + pub injected_crate_name: Once, } impl ParseSess { @@ -86,6 +88,7 @@ impl ParseSess { param_attr_spans: Lock::new(Vec::new()), let_chains_spans: Lock::new(Vec::new()), async_closure_spans: Lock::new(Vec::new()), + injected_crate_name: Once::new(), } } -- cgit 1.4.1-3-g733a5 From b5a0e6ea807bcdc71f145038dd1129c22dcf17fd Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 Jul 2019 23:29:57 +0300 Subject: syntax_ext: `proc_macro_decls` -> `proc_macro_harness` Few other minor renamings for consistency. Remove one unused dependency from `rustc_passes`. Fix libsyntax tests. Fix rebase. --- Cargo.lock | 1 - src/librustc_interface/passes.rs | 4 +- src/librustc_passes/Cargo.toml | 1 - src/libsyntax/ext/base.rs | 2 +- src/libsyntax/ext/proc_macro.rs | 2 +- src/libsyntax/parse/lexer/mod.rs | 3 +- src/libsyntax_ext/lib.rs | 9 +- src/libsyntax_ext/proc_macro_decls.rs | 414 -------------------------------- src/libsyntax_ext/proc_macro_harness.rs | 414 ++++++++++++++++++++++++++++++++ src/libsyntax_ext/test.rs | 2 +- src/libsyntax_ext/test_harness.rs | 14 +- 11 files changed, 432 insertions(+), 434 deletions(-) delete mode 100644 src/libsyntax_ext/proc_macro_decls.rs create mode 100644 src/libsyntax_ext/proc_macro_harness.rs (limited to 'src/libsyntax/parse') diff --git a/Cargo.lock b/Cargo.lock index d1afe5944ee..46d8b3de806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3052,7 +3052,6 @@ dependencies = [ "rustc 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", - "rustc_mir 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index c7be6276f4a..3c7d854b36b 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -461,7 +461,7 @@ fn configure_and_expand_inner<'a>( sess.profiler(|p| p.end_activity("macro expansion")); time(sess, "maybe building test harness", || { - syntax_ext::test_harness::modify_for_testing( + syntax_ext::test_harness::inject( &sess.parse_sess, &mut resolver, sess.opts.test, @@ -490,7 +490,7 @@ fn configure_and_expand_inner<'a>( let num_crate_types = crate_types.len(); let is_proc_macro_crate = crate_types.contains(&config::CrateType::ProcMacro); let is_test_crate = sess.opts.test; - syntax_ext::proc_macro_decls::modify( + syntax_ext::proc_macro_harness::inject( &sess.parse_sess, &mut resolver, krate, diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index 5f378dacd25..596ec6c19bc 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -11,7 +11,6 @@ path = "lib.rs" [dependencies] log = "0.4" rustc = { path = "../librustc" } -rustc_mir = { path = "../librustc_mir"} rustc_data_structures = { path = "../librustc_data_structures" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index e53757dce3d..bb7834a133f 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -1,6 +1,6 @@ use crate::ast::{self, Attribute, Name, PatKind}; use crate::attr::{HasAttrs, Stability, Deprecation}; -use crate::source_map::{SourceMap, Spanned, FileName, respan}; +use crate::source_map::{SourceMap, Spanned, respan}; use crate::edition::Edition; use crate::ext::expand::{self, AstFragment, Invocation}; use crate::ext::hygiene::{ExpnId, SyntaxContext, Transparency}; diff --git a/src/libsyntax/ext/proc_macro.rs b/src/libsyntax/ext/proc_macro.rs index 4b4ac57207e..425b9813f59 100644 --- a/src/libsyntax/ext/proc_macro.rs +++ b/src/libsyntax/ext/proc_macro.rs @@ -231,7 +231,7 @@ crate fn add_derived_markers( names.insert(unwrap_or!(path.segments.get(0), continue).ident.name); } - let span = span.fresh_expansion(cx.current_expansion.mark, ExpnInfo::allow_unstable( + let span = span.fresh_expansion(cx.current_expansion.id, ExpnInfo::allow_unstable( ExpnKind::Macro(MacroKind::Derive, Symbol::intern(&pretty_name)), span, cx.parse_sess.edition, cx.allow_derive_markers.clone(), )); diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 52f65e1b474..3cd5464f357 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -794,7 +794,7 @@ mod tests { use std::path::PathBuf; use syntax_pos::{BytePos, Span, NO_EXPANSION, edition::Edition}; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; - use rustc_data_structures::sync::Lock; + use rustc_data_structures::sync::{Lock, Once}; fn mk_sess(sm: Lrc) -> ParseSess { let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), @@ -817,6 +817,7 @@ mod tests { param_attr_spans: Lock::new(Vec::new()), let_chains_spans: Lock::new(Vec::new()), async_closure_spans: Lock::new(Vec::new()), + injected_crate_name: Once::new(), } } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index f49c75d7424..fae884860ed 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -1,4 +1,5 @@ -//! Syntax extensions in the Rust compiler. +//! This crate contains implementations of built-in macros and other code generating facilities +//! injecting code into the crate before it is lowered to HIR. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] @@ -10,16 +11,12 @@ #![feature(mem_take)] #![feature(nll)] #![feature(rustc_diagnostic_macros)] -#![feature(unicode_internals)] - -extern crate proc_macro; use crate::deriving::*; use syntax::ast::Ident; use syntax::edition::Edition; use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind, MacroExpanderFn}; -use syntax::ext::source_util; use syntax::symbol::sym; mod error_codes; @@ -42,7 +39,7 @@ mod test; mod trace_macros; pub mod plugin_macro_defs; -pub mod proc_macro_decls; +pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; diff --git a/src/libsyntax_ext/proc_macro_decls.rs b/src/libsyntax_ext/proc_macro_decls.rs deleted file mode 100644 index 357da6ba3c3..00000000000 --- a/src/libsyntax_ext/proc_macro_decls.rs +++ /dev/null @@ -1,414 +0,0 @@ -use std::mem; - -use syntax::ast::{self, Ident}; -use syntax::attr; -use syntax::source_map::{ExpnInfo, ExpnKind, respan}; -use syntax::ext::base::{ExtCtxt, MacroKind}; -use syntax::ext::build::AstBuilder; -use syntax::ext::expand::ExpansionConfig; -use syntax::ext::hygiene::ExpnId; -use syntax::ext::proc_macro::is_proc_macro_attr; -use syntax::mut_visit::MutVisitor; -use syntax::parse::ParseSess; -use syntax::ptr::P; -use syntax::symbol::{kw, sym}; -use syntax::visit::{self, Visitor}; - -use syntax_pos::{Span, DUMMY_SP}; - -struct ProcMacroDerive { - trait_name: ast::Name, - function_name: Ident, - span: Span, - attrs: Vec, -} - -struct ProcMacroDef { - function_name: Ident, - span: Span, -} - -struct CollectProcMacros<'a> { - derives: Vec, - attr_macros: Vec, - bang_macros: Vec, - in_root: bool, - handler: &'a errors::Handler, - is_proc_macro_crate: bool, - is_test_crate: bool, -} - -pub fn modify(sess: &ParseSess, - resolver: &mut dyn (::syntax::ext::base::Resolver), - mut krate: ast::Crate, - is_proc_macro_crate: bool, - has_proc_macro_decls: bool, - is_test_crate: bool, - num_crate_types: usize, - handler: &errors::Handler) -> ast::Crate { - let ecfg = ExpansionConfig::default("proc_macro".to_string()); - let mut cx = ExtCtxt::new(sess, ecfg, resolver); - - let (derives, attr_macros, bang_macros) = { - let mut collect = CollectProcMacros { - derives: Vec::new(), - attr_macros: Vec::new(), - bang_macros: Vec::new(), - in_root: true, - handler, - is_proc_macro_crate, - is_test_crate, - }; - if has_proc_macro_decls || is_proc_macro_crate { - visit::walk_crate(&mut collect, &krate); - } - (collect.derives, collect.attr_macros, collect.bang_macros) - }; - - if !is_proc_macro_crate { - return krate - } - - if num_crate_types > 1 { - handler.err("cannot mix `proc-macro` crate type with others"); - } - - if is_test_crate { - return krate; - } - - krate.module.items.push(mk_decls(&mut cx, &derives, &attr_macros, &bang_macros)); - - krate -} - -impl<'a> CollectProcMacros<'a> { - fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { - if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() { - self.handler.span_err(sp, - "`proc-macro` crate types cannot \ - export any items other than functions \ - tagged with `#[proc_macro_derive]` currently"); - } - } - - fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { - // Once we've located the `#[proc_macro_derive]` attribute, verify - // that it's of the form `#[proc_macro_derive(Foo)]` or - // `#[proc_macro_derive(Foo, attributes(A, ..))]` - let list = match attr.meta_item_list() { - Some(list) => list, - None => return, - }; - if list.len() != 1 && list.len() != 2 { - self.handler.span_err(attr.span, - "attribute must have either one or two arguments"); - return - } - let trait_attr = match list[0].meta_item() { - Some(meta_item) => meta_item, - _ => { - self.handler.span_err(list[0].span(), "not a meta item"); - return - } - }; - let trait_ident = match trait_attr.ident() { - Some(trait_ident) if trait_attr.is_word() => trait_ident, - _ => { - self.handler.span_err(trait_attr.span, "must only be one word"); - return - } - }; - - if !trait_ident.name.can_be_raw() { - self.handler.span_err(trait_attr.span, - &format!("`{}` cannot be a name of derive macro", trait_ident)); - } - - let attributes_attr = list.get(1); - let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr { - if !attr.check_name(sym::attributes) { - self.handler.span_err(attr.span(), "second argument must be `attributes`") - } - attr.meta_item_list().unwrap_or_else(|| { - self.handler.span_err(attr.span(), - "attribute must be of form: `attributes(foo, bar)`"); - &[] - }).into_iter().filter_map(|attr| { - let attr = match attr.meta_item() { - Some(meta_item) => meta_item, - _ => { - self.handler.span_err(attr.span(), "not a meta item"); - return None; - } - }; - - let ident = match attr.ident() { - Some(ident) if attr.is_word() => ident, - _ => { - self.handler.span_err(attr.span, "must only be one word"); - return None; - } - }; - if !ident.name.can_be_raw() { - self.handler.span_err( - attr.span, - &format!("`{}` cannot be a name of derive helper attribute", ident), - ); - } - - Some(ident.name) - }).collect() - } else { - Vec::new() - }; - - if self.in_root && item.vis.node.is_pub() { - self.derives.push(ProcMacroDerive { - span: item.span, - trait_name: trait_ident.name, - function_name: item.ident, - attrs: proc_attrs, - }); - } else { - let msg = if !self.in_root { - "functions tagged with `#[proc_macro_derive]` must \ - currently reside in the root of the crate" - } else { - "functions tagged with `#[proc_macro_derive]` must be `pub`" - }; - self.handler.span_err(item.span, msg); - } - } - - fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { - self.attr_macros.push(ProcMacroDef { - span: item.span, - function_name: item.ident, - }); - } else { - let msg = if !self.in_root { - "functions tagged with `#[proc_macro_attribute]` must \ - currently reside in the root of the crate" - } else { - "functions tagged with `#[proc_macro_attribute]` must be `pub`" - }; - self.handler.span_err(item.span, msg); - } - } - - fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { - self.bang_macros.push(ProcMacroDef { - span: item.span, - function_name: item.ident, - }); - } else { - let msg = if !self.in_root { - "functions tagged with `#[proc_macro]` must \ - currently reside in the root of the crate" - } else { - "functions tagged with `#[proc_macro]` must be `pub`" - }; - self.handler.span_err(item.span, msg); - } - } -} - -impl<'a> Visitor<'a> for CollectProcMacros<'a> { - fn visit_item(&mut self, item: &'a ast::Item) { - if let ast::ItemKind::MacroDef(..) = item.node { - if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { - let msg = - "cannot export macro_rules! macros from a `proc-macro` crate type currently"; - self.handler.span_err(item.span, msg); - } - } - - // 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 `#[proc_macro_derive]` attribute on it. - let is_fn = match item.node { - ast::ItemKind::Fn(..) => true, - _ => false, - }; - - let mut found_attr: Option<&'a ast::Attribute> = None; - - for attr in &item.attrs { - if is_proc_macro_attr(&attr) { - if let Some(prev_attr) = found_attr { - let msg = if attr.path.segments[0].ident.name == - prev_attr.path.segments[0].ident.name { - format!("only one `#[{}]` attribute is allowed on any given function", - attr.path) - } else { - format!("`#[{}]` and `#[{}]` attributes cannot both be applied \ - to the same function", attr.path, prev_attr.path) - }; - - self.handler.struct_span_err(attr.span, &msg) - .span_note(prev_attr.span, "previous attribute here") - .emit(); - - return; - } - - found_attr = Some(attr); - } - } - - let attr = match found_attr { - None => { - self.check_not_pub_in_root(&item.vis, item.span); - let prev_in_root = mem::replace(&mut self.in_root, false); - visit::walk_item(self, item); - self.in_root = prev_in_root; - return; - }, - Some(attr) => attr, - }; - - if !is_fn { - let msg = format!("the `#[{}]` attribute may only be used on bare functions", - attr.path); - - self.handler.span_err(attr.span, &msg); - return; - } - - if self.is_test_crate { - return; - } - - if !self.is_proc_macro_crate { - let msg = format!("the `#[{}]` attribute is only usable with crates of the \ - `proc-macro` crate type", attr.path); - - self.handler.span_err(attr.span, &msg); - return; - } - - if attr.check_name(sym::proc_macro_derive) { - self.collect_custom_derive(item, attr); - } else if attr.check_name(sym::proc_macro_attribute) { - self.collect_attr_proc_macro(item); - } else if attr.check_name(sym::proc_macro) { - self.collect_bang_proc_macro(item); - }; - - let prev_in_root = mem::replace(&mut self.in_root, false); - visit::walk_item(self, item); - self.in_root = prev_in_root; - } - - fn visit_mac(&mut self, mac: &'a ast::Mac) { - visit::walk_mac(self, mac) - } -} - -// Creates a new module which looks like: -// -// #[doc(hidden)] -// mod $gensym { -// extern crate proc_macro; -// -// use proc_macro::bridge::client::ProcMacro; -// -// #[rustc_proc_macro_decls] -// static DECLS: &[ProcMacro] = &[ -// ProcMacro::custom_derive($name_trait1, &[], ::$name1); -// ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2); -// // ... -// ]; -// } -fn mk_decls( - cx: &mut ExtCtxt<'_>, - custom_derives: &[ProcMacroDerive], - custom_attrs: &[ProcMacroDef], - custom_macros: &[ProcMacroDef], -) -> P { - let span = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( - ExpnKind::Macro(MacroKind::Attr, sym::proc_macro), DUMMY_SP, cx.parse_sess.edition, - [sym::rustc_attrs, sym::proc_macro_internals][..].into(), - )); - - let hidden = cx.meta_list_item_word(span, sym::hidden); - let doc = cx.meta_list(span, sym::doc, vec![hidden]); - let doc_hidden = cx.attribute(span, doc); - - let proc_macro = Ident::with_empty_ctxt(sym::proc_macro); - let krate = cx.item(span, - proc_macro, - Vec::new(), - ast::ItemKind::ExternCrate(None)); - - let bridge = Ident::from_str("bridge"); - let client = Ident::from_str("client"); - let proc_macro_ty = Ident::from_str("ProcMacro"); - let custom_derive = Ident::from_str("custom_derive"); - let attr = Ident::from_str("attr"); - let bang = Ident::from_str("bang"); - let crate_kw = Ident::with_empty_ctxt(kw::Crate); - - let decls = { - let local_path = |sp: Span, name| { - cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![crate_kw, name])) - }; - let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![ - proc_macro, bridge, client, proc_macro_ty, method, - ])); - custom_derives.iter().map(|cd| { - cx.expr_call(span, proc_macro_ty_method_path(custom_derive), vec![ - cx.expr_str(cd.span, cd.trait_name), - cx.expr_vec_slice( - span, - cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::>() - ), - local_path(cd.span, cd.function_name), - ]) - }).chain(custom_attrs.iter().map(|ca| { - cx.expr_call(span, proc_macro_ty_method_path(attr), vec![ - cx.expr_str(ca.span, ca.function_name.name), - local_path(ca.span, ca.function_name), - ]) - })).chain(custom_macros.iter().map(|cm| { - cx.expr_call(span, proc_macro_ty_method_path(bang), vec![ - cx.expr_str(cm.span, cm.function_name.name), - local_path(cm.span, cm.function_name), - ]) - })).collect() - }; - - let decls_static = cx.item_static( - span, - Ident::from_str("_DECLS"), - cx.ty_rptr(span, - cx.ty(span, ast::TyKind::Slice( - cx.ty_path(cx.path(span, - vec![proc_macro, bridge, client, proc_macro_ty])))), - None, ast::Mutability::Immutable), - ast::Mutability::Immutable, - cx.expr_vec_slice(span, decls), - ).map(|mut i| { - let attr = cx.meta_word(span, sym::rustc_proc_macro_decls); - i.attrs.push(cx.attribute(span, attr)); - i.vis = respan(span, ast::VisibilityKind::Public); - i - }); - - let module = cx.item_mod( - span, - span, - ast::Ident::from_str("decls").gensym(), - vec![doc_hidden], - vec![krate, decls_static], - ).map(|mut i| { - i.vis = respan(span, ast::VisibilityKind::Public); - i - }); - - cx.monotonic_expander().flat_map_item(module).pop().unwrap() -} diff --git a/src/libsyntax_ext/proc_macro_harness.rs b/src/libsyntax_ext/proc_macro_harness.rs new file mode 100644 index 00000000000..fc6cd5dc94c --- /dev/null +++ b/src/libsyntax_ext/proc_macro_harness.rs @@ -0,0 +1,414 @@ +use std::mem; + +use syntax::ast::{self, Ident}; +use syntax::attr; +use syntax::source_map::{ExpnInfo, ExpnKind, respan}; +use syntax::ext::base::{ExtCtxt, MacroKind}; +use syntax::ext::build::AstBuilder; +use syntax::ext::expand::ExpansionConfig; +use syntax::ext::hygiene::ExpnId; +use syntax::ext::proc_macro::is_proc_macro_attr; +use syntax::mut_visit::MutVisitor; +use syntax::parse::ParseSess; +use syntax::ptr::P; +use syntax::symbol::{kw, sym}; +use syntax::visit::{self, Visitor}; + +use syntax_pos::{Span, DUMMY_SP}; + +struct ProcMacroDerive { + trait_name: ast::Name, + function_name: Ident, + span: Span, + attrs: Vec, +} + +struct ProcMacroDef { + function_name: Ident, + span: Span, +} + +struct CollectProcMacros<'a> { + derives: Vec, + attr_macros: Vec, + bang_macros: Vec, + in_root: bool, + handler: &'a errors::Handler, + is_proc_macro_crate: bool, + is_test_crate: bool, +} + +pub fn inject(sess: &ParseSess, + resolver: &mut dyn (::syntax::ext::base::Resolver), + mut krate: ast::Crate, + is_proc_macro_crate: bool, + has_proc_macro_decls: bool, + is_test_crate: bool, + num_crate_types: usize, + handler: &errors::Handler) -> ast::Crate { + let ecfg = ExpansionConfig::default("proc_macro".to_string()); + let mut cx = ExtCtxt::new(sess, ecfg, resolver); + + let (derives, attr_macros, bang_macros) = { + let mut collect = CollectProcMacros { + derives: Vec::new(), + attr_macros: Vec::new(), + bang_macros: Vec::new(), + in_root: true, + handler, + is_proc_macro_crate, + is_test_crate, + }; + if has_proc_macro_decls || is_proc_macro_crate { + visit::walk_crate(&mut collect, &krate); + } + (collect.derives, collect.attr_macros, collect.bang_macros) + }; + + if !is_proc_macro_crate { + return krate + } + + if num_crate_types > 1 { + handler.err("cannot mix `proc-macro` crate type with others"); + } + + if is_test_crate { + return krate; + } + + krate.module.items.push(mk_decls(&mut cx, &derives, &attr_macros, &bang_macros)); + + krate +} + +impl<'a> CollectProcMacros<'a> { + fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { + if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() { + self.handler.span_err(sp, + "`proc-macro` crate types cannot \ + export any items other than functions \ + tagged with `#[proc_macro_derive]` currently"); + } + } + + fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { + // Once we've located the `#[proc_macro_derive]` attribute, verify + // that it's of the form `#[proc_macro_derive(Foo)]` or + // `#[proc_macro_derive(Foo, attributes(A, ..))]` + let list = match attr.meta_item_list() { + Some(list) => list, + None => return, + }; + if list.len() != 1 && list.len() != 2 { + self.handler.span_err(attr.span, + "attribute must have either one or two arguments"); + return + } + let trait_attr = match list[0].meta_item() { + Some(meta_item) => meta_item, + _ => { + self.handler.span_err(list[0].span(), "not a meta item"); + return + } + }; + let trait_ident = match trait_attr.ident() { + Some(trait_ident) if trait_attr.is_word() => trait_ident, + _ => { + self.handler.span_err(trait_attr.span, "must only be one word"); + return + } + }; + + if !trait_ident.name.can_be_raw() { + self.handler.span_err(trait_attr.span, + &format!("`{}` cannot be a name of derive macro", trait_ident)); + } + + let attributes_attr = list.get(1); + let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr { + if !attr.check_name(sym::attributes) { + self.handler.span_err(attr.span(), "second argument must be `attributes`") + } + attr.meta_item_list().unwrap_or_else(|| { + self.handler.span_err(attr.span(), + "attribute must be of form: `attributes(foo, bar)`"); + &[] + }).into_iter().filter_map(|attr| { + let attr = match attr.meta_item() { + Some(meta_item) => meta_item, + _ => { + self.handler.span_err(attr.span(), "not a meta item"); + return None; + } + }; + + let ident = match attr.ident() { + Some(ident) if attr.is_word() => ident, + _ => { + self.handler.span_err(attr.span, "must only be one word"); + return None; + } + }; + if !ident.name.can_be_raw() { + self.handler.span_err( + attr.span, + &format!("`{}` cannot be a name of derive helper attribute", ident), + ); + } + + Some(ident.name) + }).collect() + } else { + Vec::new() + }; + + if self.in_root && item.vis.node.is_pub() { + self.derives.push(ProcMacroDerive { + span: item.span, + trait_name: trait_ident.name, + function_name: item.ident, + attrs: proc_attrs, + }); + } else { + let msg = if !self.in_root { + "functions tagged with `#[proc_macro_derive]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro_derive]` must be `pub`" + }; + self.handler.span_err(item.span, msg); + } + } + + fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { + if self.in_root && item.vis.node.is_pub() { + self.attr_macros.push(ProcMacroDef { + span: item.span, + function_name: item.ident, + }); + } else { + let msg = if !self.in_root { + "functions tagged with `#[proc_macro_attribute]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro_attribute]` must be `pub`" + }; + self.handler.span_err(item.span, msg); + } + } + + fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { + if self.in_root && item.vis.node.is_pub() { + self.bang_macros.push(ProcMacroDef { + span: item.span, + function_name: item.ident, + }); + } else { + let msg = if !self.in_root { + "functions tagged with `#[proc_macro]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro]` must be `pub`" + }; + self.handler.span_err(item.span, msg); + } + } +} + +impl<'a> Visitor<'a> for CollectProcMacros<'a> { + fn visit_item(&mut self, item: &'a ast::Item) { + if let ast::ItemKind::MacroDef(..) = item.node { + if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { + let msg = + "cannot export macro_rules! macros from a `proc-macro` crate type currently"; + self.handler.span_err(item.span, msg); + } + } + + // 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 `#[proc_macro_derive]` attribute on it. + let is_fn = match item.node { + ast::ItemKind::Fn(..) => true, + _ => false, + }; + + let mut found_attr: Option<&'a ast::Attribute> = None; + + for attr in &item.attrs { + if is_proc_macro_attr(&attr) { + if let Some(prev_attr) = found_attr { + let msg = if attr.path.segments[0].ident.name == + prev_attr.path.segments[0].ident.name { + format!("only one `#[{}]` attribute is allowed on any given function", + attr.path) + } else { + format!("`#[{}]` and `#[{}]` attributes cannot both be applied \ + to the same function", attr.path, prev_attr.path) + }; + + self.handler.struct_span_err(attr.span, &msg) + .span_note(prev_attr.span, "previous attribute here") + .emit(); + + return; + } + + found_attr = Some(attr); + } + } + + let attr = match found_attr { + None => { + self.check_not_pub_in_root(&item.vis, item.span); + let prev_in_root = mem::replace(&mut self.in_root, false); + visit::walk_item(self, item); + self.in_root = prev_in_root; + return; + }, + Some(attr) => attr, + }; + + if !is_fn { + let msg = format!("the `#[{}]` attribute may only be used on bare functions", + attr.path); + + self.handler.span_err(attr.span, &msg); + return; + } + + if self.is_test_crate { + return; + } + + if !self.is_proc_macro_crate { + let msg = format!("the `#[{}]` attribute is only usable with crates of the \ + `proc-macro` crate type", attr.path); + + self.handler.span_err(attr.span, &msg); + return; + } + + if attr.check_name(sym::proc_macro_derive) { + self.collect_custom_derive(item, attr); + } else if attr.check_name(sym::proc_macro_attribute) { + self.collect_attr_proc_macro(item); + } else if attr.check_name(sym::proc_macro) { + self.collect_bang_proc_macro(item); + }; + + let prev_in_root = mem::replace(&mut self.in_root, false); + visit::walk_item(self, item); + self.in_root = prev_in_root; + } + + fn visit_mac(&mut self, mac: &'a ast::Mac) { + visit::walk_mac(self, mac) + } +} + +// Creates a new module which looks like: +// +// #[doc(hidden)] +// mod $gensym { +// extern crate proc_macro; +// +// use proc_macro::bridge::client::ProcMacro; +// +// #[rustc_proc_macro_decls] +// static DECLS: &[ProcMacro] = &[ +// ProcMacro::custom_derive($name_trait1, &[], ::$name1); +// ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2); +// // ... +// ]; +// } +fn mk_decls( + cx: &mut ExtCtxt<'_>, + custom_derives: &[ProcMacroDerive], + custom_attrs: &[ProcMacroDef], + custom_macros: &[ProcMacroDef], +) -> P { + let span = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( + ExpnKind::Macro(MacroKind::Attr, sym::proc_macro), DUMMY_SP, cx.parse_sess.edition, + [sym::rustc_attrs, sym::proc_macro_internals][..].into(), + )); + + let hidden = cx.meta_list_item_word(span, sym::hidden); + let doc = cx.meta_list(span, sym::doc, vec![hidden]); + let doc_hidden = cx.attribute(span, doc); + + let proc_macro = Ident::with_empty_ctxt(sym::proc_macro); + let krate = cx.item(span, + proc_macro, + Vec::new(), + ast::ItemKind::ExternCrate(None)); + + let bridge = Ident::from_str("bridge"); + let client = Ident::from_str("client"); + let proc_macro_ty = Ident::from_str("ProcMacro"); + let custom_derive = Ident::from_str("custom_derive"); + let attr = Ident::from_str("attr"); + let bang = Ident::from_str("bang"); + let crate_kw = Ident::with_empty_ctxt(kw::Crate); + + let decls = { + let local_path = |sp: Span, name| { + cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![crate_kw, name])) + }; + let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![ + proc_macro, bridge, client, proc_macro_ty, method, + ])); + custom_derives.iter().map(|cd| { + cx.expr_call(span, proc_macro_ty_method_path(custom_derive), vec![ + cx.expr_str(cd.span, cd.trait_name), + cx.expr_vec_slice( + span, + cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::>() + ), + local_path(cd.span, cd.function_name), + ]) + }).chain(custom_attrs.iter().map(|ca| { + cx.expr_call(span, proc_macro_ty_method_path(attr), vec![ + cx.expr_str(ca.span, ca.function_name.name), + local_path(ca.span, ca.function_name), + ]) + })).chain(custom_macros.iter().map(|cm| { + cx.expr_call(span, proc_macro_ty_method_path(bang), vec![ + cx.expr_str(cm.span, cm.function_name.name), + local_path(cm.span, cm.function_name), + ]) + })).collect() + }; + + let decls_static = cx.item_static( + span, + Ident::from_str("_DECLS"), + cx.ty_rptr(span, + cx.ty(span, ast::TyKind::Slice( + cx.ty_path(cx.path(span, + vec![proc_macro, bridge, client, proc_macro_ty])))), + None, ast::Mutability::Immutable), + ast::Mutability::Immutable, + cx.expr_vec_slice(span, decls), + ).map(|mut i| { + let attr = cx.meta_word(span, sym::rustc_proc_macro_decls); + i.attrs.push(cx.attribute(span, attr)); + i.vis = respan(span, ast::VisibilityKind::Public); + i + }); + + let module = cx.item_mod( + span, + span, + ast::Ident::from_str("decls").gensym(), + vec![doc_hidden], + vec![krate, decls_static], + ).map(|mut i| { + i.vis = respan(span, ast::VisibilityKind::Public); + i + }); + + cx.monotonic_expander().flat_map_item(module).pop().unwrap() +} diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs index 36aeb3065ff..a2d93d01cec 100644 --- a/src/libsyntax_ext/test.rs +++ b/src/libsyntax_ext/test.rs @@ -30,7 +30,7 @@ pub fn expand_test_case( if !ecx.ecfg.should_test { return vec![]; } - let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.mark)); + let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id)); let mut item = anno_item.expect_item(); item = item.map(|mut item| { item.vis = respan(item.vis.span, ast::VisibilityKind::Public); diff --git a/src/libsyntax_ext/test_harness.rs b/src/libsyntax_ext/test_harness.rs index 061f5c3408b..848c797856e 100644 --- a/src/libsyntax_ext/test_harness.rs +++ b/src/libsyntax_ext/test_harness.rs @@ -37,12 +37,14 @@ struct TestCtxt<'a> { // Traverse the crate, collecting all the test functions, eliding any // existing main functions, and synthesizing a main test harness -pub fn modify_for_testing(sess: &ParseSess, - resolver: &mut dyn Resolver, - should_test: bool, - krate: &mut ast::Crate, - span_diagnostic: &errors::Handler, - features: &Features) { +pub fn inject( + sess: &ParseSess, + resolver: &mut dyn Resolver, + should_test: bool, + krate: &mut ast::Crate, + span_diagnostic: &errors::Handler, + features: &Features, +) { // Check for #[reexport_test_harness_main = "some_name"] which // creates a `use __test::main as some_name;`. This needs to be // unconditional, so that the attribute is still marked as used in -- cgit 1.4.1-3-g733a5