Skip to content

Commit

Permalink
Add support for registering attributes with rustc in plugins
Browse files Browse the repository at this point in the history
This lets plugin authors opt attributes out of the `custom_attribute`
and `unused_attribute` checks.
  • Loading branch information
Manishearth committed May 7, 2015
1 parent fc45fd9 commit 8dc6e16
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 17 deletions.
23 changes: 23 additions & 0 deletions src/librustc/plugin/registry.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand All @@ -67,6 +71,7 @@ impl<'a> Registry<'a> {
lint_passes: vec!(),
lint_groups: HashMap::new(),
llvm_passes: vec!(),
attributes: vec!(),
}
}

Expand Down Expand Up @@ -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));
}
}
3 changes: 3 additions & 0 deletions src/librustc/session/mod.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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),
Expand Down
8 changes: 5 additions & 3 deletions src/librustc_driver/driver.rs
Expand Up @@ -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();
Expand All @@ -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.
Expand Down Expand Up @@ -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();
});
Expand Down Expand Up @@ -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();
});
Expand Down
16 changes: 15 additions & 1 deletion src/librustc_lint/builtin.rs
Expand Up @@ -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]",
Expand Down
39 changes: 26 additions & 13 deletions src/libsyntax/feature_gate.rs
Expand Up @@ -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> {
Expand All @@ -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 {
Expand All @@ -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 \
Expand All @@ -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));
}
}
}
}
Expand Down Expand Up @@ -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);
}
}

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

Expand Down Expand Up @@ -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)
Expand All @@ -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();
Expand Down Expand Up @@ -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))
}

0 comments on commit 8dc6e16

Please sign in to comment.