about summary refs log tree commit diff
path: root/src/libsyntax_ext/deriving
diff options
context:
space:
mode:
authorJosh Driver <keeperofdakeys@gmail.com>2016-11-08 21:45:02 +1030
committerJosh Driver <keeperofdakeys@gmail.com>2016-11-08 23:03:56 +1030
commit31a508e1185713c6e570bb963dd3e097a228957c (patch)
tree8f4e844613e956fc6d90b391b89b459e4bdc85c4 /src/libsyntax_ext/deriving
parentd377cf5b3fbaae4baa67b4f29a952b565ef1a814 (diff)
downloadrust-31a508e1185713c6e570bb963dd3e097a228957c.tar.gz
rust-31a508e1185713c6e570bb963dd3e097a228957c.zip
Allow proc_macro functions to whitelist specific attributes
By using a second attribute `attributes(Bar)` on
proc_macro_derive, whitelist any attributes with
the name `Bar` in the deriving item. This allows
a proc_macro function to use custom attribtues
without a custom attribute error or unused attribute
lint.
Diffstat (limited to 'src/libsyntax_ext/deriving')
-rw-r--r--src/libsyntax_ext/deriving/custom.rs44
1 files changed, 33 insertions, 11 deletions
diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs
index f8cb1294a66..2b80deded0a 100644
--- a/src/libsyntax_ext/deriving/custom.rs
+++ b/src/libsyntax_ext/deriving/custom.rs
@@ -12,20 +12,37 @@ use std::panic;
 
 use errors::FatalError;
 use proc_macro::{TokenStream, __internal};
-use syntax::ast::{self, ItemKind};
+use syntax::ast::{self, ItemKind, Attribute};
+use syntax::attr::{mark_used, mark_known};
 use syntax::codemap::{ExpnInfo, MacroAttribute, NameAndSpan, Span};
 use syntax::ext::base::*;
 use syntax::fold::Folder;
+use syntax::parse::token::InternedString;
 use syntax::parse::token::intern;
 use syntax::print::pprust;
+use syntax::visit::Visitor;
+
+struct MarkAttrs<'a>(&'a [InternedString]);
+
+impl<'a> Visitor for MarkAttrs<'a> {
+    fn visit_attribute(&mut self, attr: &Attribute) {
+        if self.0.contains(&attr.name()) {
+            mark_used(attr);
+            mark_known(attr);
+        }
+    }
+}
 
 pub struct CustomDerive {
     inner: fn(TokenStream) -> TokenStream,
+    attrs: Vec<InternedString>,
 }
 
 impl CustomDerive {
-    pub fn new(inner: fn(TokenStream) -> TokenStream) -> CustomDerive {
-        CustomDerive { inner: inner }
+    pub fn new(inner: fn(TokenStream) -> TokenStream,
+               attrs: Vec<InternedString>)
+               -> CustomDerive {
+        CustomDerive { inner: inner, attrs: attrs }
     }
 }
 
@@ -47,7 +64,7 @@ impl MultiItemModifier for CustomDerive {
         };
         match item.node {
             ItemKind::Struct(..) |
-            ItemKind::Enum(..) => {}
+            ItemKind::Enum(..) => {},
             _ => {
                 ecx.span_err(span, "custom derive attributes may only be \
                                     applied to struct/enum items");
@@ -55,6 +72,9 @@ impl MultiItemModifier for CustomDerive {
             }
         }
 
+        // Mark attributes as known, and used.
+        MarkAttrs(&self.attrs).visit_item(&item);
+
         let input_span = Span {
             expn_id: ecx.codemap().record_expansion(ExpnInfo {
                 call_site: span,
@@ -66,12 +86,13 @@ impl MultiItemModifier for CustomDerive {
             }),
             ..item.span
         };
-        let input = __internal::new_token_stream(item);
+
+        let input = __internal::new_token_stream(item.clone());
         let res = __internal::set_parse_sess(&ecx.parse_sess, || {
             let inner = self.inner;
             panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
         });
-        let item = match res {
+        let new_items = match res {
             Ok(stream) => __internal::token_stream_items(stream),
             Err(e) => {
                 let msg = "custom derive attribute panicked";
@@ -88,12 +109,13 @@ impl MultiItemModifier for CustomDerive {
             }
         };
 
-        // Right now we have no knowledge of spans at all in custom derive
-        // macros, everything is just parsed as a string. Reassign all spans to
-        // the input `item` for better errors here.
-        item.into_iter().flat_map(|item| {
+        let mut res = vec![Annotatable::Item(item)];
+        // Reassign spans of all expanded items to the input `item`
+        // for better errors here.
+        res.extend(new_items.into_iter().flat_map(|item| {
             ChangeSpan { span: input_span }.fold_item(item)
-        }).map(Annotatable::Item).collect()
+        }).map(Annotatable::Item));
+        res
     }
 }