about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/plugin/registry.rs20
-rw-r--r--src/librustc/session/mod.rs3
-rw-r--r--src/librustc_driver/driver.rs8
-rw-r--r--src/librustc_lint/builtin.rs19
-rw-r--r--src/libsyntax/feature_gate.rs44
-rw-r--r--src/test/auxiliary/attr_plugin_test.rs30
-rw-r--r--src/test/compile-fail-fulldeps/plugin-attr-register-deny.rs30
7 files changed, 137 insertions, 17 deletions
diff --git a/src/librustc/plugin/registry.rs b/src/librustc/plugin/registry.rs
index 04df4641295..5faaa53e4d0 100644
--- a/src/librustc/plugin/registry.rs
+++ b/src/librustc/plugin/registry.rs
@@ -20,6 +20,7 @@ use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ptr::P;
 use syntax::ast;
+use syntax::feature_gate::AttributeType;
 
 use std::collections::HashMap;
 use std::borrow::ToOwned;
@@ -54,6 +55,9 @@ pub struct Registry<'a> {
 
     #[doc(hidden)]
     pub llvm_passes: Vec<String>,
+
+    #[doc(hidden)]
+    pub attributes: Vec<(String, AttributeType)>,
 }
 
 impl<'a> Registry<'a> {
@@ -67,6 +71,7 @@ impl<'a> Registry<'a> {
             lint_passes: vec!(),
             lint_groups: HashMap::new(),
             llvm_passes: vec!(),
+            attributes: vec!(),
         }
     }
 
@@ -132,4 +137,19 @@ impl<'a> Registry<'a> {
     pub fn register_llvm_pass(&mut self, name: &str) {
         self.llvm_passes.push(name.to_owned());
     }
+
+
+    /// Register an attribute with an attribute type.
+    ///
+    /// Registered attributes will bypass the `custom_attribute` feature gate.
+    /// `Whitelisted` attributes will additionally not trigger the `unused_attribute`
+    /// lint. `CrateLevel` attributes will not be allowed on anything other than a crate.
+    pub fn register_attribute(&mut self, name: String, ty: AttributeType) {
+        if let AttributeType::Gated(..) = ty {
+            self.sess.span_err(self.krate_span, "plugin tried to register a gated \
+                                                 attribute. Only `Normal`, `Whitelisted`, \
+                                                 and `CrateLevel` attributes are allowed");
+        }
+        self.attributes.push((name, ty));
+    }
 }
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 87634886009..6b5f58720ab 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -23,6 +23,7 @@ use syntax::parse;
 use syntax::parse::token;
 use syntax::parse::ParseSess;
 use syntax::{ast, codemap};
+use syntax::feature_gate::AttributeType;
 
 use rustc_back::target::Target;
 
@@ -54,6 +55,7 @@ pub struct Session {
     pub lint_store: RefCell<lint::LintStore>,
     pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
     pub plugin_llvm_passes: RefCell<Vec<String>>,
+    pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
     pub crate_types: RefCell<Vec<config::CrateType>>,
     pub crate_metadata: RefCell<Vec<String>>,
     pub features: RefCell<feature_gate::Features>,
@@ -425,6 +427,7 @@ pub fn build_session_(sopts: config::Options,
         lint_store: RefCell::new(lint::LintStore::new()),
         lints: RefCell::new(NodeMap()),
         plugin_llvm_passes: RefCell::new(Vec::new()),
+        plugin_attributes: RefCell::new(Vec::new()),
         crate_types: RefCell::new(Vec::new()),
         crate_metadata: RefCell::new(Vec::new()),
         delayed_span_bug: RefCell::new(None),
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 9c78c5aec00..7c49f1e01d8 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -444,7 +444,8 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         }
     });
 
-    let Registry { syntax_exts, lint_passes, lint_groups, llvm_passes, .. } = registry;
+    let Registry { syntax_exts, lint_passes, lint_groups,
+                   llvm_passes, attributes, .. } = registry;
 
     {
         let mut ls = sess.lint_store.borrow_mut();
@@ -457,6 +458,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         }
 
         *sess.plugin_llvm_passes.borrow_mut() = llvm_passes;
+        *sess.plugin_attributes.borrow_mut() = attributes.clone();
     }
 
     // Lint plugins are registered; now we can process command line flags.
@@ -511,7 +513,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         let features =
             syntax::feature_gate::check_crate(sess.codemap(),
                                               &sess.parse_sess.span_diagnostic,
-                                              &krate);
+                                              &krate, &attributes);
         *sess.features.borrow_mut() = features;
         sess.abort_if_errors();
     });
@@ -541,7 +543,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         let features =
             syntax::feature_gate::check_crate(sess.codemap(),
                                               &sess.parse_sess.span_diagnostic,
-                                              &krate);
+                                              &krate, &attributes);
         *sess.features.borrow_mut() = features;
         sess.abort_if_errors();
     });
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 6721c4a6636..f50abc7b3ad 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -641,9 +641,26 @@ impl LintPass for UnusedAttributes {
             }
         }
 
