mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-04 04:39:16 +00:00
Add a lint for writing #[feature]
for stable features, warn by default.
The 'stable_features' lint helps people progress from unstable to stable Rust by telling them when they no longer need a `feature` attribute because upstream Rust has declared it stable. This compares to the existing 'unstable_features', which is used to implement feature staging, and triggers on *any* use of `#[feature]`.
This commit is contained in:
parent
ba2f13ef06
commit
456d23e73e
@ -2018,6 +2018,12 @@ declare_lint! {
|
|||||||
"unused or unknown features found in crate-level #[feature] directives"
|
"unused or unknown features found in crate-level #[feature] directives"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
pub STABLE_FEATURES,
|
||||||
|
Warn,
|
||||||
|
"stable features found in #[feature] directive"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
pub UNKNOWN_CRATE_TYPES,
|
pub UNKNOWN_CRATE_TYPES,
|
||||||
Deny,
|
Deny,
|
||||||
@ -2060,6 +2066,7 @@ impl LintPass for HardwiredLints {
|
|||||||
UNREACHABLE_CODE,
|
UNREACHABLE_CODE,
|
||||||
WARNINGS,
|
WARNINGS,
|
||||||
UNUSED_FEATURES,
|
UNUSED_FEATURES,
|
||||||
|
STABLE_FEATURES,
|
||||||
UNKNOWN_CRATE_TYPES,
|
UNKNOWN_CRATE_TYPES,
|
||||||
VARIANT_SIZE_DIFFERENCES,
|
VARIANT_SIZE_DIFFERENCES,
|
||||||
FAT_PTR_TRANSMUTES
|
FAT_PTR_TRANSMUTES
|
||||||
|
@ -201,8 +201,9 @@ impl Index {
|
|||||||
/// Cross-references the feature names of unstable APIs with enabled
|
/// Cross-references the feature names of unstable APIs with enabled
|
||||||
/// features and possibly prints errors. Returns a list of all
|
/// features and possibly prints errors. Returns a list of all
|
||||||
/// features used.
|
/// features used.
|
||||||
pub fn check_unstable_api_usage(tcx: &ty::ctxt) -> FnvHashSet<InternedString> {
|
pub fn check_unstable_api_usage(tcx: &ty::ctxt)
|
||||||
let ref active_lib_features = tcx.sess.features.borrow().lib_features;
|
-> FnvHashMap<InternedString, attr::StabilityLevel> {
|
||||||
|
let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
|
||||||
|
|
||||||
// Put the active features into a map for quick lookup
|
// Put the active features into a map for quick lookup
|
||||||
let active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect();
|
let active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect();
|
||||||
@ -210,7 +211,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt) -> FnvHashSet<InternedString> {
|
|||||||
let mut checker = Checker {
|
let mut checker = Checker {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
active_features: active_features,
|
active_features: active_features,
|
||||||
used_features: FnvHashSet()
|
used_features: FnvHashMap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let krate = tcx.map.krate();
|
let krate = tcx.map.krate();
|
||||||
@ -223,7 +224,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt) -> FnvHashSet<InternedString> {
|
|||||||
struct Checker<'a, 'tcx: 'a> {
|
struct Checker<'a, 'tcx: 'a> {
|
||||||
tcx: &'a ty::ctxt<'tcx>,
|
tcx: &'a ty::ctxt<'tcx>,
|
||||||
active_features: FnvHashSet<InternedString>,
|
active_features: FnvHashSet<InternedString>,
|
||||||
used_features: FnvHashSet<InternedString>
|
used_features: FnvHashMap<InternedString, attr::StabilityLevel>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Checker<'a, 'tcx> {
|
impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
@ -234,7 +235,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
|||||||
|
|
||||||
match *stab {
|
match *stab {
|
||||||
Some(Stability { level: attr::Unstable, ref feature, ref reason, .. }) => {
|
Some(Stability { level: attr::Unstable, ref feature, ref reason, .. }) => {
|
||||||
self.used_features.insert(feature.clone());
|
self.used_features.insert(feature.clone(), attr::Unstable);
|
||||||
|
|
||||||
if !self.active_features.contains(feature) {
|
if !self.active_features.contains(feature) {
|
||||||
let msg = match *reason {
|
let msg = match *reason {
|
||||||
@ -247,7 +248,9 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
|||||||
feature.get(), span, &msg[]);
|
feature.get(), span, &msg[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(..) => {
|
Some(Stability { level, ref feature, .. }) => {
|
||||||
|
self.used_features.insert(feature.clone(), level);
|
||||||
|
|
||||||
// Stable APIs are always ok to call and deprecated APIs are
|
// Stable APIs are always ok to call and deprecated APIs are
|
||||||
// handled by a lint.
|
// handled by a lint.
|
||||||
}
|
}
|
||||||
@ -433,17 +436,37 @@ pub fn lookup(tcx: &ty::ctxt, id: DefId) -> Option<Stability> {
|
|||||||
/// Given the list of enabled features that were not language features (i.e. that
|
/// Given the list of enabled features that were not language features (i.e. that
|
||||||
/// were expected to be library features), and the list of features used from
|
/// were expected to be library features), and the list of features used from
|
||||||
/// libraries, identify activated features that don't exist and error about them.
|
/// libraries, identify activated features that don't exist and error about them.
|
||||||
pub fn check_unused_features(sess: &Session,
|
pub fn check_unused_or_stable_features(sess: &Session,
|
||||||
used_lib_features: &FnvHashSet<InternedString>) {
|
lib_features_used: &FnvHashMap<InternedString,
|
||||||
let ref lib_features = sess.features.borrow().lib_features;
|
attr::StabilityLevel>) {
|
||||||
let mut active_lib_features: FnvHashMap<InternedString, Span>
|
let ref declared_lib_features = sess.features.borrow().declared_lib_features;
|
||||||
= lib_features.clone().into_iter().collect();
|
let mut remaining_lib_features: FnvHashMap<InternedString, Span>
|
||||||
|
= declared_lib_features.clone().into_iter().collect();
|
||||||
|
|
||||||
for used_feature in used_lib_features {
|
let stable_msg = "this feature is stable. attribute no longer needed";
|
||||||
active_lib_features.remove(used_feature);
|
|
||||||
|
for &span in sess.features.borrow().declared_stable_lang_features.iter() {
|
||||||
|
sess.add_lint(lint::builtin::STABLE_FEATURES,
|
||||||
|
ast::CRATE_NODE_ID,
|
||||||
|
span,
|
||||||
|
stable_msg.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, &span) in &active_lib_features {
|
for (used_lib_feature, level) in lib_features_used.iter() {
|
||||||
|
match remaining_lib_features.remove(used_lib_feature) {
|
||||||
|
Some(span) => {
|
||||||
|
if *level == attr::Stable {
|
||||||
|
sess.add_lint(lint::builtin::STABLE_FEATURES,
|
||||||
|
ast::CRATE_NODE_ID,
|
||||||
|
span,
|
||||||
|
stable_msg.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => ( /* used but undeclared, handled during the previous ast visit */ )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, &span) in remaining_lib_features.iter() {
|
||||||
sess.add_lint(lint::builtin::UNUSED_FEATURES,
|
sess.add_lint(lint::builtin::UNUSED_FEATURES,
|
||||||
ast::CRATE_NODE_ID,
|
ast::CRATE_NODE_ID,
|
||||||
span,
|
span,
|
||||||
|
@ -668,8 +668,8 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
|
|||||||
time(time_passes, "stability checking", (), |_|
|
time(time_passes, "stability checking", (), |_|
|
||||||
stability::check_unstable_api_usage(&ty_cx));
|
stability::check_unstable_api_usage(&ty_cx));
|
||||||
|
|
||||||
time(time_passes, "unused feature checking", (), |_|
|
time(time_passes, "unused lib feature checking", (), |_|
|
||||||
stability::check_unused_features(
|
stability::check_unused_or_stable_features(
|
||||||
&ty_cx.sess, lib_features_used));
|
&ty_cx.sess, lib_features_used));
|
||||||
|
|
||||||
time(time_passes, "lint checking", (), |_|
|
time(time_passes, "lint checking", (), |_|
|
||||||
|
@ -149,7 +149,10 @@ pub struct Features {
|
|||||||
pub old_orphan_check: bool,
|
pub old_orphan_check: bool,
|
||||||
pub simd_ffi: bool,
|
pub simd_ffi: bool,
|
||||||
pub unmarked_api: bool,
|
pub unmarked_api: bool,
|
||||||
pub lib_features: Vec<(InternedString, Span)>
|
/// spans of #![feature] attrs for stable language features. for error reporting
|
||||||
|
pub declared_stable_lang_features: Vec<Span>,
|
||||||
|
/// #![feature] attrs for non-language (library) features
|
||||||
|
pub declared_lib_features: Vec<(InternedString, Span)>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Features {
|
impl Features {
|
||||||
@ -162,7 +165,8 @@ impl Features {
|
|||||||
old_orphan_check: false,
|
old_orphan_check: false,
|
||||||
simd_ffi: false,
|
simd_ffi: false,
|
||||||
unmarked_api: false,
|
unmarked_api: false,
|
||||||
lib_features: Vec::new()
|
declared_stable_lang_features: Vec::new(),
|
||||||
|
declared_lib_features: Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,6 +515,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
|
|||||||
cm: cm,
|
cm: cm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut accepted_features = Vec::new();
|
||||||
let mut unknown_features = Vec::new();
|
let mut unknown_features = Vec::new();
|
||||||
|
|
||||||
for attr in &krate.attrs {
|
for attr in &krate.attrs {
|
||||||
@ -550,8 +555,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
|
|||||||
span_handler.span_err(mi.span, "feature has been removed");
|
span_handler.span_err(mi.span, "feature has been removed");
|
||||||
}
|
}
|
||||||
Some(&(_, _, Accepted)) => {
|
Some(&(_, _, Accepted)) => {
|
||||||
span_handler.span_warn(mi.span, "feature has been added to Rust, \
|
accepted_features.push(mi.span);
|
||||||
directive not necessary");
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
unknown_features.push((name, mi.span));
|
unknown_features.push((name, mi.span));
|
||||||
@ -572,7 +576,8 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
|
|||||||
old_orphan_check: cx.has_feature("old_orphan_check"),
|
old_orphan_check: cx.has_feature("old_orphan_check"),
|
||||||
simd_ffi: cx.has_feature("simd_ffi"),
|
simd_ffi: cx.has_feature("simd_ffi"),
|
||||||
unmarked_api: cx.has_feature("unmarked_api"),
|
unmarked_api: cx.has_feature("unmarked_api"),
|
||||||
lib_features: unknown_features
|
declared_stable_lang_features: accepted_features,
|
||||||
|
declared_lib_features: unknown_features
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,4 +20,3 @@
|
|||||||
#![feature = "foo"] //~ ERROR: malformed feature
|
#![feature = "foo"] //~ ERROR: malformed feature
|
||||||
|
|
||||||
#![feature(test_removed_feature)] //~ ERROR: feature has been removed
|
#![feature(test_removed_feature)] //~ ERROR: feature has been removed
|
||||||
#![feature(test_accepted_feature)] //~ WARNING: feature has been added
|
|
||||||
|
20
src/test/compile-fail/stable-features.rs
Normal file
20
src/test/compile-fail/stable-features.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Testing that the stable_features lint catches use of stable
|
||||||
|
// language and lib features.
|
||||||
|
|
||||||
|
#![deny(stable_features)]
|
||||||
|
#![feature(test_accepted_feature)] //~ ERROR this feature is stable
|
||||||
|
#![feature(rust1)] //~ ERROR this feature is stable
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _foo: Vec<()> = Vec::new();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user