diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 53054f462c8b6..685922cea1140 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -2018,6 +2018,12 @@ declare_lint! { "unused or unknown features found in crate-level #[feature] directives" } +declare_lint! { + pub STABLE_FEATURES, + Warn, + "stable features found in #[feature] directive" +} + declare_lint! { pub UNKNOWN_CRATE_TYPES, Deny, @@ -2060,6 +2066,7 @@ impl LintPass for HardwiredLints { UNREACHABLE_CODE, WARNINGS, UNUSED_FEATURES, + STABLE_FEATURES, UNKNOWN_CRATE_TYPES, VARIANT_SIZE_DIFFERENCES, FAT_PTR_TRANSMUTES diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 3304bd4ae2952..a005ae9c2227f 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -201,8 +201,9 @@ impl Index { /// Cross-references the feature names of unstable APIs with enabled /// features and possibly prints errors. Returns a list of all /// features used. -pub fn check_unstable_api_usage(tcx: &ty::ctxt) -> FnvHashSet { - let ref active_lib_features = tcx.sess.features.borrow().lib_features; +pub fn check_unstable_api_usage(tcx: &ty::ctxt) + -> FnvHashMap { + let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features; // Put the active features into a map for quick lookup 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 { let mut checker = Checker { tcx: tcx, active_features: active_features, - used_features: FnvHashSet() + used_features: FnvHashMap() }; let krate = tcx.map.krate(); @@ -223,7 +224,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt) -> FnvHashSet { struct Checker<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, active_features: FnvHashSet, - used_features: FnvHashSet + used_features: FnvHashMap } impl<'a, 'tcx> Checker<'a, 'tcx> { @@ -234,7 +235,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { match *stab { 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) { let msg = match *reason { @@ -247,7 +248,9 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { 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 // handled by a lint. } @@ -433,17 +436,37 @@ pub fn lookup(tcx: &ty::ctxt, id: DefId) -> Option { /// 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 /// libraries, identify activated features that don't exist and error about them. -pub fn check_unused_features(sess: &Session, - used_lib_features: &FnvHashSet) { - let ref lib_features = sess.features.borrow().lib_features; - let mut active_lib_features: FnvHashMap - = lib_features.clone().into_iter().collect(); - - for used_feature in used_lib_features { - active_lib_features.remove(used_feature); +pub fn check_unused_or_stable_features(sess: &Session, + lib_features_used: &FnvHashMap) { + let ref declared_lib_features = sess.features.borrow().declared_lib_features; + let mut remaining_lib_features: FnvHashMap + = declared_lib_features.clone().into_iter().collect(); + + let stable_msg = "this feature is stable. attribute no longer needed"; + + 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 (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 &active_lib_features { + for (_, &span) in remaining_lib_features.iter() { sess.add_lint(lint::builtin::UNUSED_FEATURES, ast::CRATE_NODE_ID, span, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 8ede037594a00..8c89f66f7a177 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -668,8 +668,8 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, time(time_passes, "stability checking", (), |_| stability::check_unstable_api_usage(&ty_cx)); - time(time_passes, "unused feature checking", (), |_| - stability::check_unused_features( + time(time_passes, "unused lib feature checking", (), |_| + stability::check_unused_or_stable_features( &ty_cx.sess, lib_features_used)); time(time_passes, "lint checking", (), |_| diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d7a51e1149f30..59cdb15296ac9 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -149,7 +149,10 @@ pub struct Features { pub old_orphan_check: bool, pub simd_ffi: 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, + /// #![feature] attrs for non-language (library) features + pub declared_lib_features: Vec<(InternedString, Span)> } impl Features { @@ -162,7 +165,8 @@ impl Features { old_orphan_check: false, simd_ffi: 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(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C cm: cm, }; + let mut accepted_features = Vec::new(); let mut unknown_features = Vec::new(); for attr in &krate.attrs { @@ -550,8 +555,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C span_handler.span_err(mi.span, "feature has been removed"); } Some(&(_, _, Accepted)) => { - span_handler.span_warn(mi.span, "feature has been added to Rust, \ - directive not necessary"); + accepted_features.push(mi.span); } None => { unknown_features.push((name, mi.span)); @@ -572,7 +576,8 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C old_orphan_check: cx.has_feature("old_orphan_check"), simd_ffi: cx.has_feature("simd_ffi"), unmarked_api: cx.has_feature("unmarked_api"), - lib_features: unknown_features + declared_stable_lang_features: accepted_features, + declared_lib_features: unknown_features } } diff --git a/src/test/compile-fail/gated-bad-feature.rs b/src/test/compile-fail/gated-bad-feature.rs index 39cd3e3b86afe..5baafd4153159 100644 --- a/src/test/compile-fail/gated-bad-feature.rs +++ b/src/test/compile-fail/gated-bad-feature.rs @@ -20,4 +20,3 @@ #![feature = "foo"] //~ ERROR: malformed feature #![feature(test_removed_feature)] //~ ERROR: feature has been removed -#![feature(test_accepted_feature)] //~ WARNING: feature has been added diff --git a/src/test/compile-fail/stable-features.rs b/src/test/compile-fail/stable-features.rs new file mode 100644 index 0000000000000..30eb4112c3fb2 --- /dev/null +++ b/src/test/compile-fail/stable-features.rs @@ -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 or the MIT license +// , 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(); +}