Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature gate custom attributes #22364

Merged
merged 12 commits into from
Feb 18, 2015
7 changes: 7 additions & 0 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2399,6 +2399,10 @@ The currently implemented features of the reference compiler are:
ways insufficient for concatenating identifiers, and may be
removed entirely for something more wholesome.

* `custom_attribute` - Allows the usage of attributes unknown to the compiler
so that new attributes can be added in a bacwards compatible
manner (RFC 572).

* `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
are inherently unstable and no promise about them is made.

Expand Down Expand Up @@ -2459,6 +2463,9 @@ The currently implemented features of the reference compiler are:
implemented very poorly and will likely change significantly
with a proper implementation.

* `rustc_attrs` - Gates internal `#[rustc_*]` attributes which may be
for internal use only or have meaning added to them in the future.

* `rustc_diagnostic_macros`- A mysterious feature, used in the implementation
of rustc, not meant for mortals.

Expand Down
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#![feature(simd, unsafe_destructor)]
#![feature(staged_api)]
#![feature(unboxed_closures)]
#![feature(rustc_attrs)]

#[macro_use]
mod macros;
Expand Down
65 changes: 9 additions & 56 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use syntax::{abi, ast, ast_map};
use syntax::ast_util::is_shift_binop;
use syntax::attr::{self, AttrMetaMethods};
use syntax::codemap::{self, Span};
use syntax::feature_gate::{KNOWN_ATTRIBUTES, AttributeType};
use syntax::parse::token;
use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
use syntax::ast_util;
Expand Down Expand Up @@ -640,67 +641,19 @@ impl LintPass for UnusedAttributes {
}

fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
static ATTRIBUTE_WHITELIST: &'static [&'static str] = &[
// FIXME: #14408 whitelist docs since rustdoc looks at them
"doc",

// FIXME: #14406 these are processed in trans, which happens after the
// lint pass
"cold",
"export_name",
"inline",
"link",
"link_name",
"link_section",
"linkage",
"no_builtins",
"no_mangle",
"no_split_stack",
"no_stack_check",
"packed",
"static_assert",
"thread_local",
"no_debug",
"omit_gdb_pretty_printer_section",
"unsafe_no_drop_flag",

// used in resolve
"prelude_import",

// FIXME: #14407 these are only looked at on-demand so we can't
// guarantee they'll have already been checked
"deprecated",
"must_use",
"stable",
"unstable",
"rustc_on_unimplemented",
"rustc_error",

// FIXME: #19470 this shouldn't be needed forever
"old_orphan_check",
"old_impl_check",
"rustc_paren_sugar", // FIXME: #18101 temporary unboxed closure hack
];

static CRATE_ATTRS: &'static [&'static str] = &[
"crate_name",
"crate_type",
"feature",
"no_start",
"no_main",
"no_std",
"no_builtins",
];

