Auto merge of #25168 - Manishearth:register_attr, r=eddyb
This lets plugin authors opt attributes out of the `custom_attribute` and `unused_attribute` checks. cc @thepowersgang
This commit is contained in:
commit
cc56c20ba4
@ -20,6 +20,7 @@ use syntax::codemap::Span;
|
|||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
use syntax::feature_gate::AttributeType;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
@ -54,6 +55,9 @@ pub struct Registry<'a> {
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub llvm_passes: Vec<String>,
|
pub llvm_passes: Vec<String>,
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub attributes: Vec<(String, AttributeType)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Registry<'a> {
|
impl<'a> Registry<'a> {
|
||||||
@ -67,6 +71,7 @@ impl<'a> Registry<'a> {
|
|||||||
lint_passes: vec!(),
|
lint_passes: vec!(),
|
||||||
lint_groups: HashMap::new(),
|
lint_groups: HashMap::new(),
|
||||||
llvm_passes: vec!(),
|
llvm_passes: vec!(),
|
||||||
|
attributes: vec!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,4 +137,19 @@ impl<'a> Registry<'a> {
|
|||||||
pub fn register_llvm_pass(&mut self, name: &str) {
|
pub fn register_llvm_pass(&mut self, name: &str) {
|
||||||
self.llvm_passes.push(name.to_owned());
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ use syntax::parse;
|
|||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::parse::ParseSess;
|
use syntax::parse::ParseSess;
|
||||||
use syntax::{ast, codemap};
|
use syntax::{ast, codemap};
|
||||||
|
use syntax::feature_gate::AttributeType;
|
||||||
|
|
||||||
use rustc_back::target::Target;
|
use rustc_back::target::Target;
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ pub struct Session {
|
|||||||
pub lint_store: RefCell<lint::LintStore>,
|
pub lint_store: RefCell<lint::LintStore>,
|
||||||
pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
|
pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
|
||||||
pub plugin_llvm_passes: RefCell<Vec<String>>,
|
pub plugin_llvm_passes: RefCell<Vec<String>>,
|
||||||
|
pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
|
||||||
pub crate_types: RefCell<Vec<config::CrateType>>,
|
pub crate_types: RefCell<Vec<config::CrateType>>,
|
||||||
pub crate_metadata: RefCell<Vec<String>>,
|
pub crate_metadata: RefCell<Vec<String>>,
|
||||||
pub features: RefCell<feature_gate::Features>,
|
pub features: RefCell<feature_gate::Features>,
|
||||||
@ -425,6 +427,7 @@ pub fn build_session_(sopts: config::Options,
|
|||||||
lint_store: RefCell::new(lint::LintStore::new()),
|
lint_store: RefCell::new(lint::LintStore::new()),
|
||||||
lints: RefCell::new(NodeMap()),
|
lints: RefCell::new(NodeMap()),
|
||||||
plugin_llvm_passes: RefCell::new(Vec::new()),
|
plugin_llvm_passes: RefCell::new(Vec::new()),
|
||||||
|
plugin_attributes: RefCell::new(Vec::new()),
|
||||||
crate_types: RefCell::new(Vec::new()),
|
crate_types: RefCell::new(Vec::new()),
|
||||||
crate_metadata: RefCell::new(Vec::new()),
|
crate_metadata: RefCell::new(Vec::new()),
|
||||||
delayed_span_bug: RefCell::new(None),
|
delayed_span_bug: RefCell::new(None),
|
||||||
|
@ -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();
|
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_llvm_passes.borrow_mut() = llvm_passes;
|
||||||
|
*sess.plugin_attributes.borrow_mut() = attributes.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lint plugins are registered; now we can process command line flags.
|
// 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 =
|
let features =
|
||||||
syntax::feature_gate::check_crate(sess.codemap(),
|
syntax::feature_gate::check_crate(sess.codemap(),
|
||||||
&sess.parse_sess.span_diagnostic,
|
&sess.parse_sess.span_diagnostic,
|
||||||
&krate);
|
&krate, &attributes);
|
||||||
*sess.features.borrow_mut() = features;
|
*sess.features.borrow_mut() = features;
|
||||||
sess.abort_if_errors();
|
sess.abort_if_errors();
|
||||||
});
|
});
|
||||||
@ -541,7 +543,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
|
|||||||
let features =
|
let features =
|
||||||
syntax::feature_gate::check_crate(sess.codemap(),
|
syntax::feature_gate::check_crate(sess.codemap(),
|
||||||
&sess.parse_sess.span_diagnostic,
|
&sess.parse_sess.span_diagnostic,
|
||||||
&krate);
|
&krate, &attributes);
|
||||||
*sess.features.borrow_mut() = features;
|
*sess.features.borrow_mut() = features;
|
||||||
sess.abort_if_errors();
|
sess.abort_if_errors();
|
||||||
});
|
});
|
||||||
|
@ -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) {
|
if !attr::is_used(attr) {
|
||||||
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
|
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 {
|
let msg = match attr.node.style {
|
||||||
ast::AttrOuter => "crate-level attribute should be an inner \
|
ast::AttrOuter => "crate-level attribute should be an inner \
|
||||||
attribute: add an exclamation mark: #![foo]",
|
attribute: add an exclamation mark: #![foo]",
|
||||||
|
@ -358,6 +358,7 @@ struct Context<'a> {
|
|||||||
features: Vec<&'static str>,
|
features: Vec<&'static str>,
|
||||||
span_handler: &'a SpanHandler,
|
span_handler: &'a SpanHandler,
|
||||||
cm: &'a CodeMap,
|
cm: &'a CodeMap,
|
||||||
|
plugin_attributes: &'a [(String, AttributeType)],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
@ -372,7 +373,7 @@ impl<'a> Context<'a> {
|
|||||||
self.features.iter().any(|&n| n == feature)
|
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);
|
debug!("check_attribute(attr = {:?})", attr);
|
||||||
let name = &*attr.name();
|
let name = &*attr.name();
|
||||||
for &(n, ty) in KNOWN_ATTRIBUTES {
|
for &(n, ty) in KNOWN_ATTRIBUTES {
|
||||||
@ -384,6 +385,15 @@ impl<'a> Context<'a> {
|
|||||||
return;
|
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_") {
|
if name.starts_with("rustc_") {
|
||||||
self.gate_feature("rustc_attrs", attr.span,
|
self.gate_feature("rustc_attrs", attr.span,
|
||||||
"unless otherwise specified, attributes \
|
"unless otherwise specified, attributes \
|
||||||
@ -394,6 +404,11 @@ impl<'a> Context<'a> {
|
|||||||
"attributes of the form `#[derive_*]` are reserved \
|
"attributes of the form `#[derive_*]` are reserved \
|
||||||
for the compiler");
|
for the compiler");
|
||||||
} else {
|
} else {
|
||||||
|
// 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,
|
self.gate_feature("custom_attribute", attr.span,
|
||||||
&format!("The attribute `{}` is currently \
|
&format!("The attribute `{}` is currently \
|
||||||
unknown to the compiler and \
|
unknown to the compiler and \
|
||||||
@ -403,6 +418,7 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) {
|
pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) {
|
||||||
diag.span_err(span, explain);
|
diag.span_err(span, explain);
|
||||||
@ -478,7 +494,7 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
|
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> {
|
impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
|
||||||
fn visit_attribute(&mut self, attr: &ast::Attribute) {
|
fn visit_attribute(&mut self, attr: &ast::Attribute) {
|
||||||
if !self.context.cm.span_allows_unstable(attr.span) {
|
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,
|
fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
|
||||||
krate: &ast::Crate,
|
krate: &ast::Crate,
|
||||||
|
plugin_attributes: &[(String, AttributeType)],
|
||||||
check: F)
|
check: F)
|
||||||
-> Features
|
-> Features
|
||||||
where F: FnOnce(&mut Context, &ast::Crate)
|
where F: FnOnce(&mut Context, &ast::Crate)
|
||||||
@ -692,6 +709,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
|
|||||||
features: Vec::new(),
|
features: Vec::new(),
|
||||||
span_handler: span_handler,
|
span_handler: span_handler,
|
||||||
cm: cm,
|
cm: cm,
|
||||||
|
plugin_attributes: plugin_attributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut accepted_features = Vec::new();
|
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)
|
pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
|
||||||
-> Features {
|
-> 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))
|
|ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
|
pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
|
||||||
-> Features
|
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 },
|
|ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
|
||||||
krate))
|
krate))
|
||||||
}
|
}
|
||||||
|
30
src/test/auxiliary/attr_plugin_test.rs
Normal file
30
src/test/auxiliary/attr_plugin_test.rs
Normal file
@ -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);
|
||||||
|
}
|
30
src/test/compile-fail-fulldeps/plugin-attr-register-deny.rs
Normal file
30
src/test/compile-fail-fulldeps/plugin-attr-register-deny.rs
Normal file
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user