+        let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
+        for &(ref name, ty) in plugin_attributes.iter() {
+            if ty == AttributeType::Whitelisted && attr.check_name(&*name) {
+                break;
+            }
+        }
+
         if !attr::is_used(attr) {
             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
-            if KNOWN_ATTRIBUTES.contains(&(&attr.name(), AttributeType::CrateLevel)) {
+            // Is it a builtin attribute that must be used at the crate level?
+            let known_crate = KNOWN_ATTRIBUTES.contains(&(&attr.name(),
+                                                          AttributeType::CrateLevel));
+            // Has a plugin registered this attribute as one which must be used at
+            // the crate level?
+            let plugin_crate = plugin_attributes.iter()
+                                                .find(|&&(ref x, t)| {
+                                                        &*attr.name() == &*x &&
+                                                        AttributeType::CrateLevel == t
+                                                    }).is_some();
+            if  known_crate || plugin_crate {
                 let msg = match attr.node.style {
                     ast::AttrOuter => "crate-level attribute should be an inner \
                                        attribute: add an exclamation mark: #![foo]",
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index d79714248c8..bd73e6b7de7 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -358,6 +358,7 @@ struct Context<'a> {
     features: Vec<&'static str>,
     span_handler: &'a SpanHandler,
     cm: &'a CodeMap,
+    plugin_attributes: &'a [(String, AttributeType)],
 }
 
 impl<'a> Context<'a> {
@@ -372,7 +373,7 @@ impl<'a> Context<'a> {
         self.features.iter().any(|&n| n == feature)
     }
 
-    fn check_attribute(&self, attr: &ast::Attribute) {
+    fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
         debug!("check_attribute(attr = {:?})", attr);
         let name = &*attr.name();
         for &(n, ty) in KNOWN_ATTRIBUTES {
@@ -384,6 +385,15 @@ impl<'a> Context<'a> {
                 return;
             }
         }
+        for &(ref n, ref ty) in self.plugin_attributes.iter() {
+            if &*n == name {
+                // Plugins can't gate attributes, so we don't check for it
+                // unlike the code above; we only use this loop to
+                // short-circuit to avoid the checks below
+                debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty);
+                return;
+            }
+        }
         if name.starts_with("rustc_") {
             self.gate_feature("rustc_attrs", attr.span,
                               "unless otherwise specified, attributes \
@@ -394,12 +404,18 @@ impl<'a> Context<'a> {
                               "attributes of the form `#[derive_*]` are reserved \
                                for the compiler");
         } else {
-            self.gate_feature("custom_attribute", attr.span,
-                       &format!("The attribute `{}` is currently \
-                                unknown to the compiler and \
-                                may have meaning \
-                                added to it in the future",
-                                name));
+            // Only run the custom attribute lint during regular
+            // feature gate checking. Macro gating runs
+            // before the plugin attributes are registered
+            // so we skip this then
+            if !is_macro {
+                self.gate_feature("custom_attribute", attr.span,
+                           &format!("The attribute `{}` is currently \
+                                    unknown to the compiler and \
+                                    may have meaning \
+                                    added to it in the future",
+                                    name));
+            }
         }
     }
 }
@@ -478,7 +494,7 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
     }
 
     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
-        self.context.check_attribute(attr);
+        self.context.check_attribute(attr, true);
     }
 }
 
@@ -497,7 +513,7 @@ impl<'a> PostExpansionVisitor<'a> {
 impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     fn visit_attribute(&mut self, attr: &ast::Attribute) {
         if !self.context.cm.span_allows_unstable(attr.span) {
-            self.context.check_attribute(attr);
+            self.context.check_attribute(attr, false);
         }
     }
 
@@ -684,6 +700,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
 
 fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
                         krate: &ast::Crate,
+                        plugin_attributes: &[(String, AttributeType)],
                         check: F)
                        -> Features
     where F: FnOnce(&mut Context, &ast::Crate)
@@ -692,6 +709,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
         features: Vec::new(),
         span_handler: span_handler,
         cm: cm,
+        plugin_attributes: plugin_attributes,
     };
 
     let mut accepted_features = Vec::new();
@@ -764,14 +782,14 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
 
 pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
 -> Features {
-    check_crate_inner(cm, span_handler, krate,
+    check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
                       |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
 }
 
-pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
-                   -> Features
+pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
+                   plugin_attributes: &[(String, AttributeType)]) -> Features
 {
-    check_crate_inner(cm, span_handler, krate,
+    check_crate_inner(cm, span_handler, krate, plugin_attributes,
                       |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
                                                      krate))
 }
diff --git a/src/test/auxiliary/attr_plugin_test.rs b/src/test/auxiliary/attr_plugin_test.rs
new file mode 100644
index 00000000000..a6cae743ceb
--- /dev/null
+++ b/src/test/auxiliary/attr_plugin_test.rs
@@ -0,0 +1,30 @@
+// Copyright 2014 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
+
+#![feature(plugin_registrar)]
+#![feature(rustc_private)]
+
+extern crate syntax;
+
+extern crate rustc;
+
+use syntax::feature_gate::AttributeType;
+use rustc::plugin::Registry;
+
+
+
+#[plugin_registrar]
+pub fn plugin_registrar(reg: &mut Registry) {
+    reg.register_attribute("foo".to_owned(), AttributeType::Normal);
+    reg.register_attribute("bar".to_owned(), AttributeType::CrateLevel);
+    reg.register_attribute("baz".to_owned(), AttributeType::Whitelisted);
+}
diff --git a/src/test/compile-fail-fulldeps/plugin-attr-register-deny.rs b/src/test/compile-fail-fulldeps/plugin-attr-register-deny.rs
new file mode 100644
index 00000000000..0d2a5a30c10
--- /dev/null
+++ b/src/test/compile-fail-fulldeps/plugin-attr-register-deny.rs
@@ -0,0 +1,30 @@
+// Copyright 2014 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:attr_plugin_test.rs
+// ignore-stage1
+
+#![feature(plugin)]
+#![plugin(attr_plugin_test)]
+#![deny(unused_attributes)]
+
+#[baz]
+fn baz() { } // no error
+
+#[foo]
+pub fn main() {
+     //~^^ ERROR unused
+    #[bar]
+    fn inner() {}
+    //~^^ ERROR crate
+    //~^^^ ERROR unused
+    baz();
+    inner();
+}