Handle gateage of built-in attributes seperately
This allows marking attributes as whitelisted/crate-only independent of their feature gate status. Closes #24213
This commit is contained in:
parent
6f28232756
commit
4b571b055d
4 changed files with 143 additions and 121 deletions
|
@ -145,11 +145,6 @@ impl<'a> Registry<'a> {
|
||||||
/// `Whitelisted` attributes will additionally not trigger the `unused_attribute`
|
/// `Whitelisted` attributes will additionally not trigger the `unused_attribute`
|
||||||
/// lint. `CrateLevel` attributes will not be allowed on anything other than a crate.
|
/// lint. `CrateLevel` attributes will not be allowed on anything other than a crate.
|
||||||
pub fn register_attribute(&mut self, name: String, ty: AttributeType) {
|
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));
|
self.attributes.push((name, ty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -887,10 +887,9 @@ impl LintPass for UnusedAttributes {
|
||||||
|
|
||||||
fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
|
fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
|
||||||
// Note that check_name() marks the attribute as used if it matches.
|
// Note that check_name() marks the attribute as used if it matches.
|
||||||
for &(ref name, ty) in KNOWN_ATTRIBUTES {
|
for &(ref name, ty, _) in KNOWN_ATTRIBUTES {
|
||||||
match ty {
|
match ty {
|
||||||
AttributeType::Whitelisted
|
AttributeType::Whitelisted if attr.check_name(name) => {
|
||||||
| AttributeType::Gated(_, _) if attr.check_name(name) => {
|
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => ()
|
||||||
|
@ -907,8 +906,11 @@ impl LintPass for UnusedAttributes {
|
||||||
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");
|
||||||
// Is it a builtin attribute that must be used at the crate level?
|
// Is it a builtin attribute that must be used at the crate level?
|
||||||
let known_crate = KNOWN_ATTRIBUTES.contains(&(&attr.name(),
|
let known_crate = KNOWN_ATTRIBUTES.iter().find(|&&(name, ty, _)| {
|
||||||
AttributeType::CrateLevel));
|
attr.name() == name &&
|
||||||
|
ty == AttributeType::CrateLevel
|
||||||
|
}).is_some();
|
||||||
|
|
||||||
// Has a plugin registered this attribute as one which must be used at
|
// Has a plugin registered this attribute as one which must be used at
|
||||||
// the crate level?
|
// the crate level?
|
||||||
let plugin_crate = plugin_attributes.iter()
|
let plugin_crate = plugin_attributes.iter()
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
use self::Status::*;
|
use self::Status::*;
|
||||||
use self::AttributeType::*;
|
use self::AttributeType::*;
|
||||||
|
use self::AttributeGate::*;
|
||||||
|
|
||||||
use abi::Abi;
|
use abi::Abi;
|
||||||
use ast::NodeId;
|
use ast::NodeId;
|
||||||
|
@ -203,135 +204,137 @@ enum Status {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes that have a special meaning to rustc or rustdoc
|
// Attributes that have a special meaning to rustc or rustdoc
|
||||||
pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
|
pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[
|
||||||
// Normal attributes
|
// Normal attributes
|
||||||
|
|
||||||
("warn", Normal),
|
("warn", Normal, Ungated),
|
||||||
("allow", Normal),
|
("allow", Normal, Ungated),
|
||||||
("forbid", Normal),
|
("forbid", Normal, Ungated),
|
||||||
("deny", Normal),
|
("deny", Normal, Ungated),
|
||||||
|
|
||||||
("macro_reexport", Normal),
|
("macro_reexport", Normal, Ungated),
|
||||||
("macro_use", Normal),
|
("macro_use", Normal, Ungated),
|
||||||
("macro_export", Normal),
|
("macro_export", Normal, Ungated),
|
||||||
("plugin_registrar", Normal),
|
("plugin_registrar", Normal, Ungated),
|
||||||
|
|
||||||
("cfg", Normal),
|
("cfg", Normal, Ungated),
|
||||||
("cfg_attr", Normal),
|
("cfg_attr", Normal, Ungated),
|
||||||
("main", Normal),
|
("main", Normal, Ungated),
|
||||||
("start", Normal),
|
("start", Normal, Ungated),
|
||||||
("test", Normal),
|
("test", Normal, Ungated),
|
||||||
("bench", Normal),
|
("bench", Normal, Ungated),
|
||||||
("simd", Normal),
|
("simd", Normal, Ungated),
|
||||||
("repr", Normal),
|
("repr", Normal, Ungated),
|
||||||
("path", Normal),
|
("path", Normal, Ungated),
|
||||||
("abi", Normal),
|
("abi", Normal, Ungated),
|
||||||
("automatically_derived", Normal),
|
("automatically_derived", Normal, Ungated),
|
||||||
("no_mangle", Normal),
|
("no_mangle", Normal, Ungated),
|
||||||
("no_link", Normal),
|
("no_link", Normal, Ungated),
|
||||||
("derive", Normal),
|
("derive", Normal, Ungated),
|
||||||
("should_panic", Normal),
|
("should_panic", Normal, Ungated),
|
||||||
("ignore", Normal),
|
("ignore", Normal, Ungated),
|
||||||
("no_implicit_prelude", Normal),
|
("no_implicit_prelude", Normal, Ungated),
|
||||||
("reexport_test_harness_main", Normal),
|
("reexport_test_harness_main", Normal, Ungated),
|
||||||
("link_args", Normal),
|
("link_args", Normal, Ungated),
|
||||||
("macro_escape", Normal),
|
("macro_escape", Normal, Ungated),
|
||||||
|
|
||||||
// Not used any more, but we can't feature gate it
|
// Not used any more, but we can't feature gate it
|
||||||
("no_stack_check", Normal),
|
("no_stack_check", Normal, Ungated),
|
||||||
|
|
||||||
("staged_api", Gated("staged_api",
|
("staged_api", CrateLevel, Gated("staged_api",
|
||||||
"staged_api is for use by rustc only")),
|
"staged_api is for use by rustc only")),
|
||||||
("plugin", Gated("plugin",
|
("plugin", CrateLevel, Gated("plugin",
|
||||||
"compiler plugins are experimental \
|
"compiler plugins are experimental \
|
||||||
and possibly buggy")),
|
and possibly buggy")),
|
||||||
("no_std", Gated("no_std",
|
("no_std", CrateLevel, Gated("no_std",
|
||||||
"no_std is experimental")),
|
"no_std is experimental")),
|
||||||
("no_core", Gated("no_core",
|
("no_core", CrateLevel, Gated("no_core",
|
||||||
"no_core is experimental")),
|
"no_core is experimental")),
|
||||||
("lang", Gated("lang_items",
|
("lang", Normal, Gated("lang_items",
|
||||||
"language items are subject to change")),
|
"language items are subject to change")),
|
||||||
("linkage", Gated("linkage",
|
("linkage", Whitelisted, Gated("linkage",
|
||||||
"the `linkage` attribute is experimental \
|
"the `linkage` attribute is experimental \
|
||||||
and not portable across platforms")),
|
and not portable across platforms")),
|
||||||
("thread_local", Gated("thread_local",
|
("thread_local", Whitelisted, Gated("thread_local",
|
||||||
"`#[thread_local]` is an experimental feature, and does not \
|
"`#[thread_local]` is an experimental feature, and does \
|
||||||
currently handle destructors. There is no corresponding \
|
not currently handle destructors. There is no \
|
||||||
`#[task_local]` mapping to the task model")),
|
corresponding `#[task_local]` mapping to the task \
|
||||||
|
model")),
|
||||||
|
|
||||||
("rustc_on_unimplemented", Gated("on_unimplemented",
|
("rustc_on_unimplemented", Normal, Gated("on_unimplemented",
|
||||||
"the `#[rustc_on_unimplemented]` attribute \
|
"the `#[rustc_on_unimplemented]` attribute \
|
||||||
|
is an experimental feature")),
|
||||||
|
("allocator", Whitelisted, Gated("allocator",
|
||||||
|
"the `#[allocator]` attribute is an experimental feature")),
|
||||||
|
("needs_allocator", Normal, Gated("needs_allocator",
|
||||||
|
"the `#[needs_allocator]` \
|
||||||
|
attribute is an experimental \
|
||||||
|
feature")),
|
||||||
|
("rustc_variance", Normal, Gated("rustc_attrs",
|
||||||
|
"the `#[rustc_variance]` attribute \
|
||||||
is an experimental feature")),
|
is an experimental feature")),
|
||||||
("allocator", Gated("allocator",
|
("rustc_error", Whitelisted, Gated("rustc_attrs",
|
||||||
"the `#[allocator]` attribute is an experimental feature")),
|
"the `#[rustc_error]` attribute \
|
||||||
("needs_allocator", Gated("needs_allocator", "the `#[needs_allocator]` \
|
is an experimental feature")),
|
||||||
attribute is an experimental \
|
("rustc_move_fragments", Normal, Gated("rustc_attrs",
|
||||||
feature")),
|
"the `#[rustc_move_fragments]` attribute \
|
||||||
("rustc_variance", Gated("rustc_attrs",
|
is an experimental feature")),
|
||||||
"the `#[rustc_variance]` attribute \
|
|
||||||
is an experimental feature")),
|
|
||||||
("rustc_error", Gated("rustc_attrs",
|
|
||||||
"the `#[rustc_error]` attribute \
|
|
||||||
is an experimental feature")),
|
|
||||||
("rustc_move_fragments", Gated("rustc_attrs",
|
|
||||||
"the `#[rustc_move_fragments]` attribute \
|
|
||||||
is an experimental feature")),
|
|
||||||
|
|
||||||
("allow_internal_unstable", Gated("allow_internal_unstable",
|
("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
|
||||||
EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
|
EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
|
||||||
|
|
||||||
("fundamental", Gated("fundamental",
|
("fundamental", Whitelisted, Gated("fundamental",
|
||||||
"the `#[fundamental]` attribute \
|
"the `#[fundamental]` attribute \
|
||||||
is an experimental feature")),
|
is an experimental feature")),
|
||||||
|
|
||||||
("linked_from", Gated("linked_from",
|
("linked_from", Normal, Gated("linked_from",
|
||||||
"the `#[linked_from]` attribute \
|
"the `#[linked_from]` attribute \
|
||||||
is an experimental feature")),
|
is an experimental feature")),
|
||||||
|
|
||||||
// FIXME: #14408 whitelist docs since rustdoc looks at them
|
// FIXME: #14408 whitelist docs since rustdoc looks at them
|
||||||
("doc", Whitelisted),
|
("doc", Whitelisted, Ungated),
|
||||||
|
|
||||||
// FIXME: #14406 these are processed in trans, which happens after the
|
// FIXME: #14406 these are processed in trans, which happens after the
|
||||||
// lint pass
|
// lint pass
|
||||||
("cold", Whitelisted),
|
("cold", Whitelisted, Ungated),
|
||||||
("export_name", Whitelisted),
|
("export_name", Whitelisted, Ungated),
|
||||||
("inline", Whitelisted),
|
("inline", Whitelisted, Ungated),
|
||||||
("link", Whitelisted),
|
("link", Whitelisted, Ungated),
|
||||||
("link_name", Whitelisted),
|
("link_name", Whitelisted, Ungated),
|
||||||
("link_section", Whitelisted),
|
("link_section", Whitelisted, Ungated),
|
||||||
("no_builtins", Whitelisted),
|
("no_builtins", Whitelisted, Ungated),
|
||||||
("no_mangle", Whitelisted),
|
("no_mangle", Whitelisted, Ungated),
|
||||||
("no_debug", Whitelisted),
|
("no_debug", Whitelisted, Ungated),
|
||||||
("omit_gdb_pretty_printer_section", Whitelisted),
|
("omit_gdb_pretty_printer_section", Whitelisted, Ungated),
|
||||||
("unsafe_no_drop_flag", Gated("unsafe_no_drop_flag",
|
("unsafe_no_drop_flag", Whitelisted, Gated("unsafe_no_drop_flag",
|
||||||
"unsafe_no_drop_flag has unstable semantics \
|
"unsafe_no_drop_flag has unstable semantics \
|
||||||
and may be removed in the future")),
|
and may be removed in the future")),
|
||||||
|
|
||||||
// used in resolve
|
// used in resolve
|
||||||
("prelude_import", Gated("prelude_import",
|
("prelude_import", Whitelisted, Gated("prelude_import",
|
||||||
"`#[prelude_import]` is for use by rustc only")),
|
"`#[prelude_import]` is for use by rustc only")),
|
||||||
|
|
||||||
// FIXME: #14407 these are only looked at on-demand so we can't
|
// FIXME: #14407 these are only looked at on-demand so we can't
|
||||||
// guarantee they'll have already been checked
|
// guarantee they'll have already been checked
|
||||||
("deprecated", Whitelisted),
|
("deprecated", Whitelisted, Ungated),
|
||||||
("must_use", Whitelisted),
|
("must_use", Whitelisted, Ungated),
|
||||||
("stable", Whitelisted),
|
("stable", Whitelisted, Ungated),
|
||||||
("unstable", Whitelisted),
|
("unstable", Whitelisted, Ungated),
|
||||||
|
|
||||||
("rustc_paren_sugar", Gated("unboxed_closures",
|
("rustc_paren_sugar", Normal, Gated("unboxed_closures",
|
||||||
"unboxed_closures are still evolving")),
|
"unboxed_closures are still evolving")),
|
||||||
("rustc_reflect_like", Gated("reflect",
|
("rustc_reflect_like", Whitelisted, Gated("reflect",
|
||||||
"defining reflective traits is still evolving")),
|
"defining reflective traits is still evolving")),
|
||||||
|
|
||||||
// Crate level attributes
|
// Crate level attributes
|
||||||
("crate_name", CrateLevel),
|
("crate_name", CrateLevel, Ungated),
|
||||||
("crate_type", CrateLevel),
|
("crate_type", CrateLevel, Ungated),
|
||||||
("crate_id", CrateLevel),
|
("crate_id", CrateLevel, Ungated),
|
||||||
("feature", CrateLevel),
|
("feature", CrateLevel, Ungated),
|
||||||
("no_start", CrateLevel),
|
("no_start", CrateLevel, Ungated),
|
||||||
("no_main", CrateLevel),
|
("no_main", CrateLevel, Ungated),
|
||||||
("no_builtins", CrateLevel),
|
("no_builtins", CrateLevel, Ungated),
|
||||||
("recursion_limit", CrateLevel),
|
("recursion_limit", CrateLevel, Ungated),
|
||||||
];
|
];
|
||||||
|
|
||||||
macro_rules! cfg_fn {
|
macro_rules! cfg_fn {
|
||||||
|
@ -398,14 +401,19 @@ pub enum AttributeType {
|
||||||
/// will be ignored by the unused_attribute lint
|
/// will be ignored by the unused_attribute lint
|
||||||
Whitelisted,
|
Whitelisted,
|
||||||
|
|
||||||
/// Is gated by a given feature gate and reason
|
|
||||||
/// These get whitelisted too
|
|
||||||
Gated(&'static str, &'static str),
|
|
||||||
|
|
||||||
/// Builtin attribute that is only allowed at the crate level
|
/// Builtin attribute that is only allowed at the crate level
|
||||||
CrateLevel,
|
CrateLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
|
pub enum AttributeGate {
|
||||||
|
/// Is gated by a given feature gate and reason
|
||||||
|
Gated(&'static str, &'static str),
|
||||||
|
|
||||||
|
/// Ungated attribute, can be used on all release channels
|
||||||
|
Ungated,
|
||||||
|
}
|
||||||
|
|
||||||
/// A set of features to be used by later passes.
|
/// A set of features to be used by later passes.
|
||||||
pub struct Features {
|
pub struct Features {
|
||||||
pub unboxed_closures: bool,
|
pub unboxed_closures: bool,
|
||||||
|
@ -522,12 +530,12 @@ impl<'a> Context<'a> {
|
||||||
fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
|
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, gateage) in KNOWN_ATTRIBUTES {
|
||||||
if n == name {
|
if n == name {
|
||||||
if let Gated(gate, desc) = ty {
|
if let Gated(gate, desc) = gateage {
|
||||||
self.gate_feature(gate, attr.span, desc);
|
self.gate_feature(gate, attr.span, desc);
|
||||||
}
|
}
|
||||||
debug!("check_attribute: {:?} is known, {:?}", name, ty);
|
debug!("check_attribute: {:?} is known, {:?}, {:?}", name, ty, gateage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
src/test/compile-fail/invalid-plugin-attr.rs
Normal file
17
src/test/compile-fail/invalid-plugin-attr.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
#![deny(unused_attributes)]
|
||||||
|
#![feature(plugin)]
|
||||||
|
|
||||||
|
#[plugin(bla)] //~ ERROR unused attribute
|
||||||
|
//~^ ERROR should be an inner attribute
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue