Rollup merge of #40129 - abonander:proc_macro_bang, r=jseyfried
Implement function-like procedural macros ( `#[proc_macro]`) Adds the `#[proc_macro]` attribute, which expects bare functions of the kind `fn(TokenStream) -> TokenStream`, which can be invoked like `my_macro!()`. cc rust-lang/rfcs#1913, #38356 r? @jseyfried cc @nrc
This commit is contained in:
commit
c883f4f584
10 changed files with 213 additions and 10 deletions
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -774,6 +774,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",
|
||||
|
|
|
@ -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::<String>() {
|
||||
err.help(&format!("message: {}", s));
|
||||
}
|
||||
if let Some(s) = e.downcast_ref::<&'static str>() {
|
||||
err.help(&format!("message: {}", s));
|
||||
}
|
||||
|
||||
err.emit();
|
||||
panic!(FatalError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ast::Name>,
|
||||
}
|
||||
|
||||
struct AttrProcMacro {
|
||||
struct ProcMacroDef {
|
||||
function_name: Ident,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
struct CollectProcMacros<'a> {
|
||||
derives: Vec<ProcMacroDerive>,
|
||||
attr_macros: Vec<AttrProcMacro>,
|
||||
attr_macros: Vec<ProcMacroDef>,
|
||||
bang_macros: Vec<ProcMacroDef>,
|
||||
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<ast::Item> {
|
||||
custom_attrs: &[ProcMacroDef],
|
||||
custom_macros: &[ProcMacroDef]) -> P<ast::Item> {
|
||||
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);
|
||||
|
|
|
@ -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 <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.
|
||||
|
||||
// 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
|
||||
}
|
21
src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs
Normal file
21
src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs
Normal file
|
@ -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 <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.
|
||||
|
||||
// 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]`
|
||||
}
|
|
@ -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!`?
|
||||
}
|
||||
|
|
|
@ -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 <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.
|
||||
|
||||
// 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()
|
||||
}
|
20
src/test/run-pass-fulldeps/proc-macro/bang-macro.rs
Normal file
20
src/test/run-pass-fulldeps/proc-macro/bang-macro.rs
Normal file
|
@ -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 <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.
|
||||
|
||||
// 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!");
|
||||
}
|
Loading…
Add table
Reference in a new issue