diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index ec5951e50e3a8..713ec898c2e3c 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -19,7 +19,7 @@ use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_errors::msg; use rustc_feature::{ ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, REMOVED_LANG_FEATURES, - UNSTABLE_LANG_FEATURES, + RENAMED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; use rustc_hir::attrs::AttributeKind; use rustc_hir::{ @@ -33,7 +33,7 @@ use tracing::instrument; use crate::errors::{ CrateNameInCfgAttr, CrateTypeInCfgAttr, FeatureNotAllowed, FeatureRemoved, - FeatureRemovedReason, InvalidCfg, RemoveExprNotSupported, + FeatureRemovedReason, FeatureRenamed, InvalidCfg, RemoveExprNotSupported, }; /// A folder that strips out items that do not belong in the current configuration. @@ -81,6 +81,26 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - continue; } + // The old name is deprecated in favor of a new one, point the user at it + if let Some(f) = + RENAMED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.feature.name) + { + let pull_note = if let Some(pull) = f.pull { + format!( + "; see for more information", + ) + } else { + "".to_owned() + }; + sess.dcx().emit_err(FeatureRenamed { + span: feature_ident.span, + new_name: f.new_name, + renamed_rustc_version: f.feature.since, + pull_note, + }); + continue; + } + // If the enabled feature is stable, record it. if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name) { features.set_enabled_lang_feature(EnabledLangFeature { diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 6c5732f497f8a..083f84429dc45 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -156,6 +156,23 @@ pub(crate) struct FeatureRemoved<'a> { pub pull_note: String, } +#[derive(Diagnostic)] +#[diag("feature was renamed", code = E0557)] +#[note("renamed in {$renamed_rustc_version}{$pull_note}")] +pub(crate) struct FeatureRenamed<'a> { + #[primary_span] + #[suggestion( + "update to the new name", + code = "{new_name}", + applicability = "machine-applicable" + )] + #[label("feature was renamed")] + pub span: Span, + pub new_name: &'a str, + pub renamed_rustc_version: &'a str, + pub pull_note: String, +} + #[derive(Subdiagnostic)] #[note("{$reason}")] pub(crate) struct FeatureRemovedReason<'a> { diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 9d046bdef1cf3..d24850b22129e 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -10,10 +10,13 @@ //! For the purpose of future feature-tracking, once a feature gate is added, //! even if it is stabilized or removed, *do not remove it*. Instead, move the //! symbol to the `accepted` or `removed` modules respectively. +//! +//! If a feature is renamed, move it to the `renamed` module. mod accepted; mod builtin_attrs; mod removed; +mod renamed; mod unstable; #[cfg(test)] @@ -103,6 +106,9 @@ fn find_lang_feature_issue(feature: Symbol) -> Option> { if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| f.feature.name == feature) { return f.feature.issue; } + if let Some(f) = RENAMED_LANG_FEATURES.iter().find(|f| f.feature.name == feature) { + return f.feature.issue; + } panic!("feature `{feature}` is not declared anywhere"); } @@ -135,7 +141,18 @@ pub use builtin_attrs::{ is_valid_for_get_attr, }; pub use removed::REMOVED_LANG_FEATURES; +pub use renamed::{RENAMED_LANG_FEATURES, RenamedFeature}; pub use unstable::{ DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, TRACK_FEATURE, UNSTABLE_LANG_FEATURES, }; + +macro_rules! opt_nonzero_u32 { + () => { + None + }; + ($val:expr) => { + Some(core::num::NonZeroU32::new($val).unwrap()) + }; +} +pub(crate) use opt_nonzero_u32; diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 1e08ad1384ccf..b5b4ffa33226b 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -1,10 +1,11 @@ //! List of the removed feature gates. -use std::num::{NonZero, NonZeroU32}; +use std::num::NonZero; use rustc_span::sym; use super::{Feature, to_nonzero}; +use crate::opt_nonzero_u32; pub struct RemovedFeature { pub feature: Feature, @@ -12,15 +13,6 @@ pub struct RemovedFeature { pub pull: Option>, } -macro_rules! opt_nonzero_u32 { - () => { - None - }; - ($val:expr) => { - Some(NonZeroU32::new($val).unwrap()) - }; -} - macro_rules! declare_features { ($( $(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, $reason:expr $(, $pull:expr)?), @@ -54,7 +46,6 @@ declare_features! ( /// Allows using the `amdgpu-kernel` ABI. (removed, abi_amdgpu_kernel, "1.77.0", Some(51575), None, 120495), - (removed, abi_c_cmse_nonsecure_call, "1.90.0", Some(81391), Some("renamed to abi_cmse_nonsecure_call"), 142146), (removed, advanced_slice_patterns, "1.42.0", Some(62254), Some("merged into `#![feature(slice_patterns)]`"), 67712), (removed, allocator, "1.0.0", None, None), @@ -74,8 +65,6 @@ declare_features! ( Some("cannot be allowed in const eval in any meaningful way"), 73398), /// Allows limiting the evaluation steps of const expressions (removed, const_eval_limit, "1.72.0", Some(67217), Some("removed the limit entirely"), 103877), - /// Allows non-trivial generic constants which have to be manually propagated upwards. - (removed, const_evaluatable_checked, "1.56.0", Some(76560), Some("renamed to `generic_const_exprs`"), 88369), /// Allows the definition of `const` functions with some advanced features. (removed, const_fn, "1.54.0", Some(57563), Some("split into finer-grained feature gates"), 85109), @@ -116,11 +105,6 @@ declare_features! ( /// Allows using `doc(primitive)` without a future-incompat warning. (removed, doc_primitive, "1.58.0", Some(88070), Some("merged into `#![feature(rustdoc_internals)]`"), 90420), - /// Allows `#[doc(spotlight)]`. - /// The attribute was renamed to `#[doc(notable_trait)]` - /// and the feature to `doc_notable_trait`. - (removed, doc_spotlight, "1.53.0", Some(45040), - Some("renamed to `doc_notable_trait`"), 80965), /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238). (removed, dropck_parametricity, "1.38.0", Some(28498), None), /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible[^1]. @@ -150,10 +134,6 @@ declare_features! ( /// Allows using `#[ffi_returns_twice]` on foreign functions. (removed, ffi_returns_twice, "1.78.0", Some(58314), Some("being investigated by the ffi-unwind project group"), 120502), - /// Allows generators to be cloned. - (removed, generator_clone, "1.75.0", Some(95360), Some("renamed to `coroutine_clone`"), 116958), - /// Allows defining generators. - (removed, generators, "1.75.0", Some(43122), Some("renamed to `coroutines`"), 116958), /// An extension to the `generic_associated_types` feature, allowing incomplete features. (removed, generic_associated_types_extended, "1.85.0", Some(95451), Some( @@ -199,23 +179,13 @@ declare_features! ( (removed, negate_unsigned, "1.0.0", Some(29645), None), /// Allows diverging expressions to fall back to `!` rather than `()`. (removed, never_type_fallback, "1.93.0", Some(65992), Some("removed in favor of unconditional fallback"), 148871), - /// Allows `#[no_coverage]` on functions. - /// The feature was renamed to `coverage_attribute` and the attribute to `#[coverage(on|off)]` - (removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656), /// Allows `#[no_debug]`. (removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667), - // Allows the use of `no_sanitize` attribute. - /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` - (removed, no_sanitize, "1.91.0", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), /// Note: this feature was previously recorded in a separate /// `STABLE_REMOVED` list because it, uniquely, was once stable but was /// then removed. But there was no utility storing it separately, so now /// it's in this list. (removed, no_stack_check, "1.0.0", None, None, 40110), - /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible (object safe). - /// Renamed to `dyn_compatible_for_dispatch`. - (removed, object_safe_for_dispatch, "1.83.0", Some(43561), - Some("renamed to `dyn_compatible_for_dispatch`"), 131511), /// Allows using `#[omit_gdb_pretty_printer_section]`. (removed, omit_gdb_pretty_printer_section, "1.91.0", None, None, 144738), /// Allows using `#[on_unimplemented(..)]` on traits. @@ -223,10 +193,6 @@ declare_features! ( (removed, on_unimplemented, "1.40.0", None, None, 65794), /// A way to temporarily opt out of opt-in copy. This will *never* be accepted. (removed, opt_out_copy, "1.0.0", None, None, 20740), - /// Allows features specific to OIBIT (now called auto traits). - /// Renamed to `auto_traits`. - (removed, optin_builtin_traits, "1.50.0", Some(13231), - Some("renamed to `auto_traits`"), 79336), /// Allows overlapping impls of marker traits. (removed, overlapping_marker_traits, "1.42.0", Some(29864), Some("removed in favor of `#![feature(marker_trait_attr)]`"), 68544), diff --git a/compiler/rustc_feature/src/renamed.rs b/compiler/rustc_feature/src/renamed.rs new file mode 100644 index 0000000000000..cbd8224e34411 --- /dev/null +++ b/compiler/rustc_feature/src/renamed.rs @@ -0,0 +1,86 @@ +//! List of the renamed feature gates. + +use std::num::NonZero; + +use rustc_span::sym; + +use super::{Feature, to_nonzero}; +use crate::opt_nonzero_u32; + +pub struct RenamedFeature { + pub feature: Feature, + pub new_name: &'static str, + pub pull: Option>, +} + +macro_rules! declare_features { + ($( + $(#[doc = $doc:tt])* (renamed, $old_feature_name:ident => $new_feature_name:ident, $ver:expr, $issue:expr $(, $pull:expr)?), + )+) => { + /// Features that have been renamed. + pub static RENAMED_LANG_FEATURES: &[RenamedFeature] = &[ + $(RenamedFeature { + feature: Feature { + name: sym::$old_feature_name, + since: $ver, + issue: to_nonzero($issue), + }, + new_name: stringify!($new_feature_name), + pull: opt_nonzero_u32!($($pull)?), + }),+ + ]; + }; +} + +#[rustfmt::skip] +declare_features! { + // ------------------------------------------------------------------------- + // feature-group-start: renamed features + // ------------------------------------------------------------------------- + + // Note that the version indicates when it got *renamed*. + // + // When renaming a feature, set the version number to + // `CURRENT RUSTC VERSION` with ` ` replaced by `_`. + + (renamed, abi_c_cmse_nonsecure_call => abi_cmse_nonsecure_call, "1.90.0", Some(81391), 142146), + /// Allows non-trivial generic constants which have to be manually propagated upwards. + (renamed, const_evaluatable_checked => generic_const_exprs, "1.56.0", Some(76560), 88369), + /// Allows `#[doc(spotlight)]`. + /// The attribute was renamed to `#[doc(notable_trait)]` + /// and the feature to `doc_notable_trait`. + (renamed, doc_spotlight => doc_notable_trait, "1.53.0", Some(45040), 80965), + /// Allows generators to be cloned. + (renamed, generator_clone => coroutine_clone, "1.75.0", Some(95360), 116958), + /// Allows defining generators. + (renamed, generators => coroutines, "1.75.0", Some(43122), 116958), + /// Allows `#[no_coverage]` on functions. + /// The feature was renamed to `coverage_attribute` and the attribute to `#[coverage(on|off)]` + (renamed, no_coverage => coverage_attribute, "1.74.0", Some(84605), 114656), + // Allows the use of `no_sanitize` attribute. + /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` + (renamed, no_sanitize => sanitize, "1.91.0", Some(39699), 142681), + /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible (object safe). + /// Renamed to `dyn_compatible_for_dispatch`. + (renamed, object_safe_for_dispatch => dyn_compatible_for_dispatch, "1.83.0", Some(43561), 131511), + /// Allows features specific to OIBIT (now called auto traits). + /// Renamed to `auto_traits`. + (renamed, optin_builtin_traits => auto_traits, "1.50.0", Some(13231), 79336), + + // ------------------------------------------------------------------------- + // feature-group-end: renamed features + // ------------------------------------------------------------------------- + + + // ------------------------------------------------------------------------- + // feature-group-start: renamed library features + // ------------------------------------------------------------------------- + // + // FIXME(#141617): we should have a better way to track renamed library features, but we reuse + // the infrastructure here so users still get hints. The symbols used here can be remove from + // `symbol.rs` when that happens. + + // ------------------------------------------------------------------------- + // feature-group-end: renamed library features + // ------------------------------------------------------------------------- + } diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 43e6c18382af7..9380201e93f35 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -35,6 +35,7 @@ pub enum Status { Accepted, Removed, Unstable, + Renamed, } impl fmt::Display for Status { @@ -43,6 +44,7 @@ impl fmt::Display for Status { Status::Accepted => "accepted", Status::Unstable => "unstable", Status::Removed => "removed", + Status::Renamed => "renamed", }; fmt::Display::fmt(as_str, f) } @@ -272,14 +274,49 @@ fn test_filen_gate<'f>(filen_underscore: &'f str, features: &mut Features) -> Op pub fn collect_lang_features(base_compiler_path: &Path, check: &mut RunningCheck) -> Features { let mut features = Features::new(); - collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", check); - collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", check); - collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", check); + let mut renamed_features = Vec::new(); + + collect_lang_features_in( + &mut features, + &mut renamed_features, + base_compiler_path, + "accepted.rs", + check, + ); + collect_lang_features_in( + &mut features, + &mut renamed_features, + base_compiler_path, + "removed.rs", + check, + ); + collect_lang_features_in( + &mut features, + &mut renamed_features, + base_compiler_path, + "unstable.rs", + check, + ); + collect_lang_features_in( + &mut features, + &mut renamed_features, + base_compiler_path, + "renamed.rs", + check, + ); + + for (old_name, new_name) in renamed_features { + if !features.keys().any(|feature| *feature == new_name) { + check.error(format!("feature `{old_name}` was renamed to `{new_name}`, but feature `{new_name}` does not exist")); + } + } + features } fn collect_lang_features_in( features: &mut Features, + renamed_features: &mut Vec<(String, String)>, base: &Path, file: &str, check: &mut RunningCheck, @@ -341,11 +378,23 @@ fn collect_lang_features_in( Some("unstable") => Status::Unstable, Some("incomplete") => Status::Unstable, Some("internal") => Status::Unstable, + Some("renamed") => Status::Renamed, Some("removed") => Status::Removed, Some("accepted") => Status::Accepted, _ => continue, }; - let name = parts.next().unwrap().trim(); + + let name = if level == Status::Renamed { + let part = parts.next().unwrap().trim(); + + let (old_name, new_name) = part.split_once("=>").unwrap(); + let old_name = old_name.trim().to_string(); + renamed_features.push((old_name.clone(), new_name.trim().to_string())); + + old_name + } else { + parts.next().unwrap().trim().to_string() + }; let since_str = parts.next().unwrap().trim().trim_matches('"'); let since = match since_str.parse() { @@ -394,7 +443,7 @@ fn collect_lang_features_in( path.display(), )); } - prev_names.push(name); + prev_names.push(name.clone()); } let issue_str = parts.next().unwrap().trim(); diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.rs b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs index 0a463755f1373..597517a730dea 100644 --- a/tests/ui/feature-gates/feature-gate-coverage-attribute.rs +++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs @@ -1,5 +1,5 @@ #![crate_type = "lib"] -#![feature(no_coverage)] //~ ERROR feature has been removed [E0557] +#![feature(no_coverage)] //~ ERROR feature was renamed [E0557] #[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(coverage)` struct Foo { diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr index 68d0d9bc3c31e..201adc8417e78 100644 --- a/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr +++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr @@ -1,11 +1,13 @@ -error[E0557]: feature has been removed +error[E0557]: feature was renamed --> $DIR/feature-gate-coverage-attribute.rs:2:12 | LL | #![feature(no_coverage)] - | ^^^^^^^^^^^ feature has been removed + | ^^^^^^^^^^^ + | | + | feature was renamed + | help: update to the new name: `coverage_attribute` | - = note: removed in 1.74.0; see for more information - = note: renamed to `coverage_attribute` + = note: renamed in 1.74.0; see for more information error[E0658]: the `#[coverage]` attribute is an experimental feature --> $DIR/feature-gate-coverage-attribute.rs:10:1 diff --git a/tests/ui/feature-gates/feature-gate-sanitize.rs b/tests/ui/feature-gates/feature-gate-sanitize.rs index 768417cfae8a8..c33badb288740 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.rs +++ b/tests/ui/feature-gates/feature-gate-sanitize.rs @@ -1,4 +1,4 @@ -#![feature(no_sanitize)] //~ ERROR feature has been removed +#![feature(no_sanitize)] //~ ERROR feature was renamed #[sanitize(address = "on")] //~^ ERROR the `#[sanitize]` attribute is an experimental feature diff --git a/tests/ui/feature-gates/feature-gate-sanitize.stderr b/tests/ui/feature-gates/feature-gate-sanitize.stderr index 59e8b69de2e3d..36175ebf92a11 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.stderr +++ b/tests/ui/feature-gates/feature-gate-sanitize.stderr @@ -1,11 +1,13 @@ -error[E0557]: feature has been removed +error[E0557]: feature was renamed --> $DIR/feature-gate-sanitize.rs:1:12 | LL | #![feature(no_sanitize)] - | ^^^^^^^^^^^ feature has been removed + | ^^^^^^^^^^^ + | | + | feature was renamed + | help: update to the new name: `sanitize` | - = note: removed in 1.91.0; see for more information - = note: renamed to sanitize(xyz = "on|off") + = note: renamed in 1.91.0; see for more information error[E0658]: the `#[sanitize]` attribute is an experimental feature --> $DIR/feature-gate-sanitize.rs:3:1 diff --git a/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.rs b/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.rs index 5b0bd19fff0b0..3482811325d37 100644 --- a/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.rs +++ b/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.rs @@ -1,7 +1,7 @@ // Test for #129912, which causes a deadlock bug without finding a cycle #![feature(generators)] -//~^ ERROR feature has been removed +//~^ ERROR feature was renamed #![allow(unconditional_recursion)] fn option(i: i32) -> impl Sync { diff --git a/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.stderr b/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.stderr index 420fb36d56003..45d072cbf8038 100644 --- a/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.stderr +++ b/tests/ui/parallel-rustc/recursive-impl-trait-deadlock-issue-129912.stderr @@ -4,14 +4,16 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found LL | x virtual ; | ^^^^^^^ expected one of 8 possible tokens -error[E0557]: feature has been removed +error[E0557]: feature was renamed --> $DIR/recursive-impl-trait-deadlock-issue-129912.rs:3:12 | LL | #![feature(generators)] - | ^^^^^^^^^^ feature has been removed + | ^^^^^^^^^^ + | | + | feature was renamed + | help: update to the new name: `coroutines` | - = note: removed in 1.75.0; see for more information - = note: renamed to `coroutines` + = note: renamed in 1.75.0; see for more information error[E0423]: expected value, found trait `Sized` --> $DIR/recursive-impl-trait-deadlock-issue-129912.rs:8:62