Implement function-like procedural macros ( #[proc_macro]
)
This commit is contained in:
parent
4be034e622
commit
2fcbb48c72
10 changed files with 213 additions and 10 deletions
|
@ -125,6 +125,10 @@ pub mod __internal {
|
||||||
fn register_attr_proc_macro(&mut self,
|
fn register_attr_proc_macro(&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
expand: fn(TokenStream, TokenStream) -> TokenStream);
|
expand: fn(TokenStream, TokenStream) -> TokenStream);
|
||||||
|
|
||||||
|
fn register_bang_proc_macro(&mut self,
|
||||||
|
name: &str,
|
||||||
|
expand: fn(TokenStream) -> TokenStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emulate scoped_thread_local!() here essentially
|
// Emulate scoped_thread_local!() here essentially
|
||||||
|
|
|
@ -586,7 +586,7 @@ impl<'a> CrateLoader<'a> {
|
||||||
use proc_macro::__internal::Registry;
|
use proc_macro::__internal::Registry;
|
||||||
use rustc_back::dynamic_lib::DynamicLibrary;
|
use rustc_back::dynamic_lib::DynamicLibrary;
|
||||||
use syntax_ext::deriving::custom::ProcMacroDerive;
|
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 {
|
let path = match dylib {
|
||||||
Some(dylib) => dylib,
|
Some(dylib) => dylib,
|
||||||
|
@ -630,6 +630,15 @@ impl<'a> CrateLoader<'a> {
|
||||||
);
|
);
|
||||||
self.0.push((Symbol::intern(name), Rc::new(expand)));
|
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());
|
let mut my_registrar = MyRegistrar(Vec::new());
|
||||||
|
|
|
@ -763,6 +763,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||||
"attribute proc macros are currently unstable",
|
"attribute proc macros are currently unstable",
|
||||||
cfg_fn!(proc_macro))),
|
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", Normal, Gated(Stability::Unstable,
|
||||||
"rustc_derive_registrar",
|
"rustc_derive_registrar",
|
||||||
"used internally by rustc",
|
"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;
|
use deriving;
|
||||||
|
|
||||||
|
const PROC_MACRO_KINDS: [&'static str; 3] =
|
||||||
|
["proc_macro_derive", "proc_macro_attribute", "proc_macro"];
|
||||||
|
|
||||||
struct ProcMacroDerive {
|
struct ProcMacroDerive {
|
||||||
trait_name: ast::Name,
|
trait_name: ast::Name,
|
||||||
function_name: Ident,
|
function_name: Ident,
|
||||||
|
@ -34,14 +37,15 @@ struct ProcMacroDerive {
|
||||||
attrs: Vec<ast::Name>,
|
attrs: Vec<ast::Name>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AttrProcMacro {
|
struct ProcMacroDef {
|
||||||
function_name: Ident,
|
function_name: Ident,
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CollectProcMacros<'a> {
|
struct CollectProcMacros<'a> {
|
||||||
derives: Vec<ProcMacroDerive>,
|
derives: Vec<ProcMacroDerive>,
|
||||||
attr_macros: Vec<AttrProcMacro>,
|
attr_macros: Vec<ProcMacroDef>,
|
||||||
|
bang_macros: Vec<ProcMacroDef>,
|
||||||
in_root: bool,
|
in_root: bool,
|
||||||
handler: &'a errors::Handler,
|
handler: &'a errors::Handler,
|
||||||
is_proc_macro_crate: bool,
|
is_proc_macro_crate: bool,
|
||||||
|
@ -58,17 +62,18 @@ pub fn modify(sess: &ParseSess,
|
||||||
let ecfg = ExpansionConfig::default("proc_macro".to_string());
|
let ecfg = ExpansionConfig::default("proc_macro".to_string());
|
||||||
let mut cx = ExtCtxt::new(sess, ecfg, resolver);
|
let mut cx = ExtCtxt::new(sess, ecfg, resolver);
|
||||||
|
|
||||||
let (derives, attr_macros) = {
|
let (derives, attr_macros, bang_macros) = {
|
||||||
let mut collect = CollectProcMacros {
|
let mut collect = CollectProcMacros {
|
||||||
derives: Vec::new(),
|
derives: Vec::new(),
|
||||||
attr_macros: Vec::new(),
|
attr_macros: Vec::new(),
|
||||||
|
bang_macros: Vec::new(),
|
||||||
in_root: true,
|
in_root: true,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
is_proc_macro_crate: is_proc_macro_crate,
|
is_proc_macro_crate: is_proc_macro_crate,
|
||||||
is_test_crate: is_test_crate,
|
is_test_crate: is_test_crate,
|
||||||
};
|
};
|
||||||
visit::walk_crate(&mut collect, &krate);
|
visit::walk_crate(&mut collect, &krate);
|
||||||
(collect.derives, collect.attr_macros)
|
(collect.derives, collect.attr_macros, collect.bang_macros)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !is_proc_macro_crate {
|
if !is_proc_macro_crate {
|
||||||
|
@ -83,7 +88,7 @@ pub fn modify(sess: &ParseSess,
|
||||||
return krate;
|
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 {
|
if krate.exported_macros.len() > 0 {
|
||||||
handler.err("cannot export macro_rules! macros from a `proc-macro` \
|
handler.err("cannot export macro_rules! macros from a `proc-macro` \
|
||||||
|
@ -93,6 +98,10 @@ pub fn modify(sess: &ParseSess,
|
||||||
return krate
|
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> {
|
impl<'a> CollectProcMacros<'a> {
|
||||||
fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
|
fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
|
||||||
if self.is_proc_macro_crate &&
|
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) {
|
fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
|
||||||
if let Some(_) = attr.meta_item_list() {
|
if let Some(_) = attr.meta_item_list() {
|
||||||
self.handler.span_err(attr.span, "`#[proc_macro_attribute]` attribute
|
self.handler.span_err(attr.span, "`#[proc_macro_attribute]` attribute
|
||||||
cannot contain any meta items");
|
does not take any arguments");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.in_root && item.vis == ast::Visibility::Public {
|
if self.in_root && item.vis == ast::Visibility::Public {
|
||||||
self.attr_macros.push(AttrProcMacro {
|
self.attr_macros.push(ProcMacroDef {
|
||||||
span: item.span,
|
span: item.span,
|
||||||
function_name: item.ident,
|
function_name: item.ident,
|
||||||
});
|
});
|
||||||
|
@ -215,6 +224,29 @@ impl<'a> CollectProcMacros<'a> {
|
||||||
self.handler.span_err(item.span, msg);
|
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> {
|
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;
|
let mut found_attr: Option<&'a ast::Attribute> = None;
|
||||||
|
|
||||||
for attr in &item.attrs {
|
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 {
|
if let Some(prev_attr) = found_attr {
|
||||||
let msg = if attr.name() == prev_attr.name() {
|
let msg = if attr.name() == prev_attr.name() {
|
||||||
format!("Only one `#[{}]` attribute is allowed on any given function",
|
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);
|
self.collect_custom_derive(item, attr);
|
||||||
} else if attr.check_name("proc_macro_attribute") {
|
} else if attr.check_name("proc_macro_attribute") {
|
||||||
self.collect_attr_proc_macro(item, attr);
|
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);
|
visit::walk_item(self, item);
|
||||||
|
@ -320,7 +354,8 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
|
||||||
// }
|
// }
|
||||||
fn mk_registrar(cx: &mut ExtCtxt,
|
fn mk_registrar(cx: &mut ExtCtxt,
|
||||||
custom_derives: &[ProcMacroDerive],
|
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 {
|
let eid = cx.codemap().record_expansion(ExpnInfo {
|
||||||
call_site: DUMMY_SP,
|
call_site: DUMMY_SP,
|
||||||
callee: NameAndSpan {
|
callee: NameAndSpan {
|
||||||
|
@ -342,6 +377,7 @@ fn mk_registrar(cx: &mut ExtCtxt,
|
||||||
let registrar = Ident::from_str("registrar");
|
let registrar = Ident::from_str("registrar");
|
||||||
let register_custom_derive = Ident::from_str("register_custom_derive");
|
let register_custom_derive = Ident::from_str("register_custom_derive");
|
||||||
let register_attr_proc_macro = Ident::from_str("register_attr_proc_macro");
|
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 mut stmts = custom_derives.iter().map(|cd| {
|
||||||
let path = cx.path_global(cd.span, vec![cd.function_name]);
|
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)]))
|
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 path = cx.path(span, vec![proc_macro, __internal, registry]);
|
||||||
let registrar_path = cx.ty_path(path);
|
let registrar_path = cx.ty_path(path);
|
||||||
let arg_ty = cx.ty_rptr(span, registrar_path, None, ast::Mutability::Mutable);
|
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-foo.rs
|
||||||
// aux-build:derive-clona.rs
|
// aux-build:derive-clona.rs
|
||||||
// aux-build:attr_proc_macro.rs
|
// aux-build:attr_proc_macro.rs
|
||||||
|
// aux-build:bang_proc_macro.rs
|
||||||
|
|
||||||
#![feature(proc_macro)]
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
@ -19,13 +20,19 @@ extern crate derive_foo;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate derive_clona;
|
extern crate derive_clona;
|
||||||
extern crate attr_proc_macro;
|
extern crate attr_proc_macro;
|
||||||
|
extern crate bang_proc_macro;
|
||||||
|
|
||||||
use attr_proc_macro::attr_proc_macro;
|
use attr_proc_macro::attr_proc_macro;
|
||||||
|
use bang_proc_macro::bang_proc_macro;
|
||||||
|
|
||||||
macro_rules! FooWithLongNam {
|
macro_rules! FooWithLongNam {
|
||||||
() => {}
|
() => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! attr_proc_mac {
|
||||||
|
() => {}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FooWithLongNan)]
|
#[derive(FooWithLongNan)]
|
||||||
//~^ ERROR cannot find derive macro `FooWithLongNan` in this scope
|
//~^ ERROR cannot find derive macro `FooWithLongNan` in this scope
|
||||||
//~^^ HELP did you mean `FooWithLongName`?
|
//~^^ HELP did you mean `FooWithLongName`?
|
||||||
|
@ -61,7 +68,12 @@ fn main() {
|
||||||
|
|
||||||
attr_proc_macra!();
|
attr_proc_macra!();
|
||||||
//~^ ERROR cannot find macro `attr_proc_macra!` in this scope
|
//~^ ERROR cannot find macro `attr_proc_macra!` in this scope
|
||||||
|
//~^^ HELP did you mean `attr_proc_mac!`?
|
||||||
|
|
||||||
Dlona!();
|
Dlona!();
|
||||||
//~^ ERROR cannot find macro `Dlona!` in this scope
|
//~^ 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
Add a link
Reference in a new issue