about summary refs log tree commit diff
path: root/src/libsyntax_ext/proc_macro_impl.rs
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2017-01-19 15:49:23 -0800
committerAlex Crichton <alex@alexcrichton.com>2017-01-20 08:34:30 -0800
commitd4d276faafb07f13f15178540e337864af8f74db (patch)
tree2619f62b9c8d6ec8e9c0901a57109877313f5a99 /src/libsyntax_ext/proc_macro_impl.rs
parent437d2b5e2840b220772c8d58a925c2b232bd0c2e (diff)
parent04ecee158c2c56f4a6d81ad17ac3547848ec1e4c (diff)
downloadrust-d4d276faafb07f13f15178540e337864af8f74db.tar.gz
rust-d4d276faafb07f13f15178540e337864af8f74db.zip
Rollup merge of #38842 - abonander:proc_macro_attribute, r=jseyfried
Implement `#[proc_macro_attribute]`

This implements `#[proc_macro_attribute]` as described in https://github.com/rust-lang/rfcs/pull/1566

The following major (hopefully non-breaking) changes are included:

* Refactor `proc_macro::TokenStream` to use `syntax::tokenstream::TokenStream`.
    * `proc_macro::tokenstream::TokenStream` no longer emits newlines between items, this can be trivially restored if desired
    * `proc_macro::TokenStream::from_str` does not try to parse an item anymore, moved to `impl MultiItemModifier for CustomDerive` with more informative error message

* Implement `#[proc_macro_attribute]`, which expects functions of the kind `fn(TokenStream, TokenStream) -> TokenStream`
    * Reactivated `#![feature(proc_macro)]` and gated `#[proc_macro_attribute]` under it
    * `#![feature(proc_macro)]` and `#![feature(custom_attribute)]` are mutually exclusive
    * adding `#![feature(proc_macro)]` makes the expansion pass assume that any attributes that are not built-in, or introduced by existing syntax extensions, are proc-macro attributes

* Fix `feature_gate::find_lang_feature_issue()` to not use `unwrap()`

    * This change wasn't necessary for this PR, but it helped debugging a problem where I was using the wrong feature string.

* Move "completed feature gate checking" pass to after "name resolution" pass

    * This was necessary for proper feature-gating of `#[proc_macro_attribute]` invocations when the `proc_macro` feature flag isn't set.

Prototype/Litmus Test: [Implementation](https://github.com/abonander/anterofit/blob/proc_macro/service-attr/src/lib.rs#L13) -- [Usage](https://github.com/abonander/anterofit/blob/proc_macro/service-attr/examples/post_service.rs#L35)
Diffstat (limited to 'src/libsyntax_ext/proc_macro_impl.rs')
-rw-r--r--src/libsyntax_ext/proc_macro_impl.rs58
1 files changed, 58 insertions, 0 deletions
diff --git a/src/libsyntax_ext/proc_macro_impl.rs b/src/libsyntax_ext/proc_macro_impl.rs
new file mode 100644
index 00000000000..b454628acb1
--- /dev/null
+++ b/src/libsyntax_ext/proc_macro_impl.rs
@@ -0,0 +1,58 @@
+// 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.
+
+use std::panic;
+
+use errors::FatalError;
+
+use syntax::codemap::Span;
+use syntax::ext::base::*;
+use syntax::tokenstream::TokenStream;
+use syntax::ext::base;
+
+use proc_macro::TokenStream as TsShim;
+use proc_macro::__internal;
+
+pub struct AttrProcMacro {
+    pub inner: fn(TsShim, TsShim) -> TsShim,
+}
+
+impl base::AttrProcMacro for AttrProcMacro {
+    fn expand<'cx>(&self,
+                   ecx: &'cx mut ExtCtxt,
+                   span: Span,
+                   annotation: TokenStream,
+                   annotated: TokenStream)
+                   -> TokenStream {
+        let annotation = __internal::token_stream_wrap(annotation);
+        let annotated = __internal::token_stream_wrap(annotated);
+
+        let res = __internal::set_parse_sess(&ecx.parse_sess, || {
+            panic::catch_unwind(panic::AssertUnwindSafe(|| (self.inner)(annotation, annotated)))
+        });
+
+        match res {
+            Ok(stream) => __internal::token_stream_inner(stream),
+            Err(e) => {
+                let msg = "custom attribute 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);
+            }
+        }
+    }
+}