mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-26 06:35:27 +00:00
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>
This commit is contained in:
parent
6246d66c6d
commit
e5872990d1
@ -3,14 +3,14 @@ pub mod dependency_format;
|
||||
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 mod lib_features {
|
||||
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
|
||||
|
@ -71,11 +71,11 @@ impl<'tcx> LibFeatureCollector<'tcx> {
|
||||
|
||||
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 @@ impl<'tcx> LibFeatureCollector<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
let check_features = |remaining_lib_features: &mut FxIndexMap<_, _>, defined_features: &[_]| {
|
||||
for &(feature, since) in defined_features {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -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() {}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user