]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #40129 - abonander:proc_macro_bang, r=jseyfried
authorCorey Farwell <coreyf@rwell.org>
Thu, 2 Mar 2017 19:53:46 +0000 (14:53 -0500)
committerGitHub <noreply@github.com>
Thu, 2 Mar 2017 19:53:46 +0000 (14:53 -0500)
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

src/libproc_macro/lib.rs
src/librustc_metadata/creader.rs
src/libsyntax/feature_gate.rs
src/libsyntax_ext/proc_macro_impl.rs
src/libsyntax_ext/proc_macro_registrar.rs
src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs [new file with mode: 0644]
src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs [new file with mode: 0644]
src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs
src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs [new file with mode: 0644]
src/test/run-pass-fulldeps/proc-macro/bang-macro.rs [new file with mode: 0644]

index b33caefbcd2ecbac73e3b73a7b1d88ca4af3f2a5..0516e111be3b38002bd924e6a7ada7c107082e4b 100644 (file)
@@ -125,6 +125,10 @@ fn register_custom_derive(&mut self,
         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
index 4477488f6cb38bf68b52ef7a345bd107774cf7d6..49dcffb4830a1da67eb69f88b486a2c68091bdc9 100644 (file)
@@ -586,7 +586,7 @@ fn load_derive_macros(&mut self, root: &CrateRoot, dylib: Option<PathBuf>, span:
         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 @@ fn register_attr_proc_macro(&mut self,
                 );
                 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());
index e4256ff1c840745521f636f38425b8b07688b879..6eb7d449f269287afe915646535552840b0ec16d 100644 (file)
@@ -774,6 +774,11 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
                                            "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",
index b454628acb1c09bf95b0dabcd04ed18411daf1f8..f60e5824db9627cc67e203060a8409ab58afee7f 100644 (file)
@@ -56,3 +56,38 @@ fn expand<'cx>(&self,
         }
     }
 }
+
+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);
+            }
+        }
+    }
+}
index 325f09a83ddab80860afd3524f7937ab23cd2e7c..9c96ad547e1ae33254dc8034826b68748db50bc9 100644 (file)
@@ -27,6 +27,9 @@
 
 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 @@ fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribut
     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 @@ fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attrib
             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 @@ fn visit_item(&mut self, item: &'a ast::Item) {
         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 @@ fn visit_item(&mut self, item: &'a ast::Item) {
             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 @@ fn visit_mac(&mut self, mac: &ast::Mac) {
 //      }
 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);
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 (file)
index 0000000..89ac11b
--- /dev/null
@@ -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
+}
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 (file)
index 0000000..7ecc685
--- /dev/null
@@ -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]`
+}
index eac0be6f84874ca8fdf9e81259ed1dc16df56848..e0066dd43be89f13cb3d8ec3a8abf80d302ce5a6 100644 (file)
@@ -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)]
 
 #[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 (file)
index 0000000..122a47a
--- /dev/null
@@ -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()
+}
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 (file)
index 0000000..531bd0d
--- /dev/null
@@ -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!");
+}