From 79d56265385533442e09a7f32692cdb5ae2df1dd Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Sun, 16 Aug 2026 18:17:44 +0330 Subject: [PATCH 1/5] lint ineffective #[unstable] annotations on re-exports Signed-off-by: Amirhossein Akhlaghpour --- compiler/rustc_passes/src/diagnostics.rs | 4 + compiler/rustc_passes/src/stability.rs | 135 +++++++++++++++++- .../auxiliary/stable-glob-source.rs | 10 ++ .../ineffective-unstable-reexport-glob.rs | 21 +++ .../ineffective-unstable-reexport-glob.stderr | 9 ++ .../ineffective-unstable-reexport-grouped.rs | 33 +++++ ...effective-unstable-reexport-grouped.stderr | 9 ++ .../ineffective-unstable-reexport.rs | 19 +++ .../ineffective-unstable-reexport.stderr | 9 ++ 9 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 tests/ui/stability-attribute/auxiliary/stable-glob-source.rs create mode 100644 tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs create mode 100644 tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr create mode 100644 tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs create mode 100644 tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr create mode 100644 tests/ui/stability-attribute/ineffective-unstable-reexport.rs create mode 100644 tests/ui/stability-attribute/ineffective-unstable-reexport.stderr diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 16a2cc4007318..1244c7709e0cb 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -964,6 +964,10 @@ pub(crate) struct UnnecessaryPartialStableFeature { #[note("see issue #55436 for more information")] pub(crate) struct IneffectiveUnstableImpl; +#[derive(Diagnostic)] +#[diag("`#[unstable]` does not make this re-exported path unstable")] +pub(crate) struct IneffectiveUnstableReexport; + // FIXME(jdonszelmann): move back to rustc_attr #[derive(Diagnostic)] #[diag( diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 37ec1dc01bd4b..ed35105f6c5f2 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -19,8 +19,10 @@ use rustc_hir::{ use rustc_lint_defs as lint; use rustc_lint_defs::builtin::{ DEPRECATED, DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, STABLE_FEATURES, + UNUSED_ATTRIBUTES, }; use rustc_middle::hir::nested_filter; +use rustc_middle::metadata::Reexport; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::middle::stability::{AllowUnstable, Deprecated, DeprecationEntry, EvalResult}; @@ -523,7 +525,9 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { /// Cross-references the feature names of unstable APIs with enabled /// features and possibly prints errors. fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, mod_id: LocalModId) { - tcx.hir_visit_item_likes_in_module(mod_id, &mut Checker { tcx }); + let mut checker = Checker { tcx, mod_id, unstable_reexports: FxIndexMap::default() }; + tcx.hir_visit_item_likes_in_module(mod_id, &mut checker); + checker.emit_ineffective_unstable_reexports(); let is_staged_api = tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api(); @@ -553,8 +557,123 @@ pub(crate) fn provide(providers: &mut Providers) { }; } +struct UnstableReexport { + hir_id: HirId, + has_target: bool, + all_targets_stable: bool, +} + struct Checker<'tcx> { tcx: TyCtxt<'tcx>, + mod_id: LocalModId, + unstable_reexports: FxIndexMap, +} + +impl<'tcx> Checker<'tcx> { + fn unstable_reexport_span(&self, item: &'tcx hir::Item<'tcx>) -> Option { + let attrs = self.tcx.hir_attrs(item.hir_id()); + let (stability, span) = + find_attr!(attrs, Stability { stability, span } => (*stability, *span))?; + + matches!(stability.level, StabilityLevel::Unstable { .. }).then_some(span) + } + + fn classify_reexport_targets( + &self, + targets: impl IntoIterator>, + ) -> (bool, bool) { + let mut has_target = false; + let mut all_targets_stable = true; + + for res in targets { + let Some(def_id) = res.opt_def_id() else { + // Do not emit the lint if resolution is incomplete or the + // target cannot be classified. + all_targets_stable = false; + continue; + }; + + has_target = true; + + if self.tcx.lookup_stability(def_id).is_some_and(|stab| !stab.level.is_stable()) { + all_targets_stable = false; + } + } + + (has_target, all_targets_stable) + } + + fn record_unstable_reexport( + &mut self, + item: &'tcx hir::Item<'tcx>, + span: Span, + has_target: bool, + all_targets_stable: bool, + ) { + let entry = self.unstable_reexports.entry(span).or_insert(UnstableReexport { + hir_id: item.hir_id(), + has_target: false, + all_targets_stable: true, + }); + + entry.has_target |= has_target; + entry.all_targets_stable &= all_targets_stable; + } + + fn check_single_unstable_reexport( + &mut self, + item: &'tcx hir::Item<'tcx>, + path: &'tcx UsePath<'tcx>, + ) { + let Some(span) = self.unstable_reexport_span(item) else { + return; + }; + + let (has_target, all_targets_stable) = self.classify_reexport_targets( + [path.res.type_ns, path.res.value_ns, path.res.macro_ns].into_iter().flatten(), + ); + + self.record_unstable_reexport(item, span, has_target, all_targets_stable); + } + + fn check_glob_unstable_reexport(&mut self, item: &'tcx hir::Item<'tcx>) { + let Some(span) = self.unstable_reexport_span(item) else { + return; + }; + + let glob_def_id = item.owner_id.def_id.to_def_id(); + + let targets = self + .tcx + .module_children_local(self.mod_id.to_local_def_id()) + .iter() + .filter(|child| { + child.reexport_chain.iter().any(|reexport| { + matches!( + *reexport, + Reexport::Glob(def_id) if def_id == glob_def_id + ) + }) + }) + .map(|child| child.res); + + let (has_target, all_targets_stable) = self.classify_reexport_targets(targets); + + self.record_unstable_reexport(item, span, has_target, all_targets_stable); + } + + fn emit_ineffective_unstable_reexports(&self) { + for (span, reexport) in &self.unstable_reexports { + if reexport.has_target && reexport.all_targets_stable { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + reexport.hir_id, + *span, + diagnostics::IneffectiveUnstableReexport, + ); + } + } + } } impl<'tcx> Visitor<'tcx> for Checker<'tcx> { @@ -583,6 +702,20 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None); } + hir::ItemKind::Use(path, hir::UseKind::Single(_)) + if self.tcx.features().staged_api() + && self.tcx.local_visibility(item.owner_id.def_id).is_public() => + { + self.check_single_unstable_reexport(item, path); + } + + hir::ItemKind::Use(_, hir::UseKind::Glob) + if self.tcx.features().staged_api() + && self.tcx.local_visibility(item.owner_id.def_id).is_public() => + { + self.check_glob_unstable_reexport(item); + } + // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. diff --git a/tests/ui/stability-attribute/auxiliary/stable-glob-source.rs b/tests/ui/stability-attribute/auxiliary/stable-glob-source.rs new file mode 100644 index 0000000000000..3d5bc44bc7862 --- /dev/null +++ b/tests/ui/stability-attribute/auxiliary/stable-glob-source.rs @@ -0,0 +1,10 @@ +#![crate_type = "lib"] +#![crate_name = "stable_glob_source"] +#![feature(staged_api)] +#![stable(feature = "stable_glob_source", since = "1.0.0")] + +#[stable(feature = "stable_glob_source", since = "1.0.0")] +pub fn stable_a() {} + +#[stable(feature = "stable_glob_source", since = "1.0.0")] +pub fn stable_b() {} diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs new file mode 100644 index 0000000000000..5d4edcdca6e31 --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs @@ -0,0 +1,21 @@ +//@ aux-build:lint-stability.rs +//@ aux-build:stable-glob-source.rs +//@ check-pass + +#![crate_type = "lib"] +#![feature(staged_api)] +#![stable(feature = "reexport_test", since = "1.0.0")] + +extern crate lint_stability; +extern crate stable_glob_source; + +// Every item introduced by this glob is stable, so the unstable annotation +// cannot make the exported paths unstable. +#[unstable(feature = "stable_glob_reexport", issue = "none")] +//~^ WARN `#[unstable]` does not make this re-exported path unstable +pub use stable_glob_source::*; + +// This glob contains unstable items, so #94972 makes the annotation +// relevant when checking the import itself. +#[unstable(feature = "unstable_test_feature", issue = "none")] +pub use lint_stability::*; diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr new file mode 100644 index 0000000000000..0bb2f2aaa17da --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr @@ -0,0 +1,9 @@ +warning: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexport-glob.rs:14:1 + | +LL | #[unstable(feature = "stable_glob_reexport", issue = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: requested on the command line with `-W unused-attributes` + +warning: 1 warning emitted diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs new file mode 100644 index 0000000000000..57431e90918e9 --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs @@ -0,0 +1,33 @@ +//@ aux-build:lint-stability.rs +//@ check-pass + +#![crate_type = "lib"] +#![feature(staged_api)] +#![stable(feature = "reexport_test", since = "1.0.0")] + +extern crate lint_stability; + +// Both targets are stable. +// This should produce exactly one warning for the shared attribute. +#[unstable(feature = "grouped_stable", issue = "none")] +//~^ WARN `#[unstable]` does not make this re-exported path unstable +pub use lint_stability::{ + stable as grouped_stable_a, + stable_text as grouped_stable_b, +}; + +// The annotation is used by #94972 to allow importing the unstable member +// without enabling its feature in this crate. +#[unstable(feature = "grouped_mixed", issue = "none")] +pub use lint_stability::{ + stable as grouped_mixed_stable, + unstable as grouped_mixed_unstable, +}; + +// Both targets are already unstable. This is the #94972-style case and +// should not warn. +#[unstable(feature = "unstable_test_feature", issue = "none")] +pub use lint_stability::{ + unstable as grouped_unstable_a, + unstable_text as grouped_unstable_b, +}; diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr new file mode 100644 index 0000000000000..83932b4b90877 --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr @@ -0,0 +1,9 @@ +warning: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexport-grouped.rs:12:1 + | +LL | #[unstable(feature = "grouped_stable", issue = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: requested on the command line with `-W unused-attributes` + +warning: 1 warning emitted diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport.rs new file mode 100644 index 0000000000000..28af5b44bf422 --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport.rs @@ -0,0 +1,19 @@ +//@ aux-build:lint-stability.rs +//@ check-pass + +#![crate_type = "lib"] +#![feature(staged_api)] +#![stable(feature = "reexport_test", since = "1.0.0")] + +extern crate lint_stability; + +// An unstable annotation cannot currently make a stable item unstable +// through a re-export. +#[unstable(feature = "reexport_test_unstable", issue = "none")] +//~^ WARN `#[unstable]` does not make this re-exported path unstable +pub use lint_stability::stable as supposedly_unstable; + +// This is intentional: #94972 allows an unstable upstream item to be +// re-exported without enabling its feature in this crate. +#[unstable(feature = "unstable_test_feature", issue = "none")] +pub use lint_stability::unstable as still_unstable; diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr new file mode 100644 index 0000000000000..e5e7af2a6aa0b --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr @@ -0,0 +1,9 @@ +warning: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexport.rs:12:1 + | +LL | #[unstable(feature = "reexport_test_unstable", issue = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: requested on the command line with `-W unused-attributes` + +warning: 1 warning emitted From 7b7695db6e2baf2e06a804c9cac150d10490eaf2 Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Mon, 17 Aug 2026 12:33:36 +0330 Subject: [PATCH 2/5] handle std fallout from unstable re-export lint Signed-off-by: Amirhossein Akhlaghpour --- library/core/src/io/mod.rs | 15 ++++++--------- library/core/src/lib.rs | 1 + library/core/src/ops/mod.rs | 2 +- library/std/src/lib.rs | 3 +++ library/std/src/prelude/mod.rs | 2 ++ .../ineffective-unstable-reexport-glob.rs | 1 + .../ineffective-unstable-reexport-glob.stderr | 2 +- .../ineffective-unstable-reexport-grouped.rs | 1 + .../ineffective-unstable-reexport-grouped.stderr | 2 +- .../ineffective-unstable-reexport.rs | 1 + .../ineffective-unstable-reexport.stderr | 2 +- 11 files changed, 19 insertions(+), 13 deletions(-) diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index a44d271535a9e..698f3dfb42094 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -14,21 +14,18 @@ mod write; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; +pub use self::cursor::Cursor; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; #[unstable(feature = "io_const_error_internals", issue = "none")] pub use self::error::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use self::error::const_error; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::{ - cursor::Cursor, - error::{Error, ErrorKind, Result}, - io_slice::{IoSlice, IoSliceMut}, - seek::{Seek, SeekFrom}, - util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, - write::Write, -}; +pub use self::error::{Error, ErrorKind, Result}; +pub use self::io_slice::{IoSlice, IoSliceMut}; +pub use self::seek::{Seek, SeekFrom}; +pub use self::util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use self::write::Write; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index f026434acbbc1..13e6cd4ad204c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -227,6 +227,7 @@ pub mod offload; #[unstable(feature = "contracts", issue = "128044")] pub mod contracts; +#[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use crate::macros::builtin::derive; #[stable(feature = "cfg_select", since = "1.95.0")] diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 6fa96c242fa76..9b5915a901aaa 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -156,7 +156,7 @@ mod unsize; pub use self::arith::{Add, Div, Mul, Neg, Rem, Sub}; #[stable(feature = "op_assign_traits", since = "1.8.0")] pub use self::arith::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; -#[unstable(feature = "async_fn_traits", issue = "none")] +#[stable(feature = "async_closure", since = "1.85.0")] pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::bit::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 980ec4416f04a..7dce7e045f8af 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -712,8 +712,10 @@ pub mod arch { pub use std_detect::is_aarch64_feature_detected; #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] pub use std_detect::is_arm_feature_detected; + #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")] pub use std_detect::is_loongarch_feature_detected; + #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] pub use std_detect::is_riscv_feature_detected; #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] @@ -750,6 +752,7 @@ pub use core::cfg_select; reason = "`concat_bytes` is not stable enough for use and is subject to change" )] pub use core::concat_bytes; +#[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use core::derive; #[stable(feature = "matches_macro", since = "1.42.0")] diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 78eb79ac666a2..860ee778d4872 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -181,12 +181,14 @@ pub mod rust_future { #[doc(no_inline)] pub use super::v1::*; + #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] #[doc(no_inline)] pub use core::prelude::rust_future::*; // There are two different panic macros, one in `core` and one in `std`. They are slightly // different. For `std` we explicitly want the one defined in `std`. + #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] pub use super::v1::panic; } diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs index 5d4edcdca6e31..a855830b2a8a5 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs @@ -1,6 +1,7 @@ //@ aux-build:lint-stability.rs //@ aux-build:stable-glob-source.rs //@ check-pass +//@ normalize-stderr: "(\n)\n$" -> "$1" #![crate_type = "lib"] #![feature(staged_api)] diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr index 0bb2f2aaa17da..757b7717fb660 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr @@ -1,5 +1,5 @@ warning: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport-glob.rs:14:1 + --> $DIR/ineffective-unstable-reexport-glob.rs:15:1 | LL | #[unstable(feature = "stable_glob_reexport", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs index 57431e90918e9..c62f40a28181c 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs @@ -1,5 +1,6 @@ //@ aux-build:lint-stability.rs //@ check-pass +//@ normalize-stderr: "(\n)\n$" -> "$1" #![crate_type = "lib"] #![feature(staged_api)] diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr index 83932b4b90877..939ce9519f3e8 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr @@ -1,5 +1,5 @@ warning: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport-grouped.rs:12:1 + --> $DIR/ineffective-unstable-reexport-grouped.rs:13:1 | LL | #[unstable(feature = "grouped_stable", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport.rs index 28af5b44bf422..170770babfdf8 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport.rs +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport.rs @@ -1,5 +1,6 @@ //@ aux-build:lint-stability.rs //@ check-pass +//@ normalize-stderr: "(\n)\n$" -> "$1" #![crate_type = "lib"] #![feature(staged_api)] diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr index e5e7af2a6aa0b..e10f4be868b86 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr @@ -1,5 +1,5 @@ warning: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport.rs:12:1 + --> $DIR/ineffective-unstable-reexport.rs:13:1 | LL | #[unstable(feature = "reexport_test_unstable", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 7249fbcf971aa29c5ed3ec4bd616d73deaf24ae5 Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Mon, 17 Aug 2026 15:12:33 +0330 Subject: [PATCH 3/5] allow clippy on unstable re-export suppressions Signed-off-by: Amirhossein Akhlaghpour --- library/core/src/lib.rs | 1 + library/std/src/lib.rs | 3 +++ library/std/src/prelude/mod.rs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 13e6cd4ad204c..ae6d1dbd2c399 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -227,6 +227,7 @@ pub mod offload; #[unstable(feature = "contracts", issue = "128044")] pub mod contracts; +#[allow(clippy::useless_attribute)] #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use crate::macros::builtin::derive; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7dce7e045f8af..1b7c68e89300d 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -712,9 +712,11 @@ pub mod arch { pub use std_detect::is_aarch64_feature_detected; #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] pub use std_detect::is_arm_feature_detected; + #[allow(clippy::useless_attribute)] #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")] pub use std_detect::is_loongarch_feature_detected; + #[allow(clippy::useless_attribute)] #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] pub use std_detect::is_riscv_feature_detected; @@ -752,6 +754,7 @@ pub use core::cfg_select; reason = "`concat_bytes` is not stable enough for use and is subject to change" )] pub use core::concat_bytes; +#[allow(clippy::useless_attribute)] #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use core::derive; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 860ee778d4872..2a22a9b5c249f 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -181,6 +181,7 @@ pub mod rust_future { #[doc(no_inline)] pub use super::v1::*; + #[allow(clippy::useless_attribute)] #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] #[doc(no_inline)] @@ -188,6 +189,7 @@ pub mod rust_future { // There are two different panic macros, one in `core` and one in `std`. They are slightly // different. For `std` we explicitly want the one defined in `std`. + #[allow(clippy::useless_attribute)] #[allow(unused_attributes)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] pub use super::v1::panic; From e22c3c2c096168fadd448c454148c5ca7f007fa7 Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Tue, 18 Aug 2026 17:45:42 +0330 Subject: [PATCH 4/5] add dedicated lint for ineffective unstable re-exports Signed-off-by: Amirhossein Akhlaghpour --- compiler/rustc_lint_defs/src/builtin.rs | 31 ++++++++++ compiler/rustc_passes/src/stability.rs | 62 +++++++++++-------- library/alloc/src/io/mod.rs | 14 ++--- library/core/src/lib.rs | 2 +- library/std/src/lib.rs | 6 +- library/std/src/prelude/mod.rs | 4 +- .../ineffective-unstable-reexport-glob.rs | 4 +- .../ineffective-unstable-reexport-glob.stderr | 12 ++-- .../ineffective-unstable-reexport-grouped.rs | 6 +- ...effective-unstable-reexport-grouped.stderr | 12 ++-- .../ineffective-unstable-reexport.rs | 9 ++- .../ineffective-unstable-reexport.stderr | 18 ++++-- 12 files changed, 112 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5ee3c5a741bdc..da42e700e7461 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -55,6 +55,7 @@ pub mod hardwired { HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, + INEFFECTIVE_UNSTABLE_REEXPORT, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, INLINE_NO_SANITIZE, INVALID_DOC_ATTRIBUTES, @@ -2791,6 +2792,36 @@ declare_lint! { "detects deprecation attributes with no effect", } +declare_lint! { + /// The `ineffective_unstable_reexport` lint detects `#[unstable]` attributes + /// on re-exports where the attribute does not make the re-exported path unstable. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![feature(staged_api)] + /// #![stable(feature = "test", since = "1.0.0")] + /// + /// #[stable(feature = "test", since = "1.0.0")] + /// pub struct S; + /// + /// #[unstable(feature = "reexport", issue = "none")] + /// pub use crate::S as T; + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Stability attributes on re-exports do not currently change the + /// stability of an otherwise stable re-exported item. + pub INEFFECTIVE_UNSTABLE_REEXPORT, + Deny, + "detects ineffective `#[unstable]` attributes on re-exports" +} + declare_lint! { /// The `ineffective_unstable_trait_impl` lint detects `#[unstable]` attributes which are not used. /// diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index ed35105f6c5f2..ec82be912bd6d 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -18,8 +18,8 @@ use rustc_hir::{ }; use rustc_lint_defs as lint; use rustc_lint_defs::builtin::{ - DEPRECATED, DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, STABLE_FEATURES, - UNUSED_ATTRIBUTES, + DEPRECATED, DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_REEXPORT, + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, STABLE_FEATURES, }; use rustc_middle::hir::nested_filter; use rustc_middle::metadata::Reexport; @@ -559,6 +559,7 @@ pub(crate) fn provide(providers: &mut Providers) { struct UnstableReexport { hir_id: HirId, + span: Span, has_target: bool, all_targets_stable: bool, } @@ -586,17 +587,23 @@ impl<'tcx> Checker<'tcx> { let mut all_targets_stable = true; for res in targets { - let Some(def_id) = res.opt_def_id() else { - // Do not emit the lint if resolution is incomplete or the - // target cannot be classified. - all_targets_stable = false; - continue; - }; + match res { + Res::Def(_, def_id) => { + has_target = true; - has_target = true; + if self.tcx.lookup_stability(def_id).is_some_and(|stab| !stab.level.is_stable()) + { + all_targets_stable = false; + } + } - if self.tcx.lookup_stability(def_id).is_some_and(|stab| !stab.level.is_stable()) { - all_targets_stable = false; + Res::PrimTy(_) => { + has_target = true; + } + + _ => { + all_targets_stable = false; + } } } @@ -606,12 +613,14 @@ impl<'tcx> Checker<'tcx> { fn record_unstable_reexport( &mut self, item: &'tcx hir::Item<'tcx>, + attr_span: Span, span: Span, has_target: bool, all_targets_stable: bool, ) { - let entry = self.unstable_reexports.entry(span).or_insert(UnstableReexport { + let entry = self.unstable_reexports.entry(attr_span).or_insert(UnstableReexport { hir_id: item.hir_id(), + span, has_target: false, all_targets_stable: true, }); @@ -625,19 +634,22 @@ impl<'tcx> Checker<'tcx> { item: &'tcx hir::Item<'tcx>, path: &'tcx UsePath<'tcx>, ) { - let Some(span) = self.unstable_reexport_span(item) else { + let Some(attr_span) = self.unstable_reexport_span(item) else { return; }; - let (has_target, all_targets_stable) = self.classify_reexport_targets( - [path.res.type_ns, path.res.value_ns, path.res.macro_ns].into_iter().flatten(), - ); + let (has_target, all_targets_stable) = + self.classify_reexport_targets(path.res.present_items()); - self.record_unstable_reexport(item, span, has_target, all_targets_stable); + self.record_unstable_reexport(item, attr_span, path.span, has_target, all_targets_stable); } - fn check_glob_unstable_reexport(&mut self, item: &'tcx hir::Item<'tcx>) { - let Some(span) = self.unstable_reexport_span(item) else { + fn check_glob_unstable_reexport( + &mut self, + item: &'tcx hir::Item<'tcx>, + path: &'tcx UsePath<'tcx>, + ) { + let Some(attr_span) = self.unstable_reexport_span(item) else { return; }; @@ -659,16 +671,16 @@ impl<'tcx> Checker<'tcx> { let (has_target, all_targets_stable) = self.classify_reexport_targets(targets); - self.record_unstable_reexport(item, span, has_target, all_targets_stable); + self.record_unstable_reexport(item, attr_span, path.span, has_target, all_targets_stable); } fn emit_ineffective_unstable_reexports(&self) { - for (span, reexport) in &self.unstable_reexports { + for reexport in self.unstable_reexports.values() { if reexport.has_target && reexport.all_targets_stable { self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, + INEFFECTIVE_UNSTABLE_REEXPORT, reexport.hir_id, - *span, + reexport.span, diagnostics::IneffectiveUnstableReexport, ); } @@ -709,11 +721,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { self.check_single_unstable_reexport(item, path); } - hir::ItemKind::Use(_, hir::UseKind::Glob) + hir::ItemKind::Use(path, hir::UseKind::Glob) if self.tcx.features().staged_api() && self.tcx.local_visibility(item.owner_id.def_id).is_public() => { - self.check_glob_unstable_reexport(item); + self.check_glob_unstable_reexport(item, path); } // For implementations of traits, check the stability of each item diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 44d780292317f..30f0155c08661 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -194,7 +194,6 @@ pub use core::io::SimpleMessage; pub use core::io::const_error; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use core::io::{BorrowedBuf, BorrowedCursor}; -#[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, repeat, sink, @@ -207,16 +206,13 @@ use core::io::{ slice_write_vectored, take, }; +pub use self::buf_read::BufRead; +pub use self::buffered::{BufReader, BufWriter, IntoInnerError, LineWriter, WriterPanicked}; +pub use self::copy::copy; +pub use self::read::{Read, read_to_string}; use self::read::{append_to_string, default_read_buf_exact, default_read_exact}; +pub use self::util::{Bytes, Lines, Split}; use self::util::{bytes, lines, split, uninlined_slow_read_byte}; -#[unstable(feature = "alloc_io", issue = "154046")] -pub use self::{ - buf_read::BufRead, - buffered::{BufReader, BufWriter, IntoInnerError, LineWriter, WriterPanicked}, - copy::copy, - read::{Read, read_to_string}, - util::{Bytes, Lines, Split}, -}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ae6d1dbd2c399..e72e5cbef602c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -228,7 +228,7 @@ pub mod offload; pub mod contracts; #[allow(clippy::useless_attribute)] -#[allow(unused_attributes)] // FIXME(#161153) +#[allow(ineffective_unstable_reexport)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use crate::macros::builtin::derive; #[stable(feature = "cfg_select", since = "1.95.0")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1b7c68e89300d..c28170975dda2 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -713,11 +713,11 @@ pub mod arch { #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] pub use std_detect::is_arm_feature_detected; #[allow(clippy::useless_attribute)] - #[allow(unused_attributes)] // FIXME(#161153) + #[allow(ineffective_unstable_reexport)] // FIXME(#161153) #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")] pub use std_detect::is_loongarch_feature_detected; #[allow(clippy::useless_attribute)] - #[allow(unused_attributes)] // FIXME(#161153) + #[allow(ineffective_unstable_reexport)] // FIXME(#161153) #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] pub use std_detect::is_riscv_feature_detected; #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] @@ -755,7 +755,7 @@ pub use core::cfg_select; )] pub use core::concat_bytes; #[allow(clippy::useless_attribute)] -#[allow(unused_attributes)] // FIXME(#161153) +#[allow(ineffective_unstable_reexport)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use core::derive; #[stable(feature = "matches_macro", since = "1.42.0")] diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 2a22a9b5c249f..eed3f04cacb05 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -182,7 +182,7 @@ pub mod rust_future { pub use super::v1::*; #[allow(clippy::useless_attribute)] - #[allow(unused_attributes)] // FIXME(#161153) + #[allow(ineffective_unstable_reexport)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] #[doc(no_inline)] pub use core::prelude::rust_future::*; @@ -190,7 +190,7 @@ pub mod rust_future { // There are two different panic macros, one in `core` and one in `std`. They are slightly // different. For `std` we explicitly want the one defined in `std`. #[allow(clippy::useless_attribute)] - #[allow(unused_attributes)] // FIXME(#161153) + #[allow(ineffective_unstable_reexport)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] pub use super::v1::panic; } diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs index a855830b2a8a5..bb875cb0630df 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs @@ -1,6 +1,5 @@ //@ aux-build:lint-stability.rs //@ aux-build:stable-glob-source.rs -//@ check-pass //@ normalize-stderr: "(\n)\n$" -> "$1" #![crate_type = "lib"] @@ -13,8 +12,7 @@ extern crate stable_glob_source; // Every item introduced by this glob is stable, so the unstable annotation // cannot make the exported paths unstable. #[unstable(feature = "stable_glob_reexport", issue = "none")] -//~^ WARN `#[unstable]` does not make this re-exported path unstable -pub use stable_glob_source::*; +pub use stable_glob_source::*; //~ ERROR `#[unstable]` does not make this re-exported path unstable // This glob contains unstable items, so #94972 makes the annotation // relevant when checking the import itself. diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr index 757b7717fb660..7cf8045d1da0e 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr @@ -1,9 +1,9 @@ -warning: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport-glob.rs:15:1 +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexport-glob.rs:15:9 | -LL | #[unstable(feature = "stable_glob_reexport", issue = "none")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub use stable_glob_source::*; + | ^^^^^^^^^^^^^^^^^^ | - = note: requested on the command line with `-W unused-attributes` + = note: `#[deny(ineffective_unstable_reexport)]` on by default -warning: 1 warning emitted +error: aborting due to 1 previous error diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs index c62f40a28181c..4a9b3b6a8a532 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs @@ -1,5 +1,4 @@ //@ aux-build:lint-stability.rs -//@ check-pass //@ normalize-stderr: "(\n)\n$" -> "$1" #![crate_type = "lib"] @@ -9,11 +8,10 @@ extern crate lint_stability; // Both targets are stable. -// This should produce exactly one warning for the shared attribute. +// This should produce exactly one error for the shared attribute. #[unstable(feature = "grouped_stable", issue = "none")] -//~^ WARN `#[unstable]` does not make this re-exported path unstable pub use lint_stability::{ - stable as grouped_stable_a, + stable as grouped_stable_a, //~ ERROR `#[unstable]` does not make this re-exported path unstable stable_text as grouped_stable_b, }; diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr index 939ce9519f3e8..88a0ce4d70147 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr @@ -1,9 +1,9 @@ -warning: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport-grouped.rs:13:1 +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexport-grouped.rs:14:5 | -LL | #[unstable(feature = "grouped_stable", issue = "none")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | stable as grouped_stable_a, + | ^^^^^^ | - = note: requested on the command line with `-W unused-attributes` + = note: `#[deny(ineffective_unstable_reexport)]` on by default -warning: 1 warning emitted +error: aborting due to 1 previous error diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.rs b/tests/ui/stability-attribute/ineffective-unstable-reexport.rs index 170770babfdf8..1e46da673fd15 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport.rs +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport.rs @@ -1,18 +1,21 @@ //@ aux-build:lint-stability.rs -//@ check-pass //@ normalize-stderr: "(\n)\n$" -> "$1" #![crate_type = "lib"] #![feature(staged_api)] #![stable(feature = "reexport_test", since = "1.0.0")] +extern crate core; extern crate lint_stability; // An unstable annotation cannot currently make a stable item unstable // through a re-export. #[unstable(feature = "reexport_test_unstable", issue = "none")] -//~^ WARN `#[unstable]` does not make this re-exported path unstable -pub use lint_stability::stable as supposedly_unstable; +pub use lint_stability::stable as supposedly_unstable; //~ ERROR `#[unstable]` does not make this re-exported path unstable + +// Primitive re-exports do not have a DefId, but the primitive itself is stable. +#[unstable(feature = "primitive_reexport", issue = "none")] +pub use core::primitive::bool as supposedly_unstable_bool; //~ ERROR `#[unstable]` does not make this re-exported path unstable // This is intentional: #94972 allows an unstable upstream item to be // re-exported without enabling its feature in this crate. diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr index e10f4be868b86..84c10348e5e0f 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr +++ b/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr @@ -1,9 +1,15 @@ -warning: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport.rs:13:1 +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexport.rs:14:9 | -LL | #[unstable(feature = "reexport_test_unstable", issue = "none")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub use lint_stability::stable as supposedly_unstable; + | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: requested on the command line with `-W unused-attributes` + = note: `#[deny(ineffective_unstable_reexport)]` on by default -warning: 1 warning emitted +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexport.rs:18:9 + | +LL | pub use core::primitive::bool as supposedly_unstable_bool; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors From fbec8a6e7b8718ecf8ee697d05df961c09d2e859 Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Wed, 19 Aug 2026 19:26:06 +0330 Subject: [PATCH 5/5] refine unstable reexport lint Signed-off-by: Amirhossein Akhlaghpour --- compiler/rustc_lint/src/levels.rs | 20 +++++++++++-------- compiler/rustc_lint_defs/src/builtin.rs | 9 +++++---- compiler/rustc_passes/src/diagnostics.rs | 2 +- compiler/rustc_passes/src/stability.rs | 12 +++++------ library/alloc/src/io/mod.rs | 18 ++++++++++++----- library/core/src/io/mod.rs | 17 ++++++++++------ library/core/src/lib.rs | 2 +- library/core/src/ops/mod.rs | 4 +++- library/std/src/lib.rs | 6 +++--- library/std/src/prelude/mod.rs | 4 ++-- ...ate-unused_unstable_reexport_attributes.rs | 7 +++++++ ...unused_unstable_reexport_attributes.stderr | 10 ++++++++++ ...used-unstable-reexport-attributes-glob.rs} | 0 ...-unstable-reexport-attributes-glob.stderr} | 4 ++-- ...d-unstable-reexport-attributes-grouped.rs} | 0 ...stable-reexport-attributes-grouped.stderr} | 4 ++-- ...=> unused-unstable-reexport-attributes.rs} | 0 ...nused-unstable-reexport-attributes.stderr} | 6 +++--- 18 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.rs create mode 100644 tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.stderr rename tests/ui/stability-attribute/{ineffective-unstable-reexport-glob.rs => unused-unstable-reexport-attributes-glob.rs} (100%) rename tests/ui/stability-attribute/{ineffective-unstable-reexport-glob.stderr => unused-unstable-reexport-attributes-glob.stderr} (58%) rename tests/ui/stability-attribute/{ineffective-unstable-reexport-grouped.rs => unused-unstable-reexport-attributes-grouped.rs} (100%) rename tests/ui/stability-attribute/{ineffective-unstable-reexport-grouped.stderr => unused-unstable-reexport-attributes-grouped.stderr} (55%) rename tests/ui/stability-attribute/{ineffective-unstable-reexport.rs => unused-unstable-reexport-attributes.rs} (100%) rename tests/ui/stability-attribute/{ineffective-unstable-reexport.stderr => unused-unstable-reexport-attributes.stderr} (67%) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index fbb20ed101055..472835388620c 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -969,14 +969,18 @@ where let mut lint = Diag::new(dcx, level, msg!("unknown lint: `{$name}`")) .with_arg("name", lint_id.lint.name_lower()) .with_note(msg!("the `{$name}` lint is unstable")); - rustc_session::diagnostics::add_feature_diagnostics_for_issue( - &mut lint, - sess, - feature, - GateIssue::Language, - lint_from_cli, - None, - ); + // `staged_api` is only intended for the standard library, so don't + // suggest enabling it just to use this lint. + if feature != sym::staged_api { + rustc_session::diagnostics::add_feature_diagnostics_for_issue( + &mut lint, + sess, + feature, + GateIssue::Language, + lint_from_cli, + None, + ); + } lint } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index da42e700e7461..01076781ebb1d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -55,7 +55,6 @@ pub mod hardwired { HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, - INEFFECTIVE_UNSTABLE_REEXPORT, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, INLINE_NO_SANITIZE, INVALID_DOC_ATTRIBUTES, @@ -153,6 +152,7 @@ pub mod hardwired { UNUSED_MUT, UNUSED_QUALIFICATIONS, UNUSED_UNSAFE, + UNUSED_UNSTABLE_REEXPORT_ATTRIBUTES, UNUSED_VARIABLES, UNUSED_VISIBILITIES, USELESS_DEPRECATED, @@ -2793,7 +2793,7 @@ declare_lint! { } declare_lint! { - /// The `ineffective_unstable_reexport` lint detects `#[unstable]` attributes + /// The `unused_unstable_reexport_attributes` lint detects `#[unstable]` attributes /// on re-exports where the attribute does not make the re-exported path unstable. /// /// ### Example @@ -2817,9 +2817,10 @@ declare_lint! { /// /// Stability attributes on re-exports do not currently change the /// stability of an otherwise stable re-exported item. - pub INEFFECTIVE_UNSTABLE_REEXPORT, + pub UNUSED_UNSTABLE_REEXPORT_ATTRIBUTES, Deny, - "detects ineffective `#[unstable]` attributes on re-exports" + "detects ineffective `#[unstable]` attributes on re-exports", + @feature_gate = staged_api; } declare_lint! { diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 1244c7709e0cb..d79e947c657e5 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -966,7 +966,7 @@ pub(crate) struct IneffectiveUnstableImpl; #[derive(Diagnostic)] #[diag("`#[unstable]` does not make this re-exported path unstable")] -pub(crate) struct IneffectiveUnstableReexport; +pub(crate) struct UnusedUnstableReexportAttributes; // FIXME(jdonszelmann): move back to rustc_attr #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index ec82be912bd6d..9aeadf9dc859e 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -18,8 +18,8 @@ use rustc_hir::{ }; use rustc_lint_defs as lint; use rustc_lint_defs::builtin::{ - DEPRECATED, DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_REEXPORT, - INEFFECTIVE_UNSTABLE_TRAIT_IMPL, STABLE_FEATURES, + DEPRECATED, DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, STABLE_FEATURES, + UNUSED_UNSTABLE_REEXPORT_ATTRIBUTES, }; use rustc_middle::hir::nested_filter; use rustc_middle::metadata::Reexport; @@ -527,7 +527,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, mod_id: LocalModId) { let mut checker = Checker { tcx, mod_id, unstable_reexports: FxIndexMap::default() }; tcx.hir_visit_item_likes_in_module(mod_id, &mut checker); - checker.emit_ineffective_unstable_reexports(); + checker.emit_unused_unstable_reexport_attributes(); let is_staged_api = tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api(); @@ -674,14 +674,14 @@ impl<'tcx> Checker<'tcx> { self.record_unstable_reexport(item, attr_span, path.span, has_target, all_targets_stable); } - fn emit_ineffective_unstable_reexports(&self) { + fn emit_unused_unstable_reexport_attributes(&self) { for reexport in self.unstable_reexports.values() { if reexport.has_target && reexport.all_targets_stable { self.tcx.emit_node_span_lint( - INEFFECTIVE_UNSTABLE_REEXPORT, + UNUSED_UNSTABLE_REEXPORT_ATTRIBUTES, reexport.hir_id, reexport.span, - diagnostics::IneffectiveUnstableReexport, + diagnostics::UnusedUnstableReexportAttributes, ); } } diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 30f0155c08661..d7e07aa5d0282 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -194,6 +194,9 @@ pub use core::io::SimpleMessage; pub use core::io::const_error; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use core::io::{BorrowedBuf, BorrowedCursor}; +#[allow(clippy::useless_attribute)] +#[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) +#[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, repeat, sink, @@ -206,13 +209,18 @@ use core::io::{ slice_write_vectored, take, }; -pub use self::buf_read::BufRead; -pub use self::buffered::{BufReader, BufWriter, IntoInnerError, LineWriter, WriterPanicked}; -pub use self::copy::copy; -pub use self::read::{Read, read_to_string}; use self::read::{append_to_string, default_read_buf_exact, default_read_exact}; -pub use self::util::{Bytes, Lines, Split}; use self::util::{bytes, lines, split, uninlined_slow_read_byte}; +#[allow(clippy::useless_attribute)] +#[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) +#[unstable(feature = "alloc_io", issue = "154046")] +pub use self::{ + buf_read::BufRead, + buffered::{BufReader, BufWriter, IntoInnerError, LineWriter, WriterPanicked}, + copy::copy, + read::{Read, read_to_string}, + util::{Bytes, Lines, Split}, +}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 698f3dfb42094..df463e4b29f79 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -14,18 +14,23 @@ mod write; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; -pub use self::cursor::Cursor; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; #[unstable(feature = "io_const_error_internals", issue = "none")] pub use self::error::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use self::error::const_error; -pub use self::error::{Error, ErrorKind, Result}; -pub use self::io_slice::{IoSlice, IoSliceMut}; -pub use self::seek::{Seek, SeekFrom}; -pub use self::util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; -pub use self::write::Write; +#[allow(clippy::useless_attribute)] +#[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) +#[unstable(feature = "core_io", issue = "154046")] +pub use self::{ + cursor::Cursor, + error::{Error, ErrorKind, Result}, + io_slice::{IoSlice, IoSliceMut}, + seek::{Seek, SeekFrom}, + util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, + write::Write, +}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e72e5cbef602c..afdcc3371a4a3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -228,7 +228,7 @@ pub mod offload; pub mod contracts; #[allow(clippy::useless_attribute)] -#[allow(ineffective_unstable_reexport)] // FIXME(#161153) +#[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use crate::macros::builtin::derive; #[stable(feature = "cfg_select", since = "1.95.0")] diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 9b5915a901aaa..3f5ac28db6ff7 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -156,7 +156,9 @@ mod unsize; pub use self::arith::{Add, Div, Mul, Neg, Rem, Sub}; #[stable(feature = "op_assign_traits", since = "1.8.0")] pub use self::arith::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; -#[stable(feature = "async_closure", since = "1.85.0")] +#[allow(clippy::useless_attribute)] +#[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) +#[unstable(feature = "async_fn_traits", issue = "none")] pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::bit::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c28170975dda2..6f52867775a07 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -713,11 +713,11 @@ pub mod arch { #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] pub use std_detect::is_arm_feature_detected; #[allow(clippy::useless_attribute)] - #[allow(ineffective_unstable_reexport)] // FIXME(#161153) + #[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")] pub use std_detect::is_loongarch_feature_detected; #[allow(clippy::useless_attribute)] - #[allow(ineffective_unstable_reexport)] // FIXME(#161153) + #[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] pub use std_detect::is_riscv_feature_detected; #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] @@ -755,7 +755,7 @@ pub use core::cfg_select; )] pub use core::concat_bytes; #[allow(clippy::useless_attribute)] -#[allow(ineffective_unstable_reexport)] // FIXME(#161153) +#[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use core::derive; #[stable(feature = "matches_macro", since = "1.42.0")] diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index eed3f04cacb05..e0608d78c2e3c 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -182,7 +182,7 @@ pub mod rust_future { pub use super::v1::*; #[allow(clippy::useless_attribute)] - #[allow(ineffective_unstable_reexport)] // FIXME(#161153) + #[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] #[doc(no_inline)] pub use core::prelude::rust_future::*; @@ -190,7 +190,7 @@ pub mod rust_future { // There are two different panic macros, one in `core` and one in `std`. They are slightly // different. For `std` we explicitly want the one defined in `std`. #[allow(clippy::useless_attribute)] - #[allow(ineffective_unstable_reexport)] // FIXME(#161153) + #[allow(unused_unstable_reexport_attributes)] // FIXME(#161153) #[unstable(feature = "prelude_next", issue = "none")] pub use super::v1::panic; } diff --git a/tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.rs b/tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.rs new file mode 100644 index 0000000000000..87d3d42b46bc8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.rs @@ -0,0 +1,7 @@ +//@ check-pass +//@ normalize-stderr: "(\n)\n$" -> "$1" +// This lint is only available with `staged_api`. +#![allow(unused_unstable_reexport_attributes)] +//~^ WARNING unknown lint: `unused_unstable_reexport_attributes` + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.stderr b/tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.stderr new file mode 100644 index 0000000000000..7aff3ca5e9dc2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unused_unstable_reexport_attributes.stderr @@ -0,0 +1,10 @@ +warning: unknown lint: `unused_unstable_reexport_attributes` + --> $DIR/feature-gate-unused_unstable_reexport_attributes.rs:4:10 + | +LL | #![allow(unused_unstable_reexport_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `unused_unstable_reexport_attributes` lint is unstable + = note: `#[warn(unknown_lints)]` on by default + +warning: 1 warning emitted diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs b/tests/ui/stability-attribute/unused-unstable-reexport-attributes-glob.rs similarity index 100% rename from tests/ui/stability-attribute/ineffective-unstable-reexport-glob.rs rename to tests/ui/stability-attribute/unused-unstable-reexport-attributes-glob.rs diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr b/tests/ui/stability-attribute/unused-unstable-reexport-attributes-glob.stderr similarity index 58% rename from tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr rename to tests/ui/stability-attribute/unused-unstable-reexport-attributes-glob.stderr index 7cf8045d1da0e..44f007996b3bd 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-glob.stderr +++ b/tests/ui/stability-attribute/unused-unstable-reexport-attributes-glob.stderr @@ -1,9 +1,9 @@ error: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport-glob.rs:15:9 + --> $DIR/unused-unstable-reexport-attributes-glob.rs:15:9 | LL | pub use stable_glob_source::*; | ^^^^^^^^^^^^^^^^^^ | - = note: `#[deny(ineffective_unstable_reexport)]` on by default + = note: `#[deny(unused_unstable_reexport_attributes)]` on by default error: aborting due to 1 previous error diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs b/tests/ui/stability-attribute/unused-unstable-reexport-attributes-grouped.rs similarity index 100% rename from tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.rs rename to tests/ui/stability-attribute/unused-unstable-reexport-attributes-grouped.rs diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr b/tests/ui/stability-attribute/unused-unstable-reexport-attributes-grouped.stderr similarity index 55% rename from tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr rename to tests/ui/stability-attribute/unused-unstable-reexport-attributes-grouped.stderr index 88a0ce4d70147..b3ee0b09b0ae0 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport-grouped.stderr +++ b/tests/ui/stability-attribute/unused-unstable-reexport-attributes-grouped.stderr @@ -1,9 +1,9 @@ error: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport-grouped.rs:14:5 + --> $DIR/unused-unstable-reexport-attributes-grouped.rs:14:5 | LL | stable as grouped_stable_a, | ^^^^^^ | - = note: `#[deny(ineffective_unstable_reexport)]` on by default + = note: `#[deny(unused_unstable_reexport_attributes)]` on by default error: aborting due to 1 previous error diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.rs b/tests/ui/stability-attribute/unused-unstable-reexport-attributes.rs similarity index 100% rename from tests/ui/stability-attribute/ineffective-unstable-reexport.rs rename to tests/ui/stability-attribute/unused-unstable-reexport-attributes.rs diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr b/tests/ui/stability-attribute/unused-unstable-reexport-attributes.stderr similarity index 67% rename from tests/ui/stability-attribute/ineffective-unstable-reexport.stderr rename to tests/ui/stability-attribute/unused-unstable-reexport-attributes.stderr index 84c10348e5e0f..2129efe6a616f 100644 --- a/tests/ui/stability-attribute/ineffective-unstable-reexport.stderr +++ b/tests/ui/stability-attribute/unused-unstable-reexport-attributes.stderr @@ -1,13 +1,13 @@ error: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport.rs:14:9 + --> $DIR/unused-unstable-reexport-attributes.rs:14:9 | LL | pub use lint_stability::stable as supposedly_unstable; | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[deny(ineffective_unstable_reexport)]` on by default + = note: `#[deny(unused_unstable_reexport_attributes)]` on by default error: `#[unstable]` does not make this re-exported path unstable - --> $DIR/ineffective-unstable-reexport.rs:18:9 + --> $DIR/unused-unstable-reexport-attributes.rs:18:9 | LL | pub use core::primitive::bool as supposedly_unstable_bool; | ^^^^^^^^^^^^^^^^^^^^^