From: David Wood Date: Wed, 20 Jul 2022 13:52:23 +0000 (+0100) Subject: passes: check implied feature exists X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=e5872990d13abb088397e23b226439b1b4926b91;p=rust.git passes: check implied feature exists Add a check confirming that features referenced in `implied_by` meta items actually exist. Signed-off-by: David Wood --- diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index fc35cafcc77..8dc68b1f5a8 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -3,14 +3,14 @@ pub mod exported_symbols; pub mod lang_items; pub mod lib_features { - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - use rustc_span::symbol::Symbol; + use rustc_data_structures::fx::FxHashMap; + use rustc_span::{symbol::Symbol, Span}; #[derive(HashStable, Debug)] pub struct LibFeatures { - // A map from feature to stabilisation version. - pub stable: FxHashMap, - pub unstable: FxHashSet, + /// A map from feature to stabilisation version. + pub stable: FxHashMap, + pub unstable: FxHashMap, } impl LibFeatures { @@ -18,8 +18,8 @@ pub fn to_vec(&self) -> Vec<(Symbol, Option)> { let mut all_features: Vec<_> = self .stable .iter() - .map(|(f, s)| (*f, Some(*s))) - .chain(self.unstable.iter().map(|f| (*f, None))) + .map(|(f, (s, _))| (*f, Some(*s))) + .chain(self.unstable.iter().map(|(f, _)| (*f, None))) .collect(); all_features.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap()); all_features diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 97ae8e6f325..e05994f13e4 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -71,11 +71,11 @@ fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option, Span)> { fn collect_feature(&mut self, feature: Symbol, since: Option, span: Span) { let already_in_stable = self.lib_features.stable.contains_key(&feature); - let already_in_unstable = self.lib_features.unstable.contains(&feature); + let already_in_unstable = self.lib_features.unstable.contains_key(&feature); match (since, already_in_stable, already_in_unstable) { (Some(since), _, false) => { - if let Some(prev_since) = self.lib_features.stable.get(&feature) { + if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) { if *prev_since != since { self.span_feature_error( span, @@ -89,10 +89,10 @@ fn collect_feature(&mut self, feature: Symbol, since: Option, span: Span } } - self.lib_features.stable.insert(feature, since); + self.lib_features.stable.insert(feature, (since, span)); } (None, false, _) => { - self.lib_features.unstable.insert(feature); + self.lib_features.unstable.insert(feature, span); } (Some(_), _, true) | (None, true, _) => { self.span_feature_error( diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 9cacda041f2..81b04c414ed 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -3,7 +3,7 @@ use attr::StabilityLevel; use rustc_attr::{self as attr, ConstStability, Stability, Unstable}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -952,19 +952,45 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { remaining_lib_features.remove(&sym::libc); remaining_lib_features.remove(&sym::test); + // We always collect the lib features declared in the current crate, even if there are + // no unknown features, because the collection also does feature attribute validation. + let local_defined_features = tcx.lib_features(()); + let mut all_lib_features: FxHashMap<_, _> = + local_defined_features.to_vec().iter().map(|el| *el).collect(); let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone(); for &cnum in tcx.crates(()) { implications.extend(tcx.stability_implications(cnum)); + all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el)); + } + + // Check that every feature referenced by an `implied_by` exists (for features defined in the + // local crate). + for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) { + // Only `implied_by` needs to be checked, `feature` is guaranteed to exist. + if !all_lib_features.contains_key(implied_by) { + let span = local_defined_features + .stable + .get(feature) + .map(|(_, span)| span) + .or_else(|| local_defined_features.unstable.get(feature)) + .expect("feature that implied another does not exist"); + tcx.sess + .struct_span_err( + *span, + format!("feature `{implied_by}` implying `{feature}` does not exist"), + ) + .emit(); + } } - let check_features = |remaining_lib_features: &mut FxIndexMap<_, _>, defined_features: &[_]| { - for &(feature, since) in defined_features { + if !remaining_lib_features.is_empty() { + for (feature, since) in all_lib_features.iter() { if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) { // Warn if the user has enabled an already-stable lib feature. if let Some(implies) = implications.get(&feature) { - unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since); + unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since); } else { - unnecessary_stable_feature_lint(tcx, *span, feature, since); + unnecessary_stable_feature_lint(tcx, *span, *feature, *since); } } remaining_lib_features.remove(&feature); @@ -972,20 +998,6 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { break; } } - }; - - // We always collect the lib features declared in the current crate, even if there are - // no unknown features, because the collection also does feature attribute validation. - let local_defined_features = tcx.lib_features(()).to_vec(); - if !remaining_lib_features.is_empty() { - check_features(&mut remaining_lib_features, &local_defined_features); - - for &cnum in tcx.crates(()) { - if remaining_lib_features.is_empty() { - break; - } - check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum)); - } } for (feature, span) in remaining_lib_features { diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-missing.rs b/src/test/ui/stability-attribute/stability-attribute-implies-missing.rs new file mode 100644 index 00000000000..61387853672 --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-missing.rs @@ -0,0 +1,10 @@ +#![feature(staged_api)] +#![stable(feature = "stability_attribute_implies", since = "1.0.0")] + +// Tests that `implied_by = "bar"` results in an error being emitted if `bar` does not exist. + +#[unstable(feature = "foobar", issue = "1", implied_by = "bar")] +//~^ ERROR feature `bar` implying `foobar` does not exist +pub fn foobar() {} + +fn main() {} diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-missing.stderr b/src/test/ui/stability-attribute/stability-attribute-implies-missing.stderr new file mode 100644 index 00000000000..ff1856f1763 --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-missing.stderr @@ -0,0 +1,8 @@ +error: feature `bar` implying `foobar` does not exist + --> $DIR/stability-attribute-implies-missing.rs:6:1 + | +LL | #[unstable(feature = "foobar", issue = "1", implied_by = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error +