for &name in ATTRIBUTE_WHITELIST {
if attr.check_name(name) {
break;
for &(ref name, ty) in KNOWN_ATTRIBUTES {
match ty {
AttributeType::Whitelisted
| AttributeType::Gated(_, _) if attr.check_name(name) => {
break;
},
_ => ()
}
}

if !attr::is_used(attr) {
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
if CRATE_ATTRS.contains(&&attr.name()[]) {
if KNOWN_ATTRIBUTES.contains(&(&attr.name()[], AttributeType::CrateLevel)) {
let msg = match attr.node.style {
ast::AttrOuter => "crate-level attribute should be an inner \
attribute: add an exclamation mark: #![foo]",
Expand Down
199 changes: 159 additions & 40 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
//! becomes stable.

use self::Status::*;
use self::AttributeType::*;

use abi::RustIntrinsic;
use ast::NodeId;
Expand All @@ -35,7 +36,6 @@ use visit;
use visit::Visitor;
use parse::token::{self, InternedString};

use std::slice;
use std::ascii::AsciiExt;

// If you change this list without updating src/doc/reference.md, @cmr will be sad
Expand Down Expand Up @@ -133,6 +133,12 @@ static KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
// Allows using the unsafe_no_drop_flag attribute (unlikely to
// switch to Accepted; see RFC 320)
("unsafe_no_drop_flag", "1.0.0", Active),

// Allows the use of custom attributes; RFC 572
("custom_attribute", "1.0.0", Active),

// Allows the use of rustc_* attributes; RFC 572
("rustc_attrs", "1.0.0", Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)

Expand All @@ -152,6 +158,138 @@ enum Status {
Accepted,
}

// Attributes that have a special meaning to rustc or rustdoc
pub static KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
// Normal attributes

("warn", Normal),
("allow", Normal),
("forbid", Normal),
("deny", Normal),

("macro_reexport", Normal),
("macro_use", Normal),
("macro_export", Normal),
("plugin_registrar", Normal),

("cfg", Normal),
("main", Normal),
("start", Normal),
("test", Normal),
("bench", Normal),
("simd", Normal),
("repr", Normal),
("path", Normal),
("abi", Normal),
("unsafe_destructor", Normal),
("automatically_derived", Normal),
("no_mangle", Normal),
("no_link", Normal),
("derive", Normal),
("should_fail", Normal),
("ignore", Normal),
("no_implicit_prelude", Normal),
("reexport_test_harness_main", Normal),
("link_args", Normal),
("macro_escape", Normal),


("staged_api", Gated("staged_api",
"staged_api is for use by rustc only")),
("plugin", Gated("plugin",
"compiler plugins are experimental \
and possibly buggy")),
("no_std", Gated("no_std",
"no_std is experimental")),
("lang", Gated("lang_items",
"language items are subject to change")),
("linkage", Gated("linkage",
"the `linkage` attribute is experimental \
and not portable across platforms")),
("thread_local", Gated("thread_local",
"`#[thread_local]` is an experimental feature, and does not \
currently handle destructors. There is no corresponding \
`#[task_local]` mapping to the task model")),

("rustc_on_unimplemented", Gated("on_unimplemented",
"the `#[rustc_on_unimplemented]` attribute \
is an experimental feature")),
("rustc_variance", Gated("rustc_attrs",
"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")),

// FIXME: #14408 whitelist docs since rustdoc looks at them
("doc", Whitelisted),

// FIXME: #14406 these are processed in trans, which happens after the
// lint pass
("cold", Whitelisted),
("export_name", Whitelisted),
("inline", Whitelisted),
("link", Whitelisted),
("link_name", Whitelisted),
("link_section", Whitelisted),
("no_builtins", Whitelisted),
("no_mangle", Whitelisted),
("no_split_stack", Whitelisted),
("no_stack_check", Whitelisted),
("packed", Whitelisted),
("static_assert", Whitelisted),
("no_debug", Whitelisted),
("omit_gdb_pretty_printer_section", Whitelisted),
("unsafe_no_drop_flag", Whitelisted),

// used in resolve
("prelude_import", Whitelisted),

// FIXME: #14407 these are only looked at on-demand so we can't
// guarantee they'll have already been checked
("deprecated", Whitelisted),
("must_use", Whitelisted),
("stable", Whitelisted),
("unstable", Whitelisted),

// FIXME: #19470 this shouldn't be needed forever
("old_orphan_check", Whitelisted),
("old_impl_check", Whitelisted),
("rustc_paren_sugar", Whitelisted), // FIXME: #18101 temporary unboxed closure hack

// Crate level attributes
("crate_name", CrateLevel),
("crate_type", CrateLevel),
("crate_id", CrateLevel),
("feature", CrateLevel),
("no_start", CrateLevel),
("no_main", CrateLevel),
("no_builtins", CrateLevel),
("recursion_limit", CrateLevel),
];

#[derive(PartialEq, Copy)]
pub enum AttributeType {
/// Normal, builtin attribute that is consumed
/// by the compiler before the unused_attribute check
Normal,

/// Builtin attribute that may not be consumed by the compiler
/// before the unused_attribute check. These attributes
/// will be ignored by the unused_attribute lint
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
CrateLevel,
}

/// A set of features to be used by later passes.
pub struct Features {
pub unboxed_closures: bool,
Expand Down Expand Up @@ -274,22 +412,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
}

fn visit_item(&mut self, i: &ast::Item) {
for attr in &i.attrs {
if attr.name() == "thread_local" {
self.gate_feature("thread_local", i.span,
"`#[thread_local]` is an experimental feature, and does not \
currently handle destructors. There is no corresponding \
`#[task_local]` mapping to the task model");
} else if attr.name() == "linkage" {
self.gate_feature("linkage", i.span,
"the `linkage` attribute is experimental \
and not portable across platforms")
} else if attr.name() == "rustc_on_unimplemented" {
self.gate_feature("on_unimplemented", i.span,
"the `#[rustc_on_unimplemented]` attribute \
is an experimental feature")
}
}
match i.node {
ast::ItemExternCrate(_) => {
if attr::contains_name(&i.attrs[], "macro_reexport") {
Expand Down Expand Up @@ -463,30 +585,27 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
}

fn visit_attribute(&mut self, attr: &ast::Attribute) {
if attr.check_name("staged_api") {
self.gate_feature("staged_api", attr.span,
"staged_api is for use by rustc only");
} else if attr.check_name("plugin") {
self.gate_feature("plugin", attr.span,
"compiler plugins are experimental \
and possibly buggy");
}

if attr::contains_name(slice::ref_slice(attr), "lang") {
self.gate_feature("lang_items",
attr.span,
"language items are subject to change");
}

if attr.check_name("no_std") {
self.gate_feature("no_std", attr.span,
"no_std is experimental");
let name = &*attr.name();
for &(n, ty) in KNOWN_ATTRIBUTES {
if n == name {
if let Gated(gate, desc) = ty {
self.gate_feature(gate, attr.span, desc);
}
return;
}
}

if attr.check_name("unsafe_no_drop_flag") {
self.gate_feature("unsafe_no_drop_flag", attr.span,
"unsafe_no_drop_flag has unstable semantics \
and may be removed in the future");
if name.starts_with("rustc_") {
self.gate_feature("rustc_attrs", attr.span,
"unless otherwise specified, attributes \
with the prefix `rustc_` \
are reserved for internal compiler diagnostics");
} else {
self.gate_feature("custom_attribute", attr.span,
format!("The attribute `{}` is currently \
unknown to the the compiler and \
may have meaning \
added to it in the future",
name).as_slice());
}
}

Expand Down
20 changes: 0 additions & 20 deletions src/test/auxiliary/lint_stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,6 @@ impl MethodTester {
pub fn method_stable(&self) {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
pub fn method_stable_text(&self) {}

#[locked]
pub fn method_locked(&self) {}
#[locked="text"]
pub fn method_locked_text(&self) {}

#[frozen]
pub fn method_frozen(&self) {}
#[frozen="text"]
pub fn method_frozen_text(&self) {}
}

#[stable(feature = "test_feature", since = "1.0.0")]
Expand Down Expand Up @@ -101,16 +91,6 @@ pub trait Trait {
fn trait_stable(&self) {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
fn trait_stable_text(&self) {}

#[locked]
fn trait_locked(&self) {}
#[locked="text"]
fn trait_locked_text(&self) {}

#[frozen]
fn trait_frozen(&self) {}
#[frozen="text"]
fn trait_frozen_text(&self) {}
}

impl Trait for MethodTester {}
Expand Down
Loading