Add support for registering attributes with rustc in plugins

This lets plugin authors opt attributes out of the `custom_attribute`
and `unused_attribute` checks.
This commit is contained in:
Manish Goregaokar 2015-05-06 22:08:36 +05:30
parent fc45fd99f5
commit 8dc6e16933
5 changed files with 72 additions and 17 deletions

View file

@ -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!(),
}
}
@ -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));
}
}

View file

@ -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>,
@ -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),

View file

@ -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();
});

View file

@ -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]",

View file

@ -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<F>(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<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();
@ -765,14 +778,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))
}