Skip to content

Commit e587299

Browse files
committed
passes: check implied feature exists
Add a check confirming that features referenced in `implied_by` meta items actually exist. Signed-off-by: David Wood <david.wood@huawei.com>
1 parent 6246d66 commit e587299

File tree

5 files changed

+60
-30
lines changed

5 files changed

+60
-30
lines changed

Diff for: compiler/rustc_middle/src/middle/mod.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@ pub mod dependency_format;
33
pub mod exported_symbols;
44
pub mod lang_items;
55
pub mod lib_features {
6-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7-
use rustc_span::symbol::Symbol;
6+
use rustc_data_structures::fx::FxHashMap;
7+
use rustc_span::{symbol::Symbol, Span};
88

99
#[derive(HashStable, Debug)]
1010
pub struct LibFeatures {
11-
// A map from feature to stabilisation version.
12-
pub stable: FxHashMap<Symbol, Symbol>,
13-
pub unstable: FxHashSet<Symbol>,
11+
/// A map from feature to stabilisation version.
12+
pub stable: FxHashMap<Symbol, (Symbol, Span)>,
13+
pub unstable: FxHashMap<Symbol, Span>,
1414
}
1515

1616
impl LibFeatures {
1717
pub fn to_vec(&self) -> Vec<(Symbol, Option<Symbol>)> {
1818
let mut all_features: Vec<_> = self
1919
.stable
2020
.iter()
21-
.map(|(f, s)| (*f, Some(*s)))
22-
.chain(self.unstable.iter().map(|f| (*f, None)))
21+
.map(|(f, (s, _))| (*f, Some(*s)))
22+
.chain(self.unstable.iter().map(|(f, _)| (*f, None)))
2323
.collect();
2424
all_features.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap());
2525
all_features

Diff for: compiler/rustc_passes/src/lib_features.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ impl<'tcx> LibFeatureCollector<'tcx> {
7171

7272
fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) {
7373
let already_in_stable = self.lib_features.stable.contains_key(&feature);
74-
let already_in_unstable = self.lib_features.unstable.contains(&feature);
74+
let already_in_unstable = self.lib_features.unstable.contains_key(&feature);
7575

7676
match (since, already_in_stable, already_in_unstable) {
7777
(Some(since), _, false) => {
78-
if let Some(prev_since) = self.lib_features.stable.get(&feature) {
78+
if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) {
7979
if *prev_since != since {
8080
self.span_feature_error(
8181
span,
@@ -89,10 +89,10 @@ impl<'tcx> LibFeatureCollector<'tcx> {
8989
}
9090
}
9191

92-
self.lib_features.stable.insert(feature, since);
92+
self.lib_features.stable.insert(feature, (since, span));
9393
}
9494
(None, false, _) => {
95-
self.lib_features.unstable.insert(feature);
95+
self.lib_features.unstable.insert(feature, span);
9696
}
9797
(Some(_), _, true) | (None, true, _) => {
9898
self.span_feature_error(

Diff for: compiler/rustc_passes/src/stability.rs

+31-19
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use attr::StabilityLevel;
55
use rustc_attr::{self as attr, ConstStability, Stability, Unstable};
6-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
6+
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
77
use rustc_errors::{struct_span_err, Applicability};
88
use rustc_hir as hir;
99
use rustc_hir::def::{DefKind, Res};
@@ -952,40 +952,52 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
952952
remaining_lib_features.remove(&sym::libc);
953953
remaining_lib_features.remove(&sym::test);
954954

955+
// We always collect the lib features declared in the current crate, even if there are
956+
// no unknown features, because the collection also does feature attribute validation.
957+
let local_defined_features = tcx.lib_features(());
958+
let mut all_lib_features: FxHashMap<_, _> =
959+
local_defined_features.to_vec().iter().map(|el| *el).collect();
955960
let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
956961
for &cnum in tcx.crates(()) {
957962
implications.extend(tcx.stability_implications(cnum));
963+
all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el));
964+
}
965+
966+
// Check that every feature referenced by an `implied_by` exists (for features defined in the
967+
// local crate).
968+
for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) {
969+
// Only `implied_by` needs to be checked, `feature` is guaranteed to exist.
970+
if !all_lib_features.contains_key(implied_by) {
971+
let span = local_defined_features
972+
.stable
973+
.get(feature)
974+
.map(|(_, span)| span)
975+
.or_else(|| local_defined_features.unstable.get(feature))
976+
.expect("feature that implied another does not exist");
977+
tcx.sess
978+
.struct_span_err(
979+
*span,
980+
format!("feature `{implied_by}` implying `{feature}` does not exist"),
981+
)
982+
.emit();
983+
}
958984
}
959985

960-
let check_features = |remaining_lib_features: &mut FxIndexMap<_, _>, defined_features: &[_]| {
961-
for &(feature, since) in defined_features {
986+
if !remaining_lib_features.is_empty() {
987+
for (feature, since) in all_lib_features.iter() {
962988
if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) {
963989
// Warn if the user has enabled an already-stable lib feature.
964990
if let Some(implies) = implications.get(&feature) {
965-
unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since);
991+
unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since);
966992
} else {
967-
unnecessary_stable_feature_lint(tcx, *span, feature, since);
993+
unnecessary_stable_feature_lint(tcx, *span, *feature, *since);
968994
}
969995
}
970996
remaining_lib_features.remove(&feature);
971997
if remaining_lib_features.is_empty() {
972998
break;
973999
}
9741000
}
975-
};
976-
977-
// We always collect the lib features declared in the current crate, even if there are
978-
// no unknown features, because the collection also does feature attribute validation.
979-
let local_defined_features = tcx.lib_features(()).to_vec();
980-
if !remaining_lib_features.is_empty() {
981-
check_features(&mut remaining_lib_features, &local_defined_features);
982-
983-
for &cnum in tcx.crates(()) {
984-
if remaining_lib_features.is_empty() {
985-
break;
986-
}
987-
check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum));
988-
}
9891001
}
9901002

9911003
for (feature, span) in remaining_lib_features {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(staged_api)]
2+
#![stable(feature = "stability_attribute_implies", since = "1.0.0")]
3+
4+
// Tests that `implied_by = "bar"` results in an error being emitted if `bar` does not exist.
5+
6+
#[unstable(feature = "foobar", issue = "1", implied_by = "bar")]
7+
//~^ ERROR feature `bar` implying `foobar` does not exist
8+
pub fn foobar() {}
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: feature `bar` implying `foobar` does not exist
2+
--> $DIR/stability-attribute-implies-missing.rs:6:1
3+
|
4+
LL | #[unstable(feature = "foobar", issue = "1", implied_by = "bar")]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)