diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index b33caefbcd2ec..0516e111be3b3 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -125,6 +125,10 @@ pub mod __internal { fn register_attr_proc_macro(&mut self, name: &str, expand: fn(TokenStream, TokenStream) -> TokenStream); + + fn register_bang_proc_macro(&mut self, + name: &str, + expand: fn(TokenStream) -> TokenStream); } // Emulate scoped_thread_local!() here essentially diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 4477488f6cb38..49dcffb4830a1 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -586,7 +586,7 @@ impl<'a> CrateLoader<'a> { use proc_macro::__internal::Registry; use rustc_back::dynamic_lib::DynamicLibrary; use syntax_ext::deriving::custom::ProcMacroDerive; - use syntax_ext::proc_macro_impl::AttrProcMacro; + use syntax_ext::proc_macro_impl::{AttrProcMacro, BangProcMacro}; let path = match dylib { Some(dylib) => dylib, @@ -630,6 +630,15 @@ impl<'a> CrateLoader<'a> { ); self.0.push((Symbol::intern(name), Rc::new(expand))); } + + fn register_bang_proc_macro(&mut self, + name: &str, + expand: fn(TokenStream) -> TokenStream) { + let expand = SyntaxExtension::ProcMacro( + Box::new(BangProcMacro { inner: expand }) + ); + self.0.push((Symbol::intern(name), Rc::new(expand))); + } } let mut my_registrar = MyRegistrar(Vec::new()); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 78f13c18dc104..7cb3c34b07aa0 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -766,6 +766,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "attribute proc macros are currently unstable", cfg_fn!(proc_macro))), + ("proc_macro", Normal, Gated(Stability::Unstable, + "proc_macro", + "function-like proc macros are currently unstable", + cfg_fn!(proc_macro))), + ("rustc_derive_registrar", Normal, Gated(Stability::Unstable, "rustc_derive_registrar", "used internally by rustc", diff --git a/src/libsyntax_ext/proc_macro_impl.rs b/src/libsyntax_ext/proc_macro_impl.rs index b454628acb1c0..f60e5824db962 100644 --- a/src/libsyntax_ext/proc_macro_impl.rs +++ b/src/libsyntax_ext/proc_macro_impl.rs @@ -56,3 +56,38 @@ impl base::AttrProcMacro for AttrProcMacro { } } } + +pub struct BangProcMacro { + pub inner: fn(TsShim) -> TsShim, +} + +impl base::ProcMacro for BangProcMacro { + fn expand<'cx>(&self, + ecx: &'cx mut ExtCtxt, + span: Span, + input: TokenStream) + -> TokenStream { + let input = __internal::token_stream_wrap(input); + + let res = __internal::set_parse_sess(&ecx.parse_sess, || { + panic::catch_unwind(panic::AssertUnwindSafe(|| (self.inner)(input))) + }); + + match res { + Ok(stream) => __internal::token_stream_inner(stream), + Err(e) => { + let msg = "proc macro panicked"; + let mut err = ecx.struct_span_fatal(span, msg); + if let Some(s) = e.downcast_ref::() { + err.help(&format!("message: {}", s)); + } + if let Some(s) = e.downcast_ref::<&'static str>() { + err.help(&format!("message: {}", s)); + } + + err.emit(); + panic!(FatalError); + } + } + } +} diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index 325f09a83ddab..9c96ad547e1ae 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -27,6 +27,9 @@ use syntax_pos::{Span, DUMMY_SP}; use deriving; +const PROC_MACRO_KINDS: [&'static str; 3] = + ["proc_macro_derive", "proc_macro_attribute", "proc_macro"]; + struct ProcMacroDerive { trait_name: ast::Name, function_name: Ident, @@ -34,14 +37,15 @@ struct ProcMacroDerive { attrs: Vec, } -struct AttrProcMacro { +struct ProcMacroDef { function_name: Ident, span: Span, } struct CollectProcMacros<'a> { derives: Vec, - attr_macros: Vec, + attr_macros: Vec, + bang_macros: Vec, in_root: bool, handler: &'a errors::Handler, is_proc_macro_crate: bool, @@ -58,17 +62,18 @@ pub fn modify(sess: &ParseSess, let ecfg = ExpansionConfig::default("proc_macro".to_string()); let mut cx = ExtCtxt::new(sess, ecfg, resolver); - let (derives, attr_macros) = { + 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: handler, is_proc_macro_crate: is_proc_macro_crate, is_test_crate: is_test_crate, }; visit::walk_crate(&mut collect, &krate); - (collect.derives, collect.attr_macros) + (collect.derives, collect.attr_macros, collect.bang_macros) }; if !is_proc_macro_crate { @@ -83,7 +88,7 @@ pub fn modify(sess: &ParseSess, return krate; } - krate.module.items.push(mk_registrar(&mut cx, &derives, &attr_macros)); + krate.module.items.push(mk_registrar(&mut cx, &derives, &attr_macros, &bang_macros)); if krate.exported_macros.len() > 0 { handler.err("cannot export macro_rules! macros from a `proc-macro` \ @@ -93,6 +98,10 @@ pub fn modify(sess: &ParseSess, return krate } +fn is_proc_macro_attr(attr: &ast::Attribute) -> bool { + PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(kind)) +} + impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { if self.is_proc_macro_crate && @@ -196,12 +205,12 @@ impl<'a> CollectProcMacros<'a> { fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { if let Some(_) = attr.meta_item_list() { self.handler.span_err(attr.span, "`#[proc_macro_attribute]` attribute - cannot contain any meta items"); + does not take any arguments"); return; } if self.in_root && item.vis == ast::Visibility::Public { - self.attr_macros.push(AttrProcMacro { + self.attr_macros.push(ProcMacroDef { span: item.span, function_name: item.ident, }); @@ -215,6 +224,29 @@ impl<'a> CollectProcMacros<'a> { self.handler.span_err(item.span, msg); } } + + fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { + if let Some(_) = attr.meta_item_list() { + self.handler.span_err(attr.span, "`#[proc_macro]` attribute + does not take any arguments"); + return; + } + + if self.in_root && item.vis == ast::Visibility::Public { + 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> { @@ -232,7 +264,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { let mut found_attr: Option<&'a ast::Attribute> = None; for attr in &item.attrs { - if attr.check_name("proc_macro_derive") || attr.check_name("proc_macro_attribute") { + if is_proc_macro_attr(&attr) { if let Some(prev_attr) = found_attr { let msg = if attr.name() == prev_attr.name() { format!("Only one `#[{}]` attribute is allowed on any given function", @@ -285,6 +317,8 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { self.collect_custom_derive(item, attr); } else if attr.check_name("proc_macro_attribute") { self.collect_attr_proc_macro(item, attr); + } else if attr.check_name("proc_macro") { + self.collect_bang_proc_macro(item, attr); }; visit::walk_item(self, item); @@ -320,7 +354,8 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { // } fn mk_registrar(cx: &mut ExtCtxt, custom_derives: &[ProcMacroDerive], - custom_attrs: &[AttrProcMacro]) -> P { + custom_attrs: &[ProcMacroDef], + custom_macros: &[ProcMacroDef]) -> P { let eid = cx.codemap().record_expansion(ExpnInfo { call_site: DUMMY_SP, callee: NameAndSpan { @@ -342,6 +377,7 @@ fn mk_registrar(cx: &mut ExtCtxt, let registrar = Ident::from_str("registrar"); let register_custom_derive = Ident::from_str("register_custom_derive"); let register_attr_proc_macro = Ident::from_str("register_attr_proc_macro"); + let register_bang_proc_macro = Ident::from_str("register_bang_proc_macro"); let mut stmts = custom_derives.iter().map(|cd| { let path = cx.path_global(cd.span, vec![cd.function_name]); @@ -371,6 +407,18 @@ fn mk_registrar(cx: &mut ExtCtxt, vec![registrar, name, cx.expr_path(path)])) })); + stmts.extend(custom_macros.iter().map(|cm| { + let name = cx.expr_str(cm.span, cm.function_name.name); + let path = cx.path_global(cm.span, vec![cm.function_name]); + let registrar = cx.expr_ident(cm.span, registrar); + + let ufcs_path = cx.path(span, + vec![proc_macro, __internal, registry, register_bang_proc_macro]); + + cx.stmt_expr(cx.expr_call(span, cx.expr_path(ufcs_path), + vec![registrar, name, cx.expr_path(path)])) + })); + let path = cx.path(span, vec![proc_macro, __internal, registry]); let registrar_path = cx.ty_path(path); let arg_ty = cx.ty_rptr(span, registrar_path, None, ast::Mutability::Mutable); diff --git a/src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs new file mode 100644 index 0000000000000..89ac11b309d75 --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host +// no-prefer-dynamic +#![feature(proc_macro)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn bang_proc_macro(input: TokenStream) -> TokenStream { + input +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs b/src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs new file mode 100644 index 0000000000000..7ecc685357ee6 --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs @@ -0,0 +1,21 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:bang_proc_macro.rs + +#![feature(proc_macro)] + +#[macro_use] +extern crate bang_proc_macro; + +fn main() { + bang_proc_macro!(println!("Hello, world!")); + //~^ ERROR: procedural macros cannot be imported with `#[macro_use]` +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs b/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs index eac0be6f84874..e0066dd43be89 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs @@ -11,6 +11,7 @@ // aux-build:derive-foo.rs // aux-build:derive-clona.rs // aux-build:attr_proc_macro.rs +// aux-build:bang_proc_macro.rs #![feature(proc_macro)] @@ -19,13 +20,19 @@ extern crate derive_foo; #[macro_use] extern crate derive_clona; extern crate attr_proc_macro; +extern crate bang_proc_macro; use attr_proc_macro::attr_proc_macro; +use bang_proc_macro::bang_proc_macro; macro_rules! FooWithLongNam { () => {} } +macro_rules! attr_proc_mac { + () => {} +} + #[derive(FooWithLongNan)] //~^ ERROR cannot find derive macro `FooWithLongNan` in this scope //~^^ HELP did you mean `FooWithLongName`? @@ -61,7 +68,12 @@ fn main() { attr_proc_macra!(); //~^ ERROR cannot find macro `attr_proc_macra!` in this scope + //~^^ HELP did you mean `attr_proc_mac!`? Dlona!(); //~^ ERROR cannot find macro `Dlona!` in this scope + + bang_proc_macrp!(); + //~^ ERROR cannot find macro `bang_proc_macrp!` in this scope + //~^^ HELP did you mean `bang_proc_macro!`? } diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs new file mode 100644 index 0000000000000..122a47aff7198 --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs @@ -0,0 +1,26 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic +#![feature(proc_macro)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn rewrite(input: TokenStream) -> TokenStream { + let input = input.to_string(); + + assert_eq!(input, r#""Hello, world!""#); + + r#""NOT Hello, world!""#.parse().unwrap() +} diff --git a/src/test/run-pass-fulldeps/proc-macro/bang-macro.rs b/src/test/run-pass-fulldeps/proc-macro/bang-macro.rs new file mode 100644 index 0000000000000..531bd0dd3569d --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/bang-macro.rs @@ -0,0 +1,20 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:bang-macro.rs + +#![feature(proc_macro)] + +extern crate bang_macro; +use bang_macro::rewrite; + +fn main() { + assert_eq!(rewrite!("Hello, world!"), "NOT Hello, world!"); +}