From 224aec213d62aa11758c864c871c341b7e7e7681 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 13 Jul 2022 13:10:37 +0100 Subject: middle: add `implies_by` to `#[unstable]` If part of a feature is stabilized and a new feature is added for the remaining parts, then the `implied_by` attribute can be used to indicate which now-stable feature previously contained a item. If the now-stable feature is still active (if the user has only just updated rustc, for example) then there will not be an stability error for uses of the item from the implied feature. Signed-off-by: David Wood --- .../stability-attribute-implies-using-stable.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs (limited to 'src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs') diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs new file mode 100644 index 00000000000..527639ec70b --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs @@ -0,0 +1,15 @@ +// aux-build:stability-attribute-implies.rs +#![deny(stable_features)] +#![feature(foo)] +//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable + +// Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic +// mentioning partial stabilization, and that given the implied unstable feature is unused (there +// is no `foobar` call), that the compiler suggests removing the flag. + +extern crate stability_attribute_implies; +use stability_attribute_implies::foo; + +fn main() { + foo(); +} -- cgit 1.4.1-3-g733a5 From 6246d66c6da3064f658831c0ed8162df169a001e Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 13 Jul 2022 15:10:19 +0100 Subject: passes: improved partial stabilization diagnostic Improves the diagnostic when a feature attribute is specified unnecessarily but the feature implies another (i.e. it was partially stabilized) to refer to the implied feature. Signed-off-by: David Wood --- compiler/rustc_metadata/src/rmeta/decoder.rs | 7 +++ .../src/rmeta/decoder/cstore_impl.rs | 3 ++ compiler/rustc_metadata/src/rmeta/encoder.rs | 15 ++++++ compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_middle/src/middle/stability.rs | 13 +++++ compiler/rustc_middle/src/query/mod.rs | 8 ++- compiler/rustc_passes/src/stability.rs | 58 +++++++++++++++++++--- .../stability-attribute-implies-using-stable.rs | 2 +- ...stability-attribute-implies-using-stable.stderr | 10 +++- .../stability-attribute-implies-using-unstable.rs | 2 +- ...ability-attribute-implies-using-unstable.stderr | 10 +++- 11 files changed, 115 insertions(+), 14 deletions(-) (limited to 'src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs') diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index f0874f8f2da..aa5705d3fcd 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -951,6 +951,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) } + /// Iterates over the stability implications in the given crate (when a `#[unstable]` attribute + /// has an `implied_by` meta item, then the mapping from the implied feature to the actual + /// feature is a stability implication). + fn get_stability_implications(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Symbol)] { + tcx.arena.alloc_from_iter(self.root.stability_implications.decode(self)) + } + /// Iterates over the language items in the given crate. fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] { tcx.arena.alloc_from_iter( diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 565eec18ea9..65cae29c58d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -291,6 +291,9 @@ provide! { <'tcx> tcx, def_id, other, cdata, tcx.arena.alloc_slice(&result) } defined_lib_features => { cdata.get_lib_features(tcx) } + stability_implications => { + cdata.get_stability_implications(tcx).iter().copied().collect() + } is_intrinsic => { cdata.get_is_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8e973009777..50d983754e8 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -538,6 +538,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let lib_features = self.encode_lib_features(); let lib_feature_bytes = self.position() - i; + // Encode the stability implications. + i = self.position(); + let stability_implications = self.encode_stability_implications(); + let stability_implications_bytes = self.position() - i; + // Encode the language items. i = self.position(); let lang_items = self.encode_lang_items(); @@ -686,6 +691,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { crate_deps, dylib_dependency_formats, lib_features, + stability_implications, lang_items, diagnostic_items, lang_items_missing, @@ -710,6 +716,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let computed_total_bytes = preamble_bytes + dep_bytes + lib_feature_bytes + + stability_implications_bytes + lang_item_bytes + diagnostic_item_bytes + native_lib_bytes @@ -761,6 +768,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { p("preamble", preamble_bytes); p("dep", dep_bytes); p("lib feature", lib_feature_bytes); + p("stability_implications", stability_implications_bytes); p("lang item", lang_item_bytes); p("diagnostic item", diagnostic_item_bytes); p("native lib", native_lib_bytes); @@ -1777,6 +1785,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(lib_features.to_vec()) } + fn encode_stability_implications(&mut self) -> LazyArray<(Symbol, Symbol)> { + empty_proc_macro!(self); + let tcx = self.tcx; + let implications = tcx.stability_implications(LOCAL_CRATE); + self.lazy_array(implications.iter().map(|(k, v)| (*k, *v))) + } + fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> { empty_proc_macro!(self); let tcx = self.tcx; diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index af1c09f4ae8..0f291f92647 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -226,6 +226,7 @@ pub(crate) struct CrateRoot { crate_deps: LazyArray, dylib_dependency_formats: LazyArray>, lib_features: LazyArray<(Symbol, Option)>, + stability_implications: LazyArray<(Symbol, Symbol)>, lang_items: LazyArray<(DefIndex, usize)>, lang_items_missing: LazyArray, diagnostic_items: LazyArray<(Symbol, DefIndex)>, diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 63f66be1507..0fbad3f0f0f 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -62,6 +62,19 @@ pub struct Index { pub stab_map: FxHashMap, pub const_stab_map: FxHashMap, pub depr_map: FxHashMap, + /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` + /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute + /// exists, then this map will have a `impliee -> implier` entry. + /// + /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should + /// specify their implications (both `implies` and `implied_by`). If only one of the two + /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this + /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is + /// reported, only the `#[stable]` attribute information is available, so the map is necessary + /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` + /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of + /// unstable feature" error for a feature that was implied. + pub implications: FxHashMap, } impl Index { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0581ef41f66..466a0fc25f7 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1634,11 +1634,15 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) desc { "calculating the lib features map" } } - query defined_lib_features(_: CrateNum) - -> &'tcx [(Symbol, Option)] { + query defined_lib_features(_: CrateNum) -> &'tcx [(Symbol, Option)] { desc { "calculating the lib features defined in a crate" } separate_provide_extern } + query stability_implications(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the implications between `#[unstable]` features defined in a crate" } + separate_provide_extern + } /// Whether the function is an intrinsic query is_intrinsic(def_id: DefId) -> bool { desc { |tcx| "is_intrinsic({})", tcx.def_path_str(def_id) } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index a15a42bd76f..9cacda041f2 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -2,9 +2,9 @@ //! propagating default levels lexically from parent to children ast nodes. use attr::StabilityLevel; -use rustc_attr::{self as attr, ConstStability, Stability}; +use rustc_attr::{self as attr, ConstStability, Stability, Unstable}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -265,6 +265,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab { + self.index.implications.insert(implied_by, feature); + } + self.index.stab_map.insert(def_id, stab); stab }); @@ -610,6 +614,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { stab_map: Default::default(), const_stab_map: Default::default(), depr_map: Default::default(), + implications: Default::default(), }; { @@ -668,6 +673,7 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_unstable_api_usage, stability_index, + stability_implications: |tcx, _| tcx.stability().implications.clone(), lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()), lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()), lookup_deprecation_entry: |tcx, id| { @@ -946,11 +952,18 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { remaining_lib_features.remove(&sym::libc); remaining_lib_features.remove(&sym::test); + let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone(); + for &cnum in tcx.crates(()) { + implications.extend(tcx.stability_implications(cnum)); + } + let check_features = |remaining_lib_features: &mut FxIndexMap<_, _>, defined_features: &[_]| { for &(feature, since) in defined_features { - if let Some(since) = since { - if let Some(span) = remaining_lib_features.get(&feature) { - // Warn if the user has enabled an already-stable lib feature. + 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); + } else { unnecessary_stable_feature_lint(tcx, *span, feature, since); } } @@ -983,12 +996,41 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // don't lint about unused features. We should re-enable this one day! } +fn unnecessary_partially_stable_feature_lint( + tcx: TyCtxt<'_>, + span: Span, + feature: Symbol, + implies: Symbol, + since: Symbol, +) { + tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { + lint.build(&format!( + "the feature `{feature}` has been partially stabilized since {since} and is succeeded \ + by the feature `{implies}`" + )) + .span_suggestion( + span, + &format!( + "if you are using features which are still unstable, change to using `{implies}`" + ), + implies, + Applicability::MaybeIncorrect, + ) + .span_suggestion( + tcx.sess.source_map().span_extend_to_line(span), + "if you are using features which are now stable, remove this line", + "", + Applicability::MaybeIncorrect, + ) + .emit(); + }); +} + fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) { tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { lint.build(&format!( - "the feature `{}` has been stable since {} and no longer requires \ - an attribute to enable", - feature, since + "the feature `{feature}` has been stable since {since} and no longer requires an \ + attribute to enable", )) .emit(); }); diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs index 527639ec70b..1a2d8e271de 100644 --- a/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs @@ -1,7 +1,7 @@ // aux-build:stability-attribute-implies.rs #![deny(stable_features)] #![feature(foo)] -//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable +//~^ ERROR the feature `foo` has been partially stabilized since 1.62.0 and is succeeded by the feature `foobar` // Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic // mentioning partial stabilization, and that given the implied unstable feature is unused (there diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr index c8767e85a68..c9b3f07cc70 100644 --- a/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr @@ -1,4 +1,4 @@ -error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable +error: the feature `foo` has been partially stabilized since 1.62.0 and is succeeded by the feature `foobar` --> $DIR/stability-attribute-implies-using-stable.rs:3:12 | LL | #![feature(foo)] @@ -9,6 +9,14 @@ note: the lint level is defined here | LL | #![deny(stable_features)] | ^^^^^^^^^^^^^^^ +help: if you are using features which are still unstable, change to using `foobar` + | +LL | #![feature(foobar)] + | ~~~~~~ +help: if you are using features which are now stable, remove this line + | +LL - #![feature(foo)] + | error: aborting due to previous error diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs index d6ad4d3510e..3c73c5abf3b 100644 --- a/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs @@ -1,7 +1,7 @@ // aux-build:stability-attribute-implies.rs #![deny(stable_features)] #![feature(foo)] -//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable +//~^ ERROR the feature `foo` has been partially stabilized since 1.62.0 and is succeeded by the feature `foobar` // Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic // mentioning partial stabilization and that given the implied unstable feature is used (there is a diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr index 35cbac6035c..9a5c7ef5a47 100644 --- a/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr @@ -1,4 +1,4 @@ -error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable +error: the feature `foo` has been partially stabilized since 1.62.0 and is succeeded by the feature `foobar` --> $DIR/stability-attribute-implies-using-unstable.rs:3:12 | LL | #![feature(foo)] @@ -9,6 +9,14 @@ note: the lint level is defined here | LL | #![deny(stable_features)] | ^^^^^^^^^^^^^^^ +help: if you are using features which are still unstable, change to using `foobar` + | +LL | #![feature(foobar)] + | ~~~~~~ +help: if you are using features which are now stable, remove this line + | +LL - #![feature(foo)] + | error: aborting due to previous error -- cgit 1.4.1-3-g733a5