1
Fork 0

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:
Jonas Schievink 2015-08-29 00:23:32 +02:00
parent 6f28232756
commit 4b571b055d
4 changed files with 143 additions and 121 deletions

View file

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

View file

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

View file

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

View 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() {}