]> git.lizzy.rs Git - rust.git/commitdiff
passes: check implied feature exists
authorDavid Wood <david.wood@huawei.com>
Wed, 20 Jul 2022 13:52:23 +0000 (14:52 +0100)
committerDavid Wood <david.wood@huawei.com>
Wed, 20 Jul 2022 14:50:59 +0000 (15:50 +0100)
Add a check confirming that features referenced in `implied_by` meta
items actually exist.

Signed-off-by: David Wood <david.wood@huawei.com>
compiler/rustc_middle/src/middle/mod.rs
compiler/rustc_passes/src/lib_features.rs
compiler/rustc_passes/src/stability.rs
src/test/ui/stability-attribute/stability-attribute-implies-missing.rs [new file with mode: 0644]
src/test/ui/stability-attribute/stability-attribute-implies-missing.stderr [new file with mode: 0644]

index fc35cafcc77a16bc30e1080404ddb121a3b435dc..8dc68b1f5a820d415d0e21d696eeaedf3c780000 100644 (file)
@@ -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<Symbol, Symbol>,
-        pub unstable: FxHashSet<Symbol>,
+        /// A map from feature to stabilisation version.
+        pub stable: FxHashMap<Symbol, (Symbol, Span)>,
+        pub unstable: FxHashMap<Symbol, Span>,
     }
 
     impl LibFeatures {
@@ -18,8 +18,8 @@ pub fn to_vec(&self) -> Vec<(Symbol, Option<Symbol>)> {
             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
index 97ae8e6f3259c95099aa77a45214170e63d070df..e05994f13e4d9b8ef73bc5c8b50ca878cb599636 100644 (file)
@@ -71,11 +71,11 @@ fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> {
 
     fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, 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<Symbol>, 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(
index 9cacda041f22e730806f05454c90cd1eed8aa65c..81b04c414ed9b6c11ba48e8f078310476b359407 100644 (file)
@@ -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 (file)
index 0000000..6138785
--- /dev/null
@@ -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 (file)
index 0000000..ff1856f
--- /dev/null
@@ -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
+