diff --git a/src/librustc/plugin/registry.rs b/src/librustc/plugin/registry.rs index 322b5d3a8cf2..c85b30c811cd 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, + + #[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!(), } } @@ -130,4 +135,22 @@ 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.err("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 7a8ce1bf48e4..780fff9b8384 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, pub lints: RefCell>>, pub plugin_llvm_passes: RefCell>, + pub plugin_attributes: RefCell>, pub crate_types: RefCell>, pub crate_metadata: RefCell>, pub features: RefCell, @@ -416,6 +418,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 45d81ff0f652..70ee041b7195 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 19e71d6e40d5..9242274a7a3b 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -641,9 +641,23 @@ impl LintPass for UnusedAttributes { } } + let plugin_attributes = cx.sess().plugin_attributes.borrow_mut(); + for &(ref name, ty) in plugin_attributes.iter() { + match ty { + AttributeType::Whitelisted if 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)) { + if KNOWN_ATTRIBUTES.contains(&(&attr.name(), AttributeType::CrateLevel)) || + plugin_attributes.iter() + .find(|&&(ref x, t)| &*attr.name() == &*x && + AttributeType::CrateLevel == t) + .is_some() { 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 28deb4eec3fa..abb4ce15996c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -359,6 +359,7 @@ struct Context<'a> { features: Vec<&'static str>, span_handler: &'a SpanHandler, cm: &'a CodeMap, + plugin_attributes: &'a [(String, AttributeType)], } impl<'a> Context<'a> { @@ -373,7 +374,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 { @@ -385,6 +386,13 @@ 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 + 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 \ @@ -395,12 +403,15 @@ 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 do the custom attribute lint post-expansion + 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)); + } } } } @@ -479,7 +490,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); } } @@ -498,7 +509,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); } } @@ -685,6 +696,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate, + plugin_attributes: &[(String, AttributeType)], check: F) -> Features where F: FnOnce(&mut Context, &ast::Crate) @@ -693,6 +705,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, features: Vec::new(), span_handler: span_handler, cm: cm, + plugin_attributes: plugin_attributes, }; let mut accepted_features = Vec::new(); @@ -765,14 +778,14 @@ fn check_crate_inner(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)) }