diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index d69f9ab7f0641..a041e83a646ce 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -481,11 +481,13 @@ impl<'a> TraitDef<'a> { self.expand_struct_def(cx, struct_def, *ident, generics, from_scratch, is_packed) } ast::ItemKind::Enum(ident, generics, enum_def) => { - // We ignore `is_packed` here, because `repr(packed)` - // enums cause an error later on. - // + // We can skip generating the impl here, because `repr(packed)` + // enums cause an error later on and to prevent ICEs like #133025. // This can only cause further compilation errors // downstream in blatantly illegal code, so it is fine. + if is_packed { + return; + } self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch) } ast::ItemKind::Union(ident, generics, struct_def) => { 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 9caba9c1b5fdb..e1184d91377be 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -54,6 +54,7 @@ pub mod hardwired { HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, + INEFFECTIVE_UNSTABLE_REEXPORTS, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, INLINE_NO_SANITIZE, INVALID_DOC_ATTRIBUTES, @@ -2816,6 +2817,39 @@ declare_lint! { "detects deprecation attributes with no effect", } +declare_lint! { + /// The `ineffective_unstable_reexports` lint detects `#[unstable]` + /// attributes on re-exports where the attribute does not make the + /// re-exported path unstable. + /// + /// ### Example + /// + #[cfg_attr(bootstrap, doc = "```rust,ignore")] + #[cfg_attr(not(bootstrap), doc = "```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 self::S as T; + /// + /// fn main() {} + #[doc = "```"] + /// + #[cfg_attr(not(bootstrap), doc = "{{produces}}")] + /// + /// ### Explanation + /// + /// `#[unstable]` on a re-export does not make a stable path unstable + /// re-exports inside unstable modules are already on an unstable path + pub INEFFECTIVE_UNSTABLE_REEXPORTS, + Deny, + "detects ineffective `#[unstable]` attributes on re-exports", + @feature_gate = staged_api; +} + declare_lint! { /// The `ineffective_unstable_trait_impl` lint detects `#[unstable]` attributes which are not used. /// diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 8b0ba0ab8f102..a396695ba6d52 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -950,6 +950,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 e8b19b510c63b..ecbb16c51c140 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -17,7 +17,8 @@ use rustc_hir::{ UsePath, VERSION_PLACEHOLDER, Variant, find_attr, }; use rustc_lint_defs::builtin::{ - DEPRECATED, DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, STABLE_FEATURES, + DEPRECATED, DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_REEXPORTS, + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, STABLE_FEATURES, }; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; @@ -522,7 +523,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(); @@ -552,8 +555,155 @@ pub(crate) fn provide(providers: &mut Providers) { }; } +struct UnstableReexport { + hir_id: HirId, + span: Span, + 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))?; + + stability.level.is_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 { + match res { + Res::Def(_, def_id) => { + has_target = true; + + match self.tcx.lookup_stability(def_id) { + Some(stability) if stability.level.is_unstable() => { + all_targets_stable = false; + } + Some(_) => {} + + None => { + // Items from crates without staged API metadata are + // effectively stable. Unmarked items in staged API + // crates are diagnosed by the existing stability checks. + if self.tcx.lookup_stability(def_id.krate.as_def_id()).is_some() { + all_targets_stable = false; + } + } + } + } + + // Primitives are stable and have no DefId. + Res::PrimTy(_) => { + has_target = true; + } + + // Do not lint if the target cannot be classified. + _ => { + all_targets_stable = false; + } + } + } + + (has_target, all_targets_stable) + } + + 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(attr_span).or_insert(UnstableReexport { + hir_id: item.hir_id(), + span, + 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(attr_span) = self.unstable_reexport_span(item) else { + return; + }; + + let (has_target, all_targets_stable) = + self.classify_reexport_targets(path.res.present_items()); + + 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>, + path: &'tcx UsePath<'tcx>, + ) { + let Some(attr_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| reexport.id() == Some(glob_def_id)) + }) + .map(|child| child.res); + + let (has_target, all_targets_stable) = self.classify_reexport_targets(targets); + + self.record_unstable_reexport(item, attr_span, path.span, has_target, all_targets_stable); + } + + fn containing_module_is_unstable(&self) -> bool { + self.tcx + .lookup_stability(self.mod_id.to_local_def_id()) + .is_some_and(|stability| stability.level.is_unstable()) + } + + fn emit_ineffective_unstable_reexports(&self) { + // an unstable module already makes its re-exports unstable + // keep the explicit annotation without linting it as ineffective + if self.unstable_reexports.is_empty() || self.containing_module_is_unstable() { + return; + } + + for reexport in self.unstable_reexports.values() { + if reexport.has_target && reexport.all_targets_stable { + self.tcx.emit_node_span_lint( + INEFFECTIVE_UNSTABLE_REEXPORTS, + reexport.hir_id, + reexport.span, + diagnostics::IneffectiveUnstableReexport, + ); + } + } + } } impl<'tcx> Visitor<'tcx> for Checker<'tcx> { @@ -582,6 +732,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(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, path); + } + // 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/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 07228664d65ef..bf77b477eef70 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1780,7 +1780,7 @@ impl Box { pub fn into_unique(b: Self) -> (Unique, A) { let (ptr, alloc) = Box::into_raw_with_allocator(b); // SAFETY: Pointer is valid and unique. - unsafe { (Unique::from(&mut *ptr), alloc) } + unsafe { (Unique::new_unchecked(ptr), alloc) } } /// Returns a raw mutable pointer to the `Box`'s contents. diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index b2afe0b464eb6..0a37489474511 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -802,7 +802,7 @@ impl Rc { { // Construct the inner in the "uninitialized" state with a single // weak reference. - let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( + let (uninit_ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( RcInner { strong: Cell::new(0), weak: Cell::new(1), @@ -810,8 +810,6 @@ impl Rc { }, alloc, )); - // ignore-tidy-undocumented-unsafe - let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); let weak = Weak { ptr: init_ptr, alloc }; @@ -863,12 +861,12 @@ impl Rc { // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. - let (ptr, alloc) = Box::into_unique(Box::try_new_in( + let (ptr, alloc) = Box::into_non_null_with_allocator(Box::try_new_in( RcInner { strong: Cell::new(1), weak: Cell::new(1), value }, alloc, )?); - // ignore-tidy-undocumented-unsafe - Ok(unsafe { Self::from_inner_in(ptr.into(), alloc) }) + // SAFETY: Pointer is valid. + Ok(unsafe { Self::from_inner_in(ptr, alloc) }) } /// Constructs a new `Rc` with uninitialized contents, in the provided allocator, returning an @@ -4340,7 +4338,7 @@ impl UniqueRc { #[must_use] // #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(value: T, alloc: A) -> Self { - let (ptr, alloc) = Box::into_unique(Box::new_in( + let (ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( RcInner { strong: Cell::new(0), // keep one weak reference so if all the weak pointers that are created are dropped @@ -4350,7 +4348,7 @@ impl UniqueRc { }, alloc, )); - Self { ptr: ptr.into(), _marker: PhantomData, _marker2: PhantomData, alloc } + Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } } #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 09a371f94bbb9..3f5e8614987aa 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -714,9 +714,9 @@ impl Arc { }, alloc, ); - let (ptr, alloc) = Box::into_unique(x); + let (ptr, alloc) = Box::into_non_null_with_allocator(x); // SAFETY: Pointer is valid. - unsafe { Self::from_inner_in(ptr.into(), alloc) } + unsafe { Self::from_inner_in(ptr, alloc) } } /// Constructs a new `Arc` with uninitialized contents in the provided allocator. @@ -834,7 +834,7 @@ impl Arc { { // Construct the inner in the "uninitialized" state with a single // weak reference. - let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( + let (uninit_ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( ArcInner { strong: atomic::AtomicUsize::new(0), weak: atomic::AtomicUsize::new(1), @@ -842,8 +842,6 @@ impl Arc { }, alloc, )); - // SAFETY: Pointer is valid since we constructed it. - let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); let weak = Weak { ptr: init_ptr, alloc }; @@ -939,9 +937,9 @@ impl Arc { }, alloc, )?; - let (ptr, alloc) = Box::into_unique(x); + let (ptr, alloc) = Box::into_non_null_with_allocator(x); // SAFETY: Pointer is valid since we created it. - Ok(unsafe { Self::from_inner_in(ptr.into(), alloc) }) + Ok(unsafe { Self::from_inner_in(ptr, alloc) }) } /// Constructs a new `Arc` with uninitialized contents, in the provided allocator, returning an @@ -4817,7 +4815,7 @@ impl UniqueArc { #[must_use] // #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(data: T, alloc: A) -> Self { - let (ptr, alloc) = Box::into_unique(Box::new_in( + let (ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( ArcInner { strong: atomic::AtomicUsize::new(0), // keep one weak reference so if all the weak pointers that are created are dropped @@ -4827,7 +4825,7 @@ impl UniqueArc { }, alloc, )); - Self { ptr: ptr.into(), _marker: PhantomData, _marker2: PhantomData, alloc } + Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } } #[cfg(not(no_global_oom_handling))] diff --git a/library/alloctests/tests/arc.rs b/library/alloctests/tests/arc.rs index 4299fea4120d8..31a98fc6a6fa2 100644 --- a/library/alloctests/tests/arc.rs +++ b/library/alloctests/tests/arc.rs @@ -351,3 +351,46 @@ fn issue_158875_make_mut_dont_leak_allocator() { fn new_uninit_slice_capacity_overflow() { let _ = Arc::<[u8]>::new_uninit_slice(isize::MAX as usize); } + +mod arc_allocator_provenance { + //! Regression tests for issues where the pointer passed back to the allocator + //! only had the provenance of a reborrow, which is unsound with allocators + //! that store metadata next to the allocation. + //! Mainly meant to be run in Miri (but should also work outside it). + + use std::alloc::{AllocError, Allocator, Global, Layout}; + use std::ptr::NonNull; + use std::sync::Arc; + + struct MyMetadataAlloc; + + fn widen(layout: Layout) -> Layout { + Layout::from_size_align(layout.size() + 10, layout.align()) + .unwrap_or_else(|_| std::process::abort()) + } + + unsafe impl Allocator for MyMetadataAlloc { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + // imagine we are storing metadata in the extra bytes + let ptr = Global.allocate(widen(layout))?; + Ok(NonNull::slice_from_raw_parts(ptr.cast(), layout.size())) + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + // SAFETY: we get back a pointer with the same provenance as was + // returned by `allocate`, so it's okay to deallocate the full + // `layout.size() + 10` bytes through it. + unsafe { Global.deallocate(ptr, widen(layout)) } + } + } + + #[test] + fn issue_162719() { + drop(Arc::::new_uninit_in(MyMetadataAlloc)); + drop(Arc::new_in(1i32, MyMetadataAlloc)); + } + + #[test] + fn issue_162720() { + drop(Arc::new_cyclic_in(|_| 1i32, MyMetadataAlloc)); + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ba6d786fbbd89..c215560122022 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -230,6 +230,11 @@ pub mod offload; #[unstable(feature = "contracts", issue = "128044")] pub mod contracts; +#[allow(clippy::useless_attribute)] +#[expect( + ineffective_unstable_reexports, + reason = "accepted as stable after accidental stabilization in 1.96, see #154645" +)] #[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/collections/hash/mod.rs b/library/std/src/collections/hash/mod.rs index 348820af54bff..0476b0206f3d7 100644 --- a/library/std/src/collections/hash/mod.rs +++ b/library/std/src/collections/hash/mod.rs @@ -1,4 +1,4 @@ //! Unordered containers, implemented as hash-tables -pub mod map; -pub mod set; +pub(crate) mod map; +pub(crate) mod set; diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index dab1df38aa1f5..148f1c32b08b9 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -45,7 +45,7 @@ macro_rules! error_contains { // have permission, and return otherwise. This way, we still don't run these // tests most of the time, but at least we do if the user has the right // permissions. -pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { +pub(crate) fn got_symlink_permission(tmpdir: &TempDir) -> bool { if cfg!(not(windows)) || env::var_os("CI").is_some() { return true; } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index b104ea69cd1fc..527671442fb72 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -726,7 +726,7 @@ pub fn stdout() -> Stdout { // Flush the data and disable buffering during shutdown // by replacing the line writer by one with zero // buffering capacity. -pub fn cleanup() { +pub(crate) fn cleanup() { let mut initialized = false; let stdout = STDOUT.get_or_init(|| { initialized = true; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ef23cb402b5af..5f98aa54a6620 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -241,6 +241,7 @@ // Lints: #![warn(deprecated_in_future)] #![warn(missing_docs)] +#![warn(unreachable_pub)] #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] @@ -640,6 +641,7 @@ pub mod hash; pub mod io; pub mod net; pub mod num; +#[allow(unreachable_pub)] pub mod os; pub mod panic; #[unstable(feature = "pattern_type_macro", issue = "123646")] @@ -712,9 +714,9 @@ 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; - #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")] + #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] pub use std_detect::is_loongarch_feature_detected; - #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] + #[stable(feature = "riscv_ratified", since = "1.78.0")] pub use std_detect::is_riscv_feature_detected; #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] pub use std_detect::is_s390x_feature_detected; @@ -730,6 +732,7 @@ pub mod arch { #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; +#[allow(unreachable_pub)] mod sys; pub mod alloc; @@ -750,6 +753,11 @@ 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)] +#[expect( + ineffective_unstable_reexports, + reason = "accepted as stable after accidental stabilization in 1.96, see #154645" +)] #[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/net/udp.rs b/library/std/src/net/udp.rs index 70a0e663baefd..6448c9f2e0091 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -483,6 +483,9 @@ impl UdpSocket { /// /// Note that this might not have any effect on IPv6 sockets. /// + /// Since the underlying socket option value is byte-sized, + /// any value above 255 will result in an error. + /// /// # Examples /// /// ```no_run diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 5a4684a973942..f69e0a749f579 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -41,7 +41,7 @@ use crate::{fmt, intrinsics, process, thread}; #[doc(hidden)] #[allow(dead_code)] #[used(compiler)] -pub static EMPTY_PANIC: fn(&'static str) -> ! = +pub(crate) static EMPTY_PANIC: fn(&'static str) -> ! = begin_panic::<&'static str> as fn(&'static str) -> !; // Binary interface to the panic runtime that the standard library depends on. @@ -495,7 +495,7 @@ pub unsafe fn catch_unwind R>(f: F) -> Result R>(f: F) -> Result> { +pub(crate) unsafe fn catch_unwind R>(f: F) -> Result> { union Data { f: ManuallyDrop, r: ManuallyDrop, @@ -599,14 +599,14 @@ pub unsafe fn catch_unwind R>(f: F) -> Result bool { +pub(crate) fn panicking() -> bool { !panic_count::count_is_zero() } /// Entry point of panics from the core crate (`panic_impl` lang item). #[cfg(not(any(test, doctest)))] #[panic_handler] -pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { +pub(crate) fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { struct FormatStringPayload<'a> { inner: &'a core::panic::PanicMessage<'a>, string: Option, @@ -839,7 +839,7 @@ fn panic_with_hook( /// This is the entry point for `resume_unwind`. /// It just forwards the payload to the panic runtime. #[cfg_attr(panic = "immediate-abort", inline)] -pub fn resume_unwind(payload: Box) -> ! { +pub(crate) fn resume_unwind(payload: Box) -> ! { if let Some(must_abort) = panic_count::increase(false) { match must_abort { panic_count::MustAbort::PanicInHook => { diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 35ce30f1146e4..d5b01bcb3c29b 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -95,7 +95,7 @@ fn signal_reported_right() { } } -pub fn run_output(mut cmd: Command) -> String { +pub(crate) fn run_output(mut cmd: Command) -> String { let p = cmd.spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); @@ -361,7 +361,7 @@ fn test_wait_with_output_once() { } #[cfg(all(unix, not(target_os = "android")))] -pub fn env_cmd() -> Command { +pub(crate) fn env_cmd() -> Command { Command::new("env") } #[cfg(target_os = "android")] diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs index 6b2f4cb6ffd29..b4fee60a574ff 100644 --- a/library/std/src/sync/mpmc/context.rs +++ b/library/std/src/sync/mpmc/context.rs @@ -11,7 +11,7 @@ use crate::time::Instant; /// Thread-local context. #[derive(Debug, Clone)] -pub struct Context { +pub(crate) struct Context { inner: Arc, } @@ -34,7 +34,7 @@ struct Inner { impl Context { /// Creates a new context for the duration of the closure. #[inline] - pub fn with(f: F) -> R + pub(crate) fn with(f: F) -> R where F: FnOnce(&Context) -> R, { @@ -86,7 +86,7 @@ impl Context { /// /// On failure, the previously selected operation is returned. #[inline] - pub fn try_select(&self, select: Selected) -> Result<(), Selected> { + pub(crate) fn try_select(&self, select: Selected) -> Result<(), Selected> { self.inner .select .compare_exchange( @@ -103,7 +103,7 @@ impl Context { /// /// This method must be called after `try_select` succeeds and there is a packet to provide. #[inline] - pub fn store_packet(&self, packet: *mut ()) { + pub(crate) fn store_packet(&self, packet: *mut ()) { if !packet.is_null() { self.inner.packet.store(packet, Ordering::Release); } @@ -116,7 +116,7 @@ impl Context { /// # Safety /// This may only be called from the thread this `Context` belongs to. #[inline] - pub unsafe fn wait_until(&self, deadline: Option) -> Selected { + pub(crate) unsafe fn wait_until(&self, deadline: Option) -> Selected { loop { // Check whether an operation has been selected. let sel = Selected::from(self.inner.select.load(Ordering::Acquire)); @@ -147,13 +147,13 @@ impl Context { /// Unparks the thread this context belongs to. #[inline] - pub fn unpark(&self) { + pub(crate) fn unpark(&self) { self.inner.thread.unpark(); } /// Returns the id of the thread this context belongs to. #[inline] - pub fn thread_id(&self) -> usize { + pub(crate) fn thread_id(&self) -> usize { self.inner.thread_id } } diff --git a/library/std/src/sync/mpmc/select.rs b/library/std/src/sync/mpmc/select.rs index ff537aa686157..60f81d863ae4f 100644 --- a/library/std/src/sync/mpmc/select.rs +++ b/library/std/src/sync/mpmc/select.rs @@ -3,7 +3,7 @@ /// /// Each field contains data associated with a specific channel flavor. #[derive(Debug, Default)] -pub struct Token { +pub(crate) struct Token { pub(crate) array: super::array::ArrayToken, pub(crate) list: super::list::ListToken, #[allow(dead_code)] @@ -12,7 +12,7 @@ pub struct Token { /// Identifier associated with an operation by a specific thread on a specific channel. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Operation(usize); +pub(crate) struct Operation(usize); impl Operation { /// Creates an operation identifier from a mutable reference. @@ -21,7 +21,7 @@ impl Operation { /// reference should point to a variable that is specific to the thread and the operation, /// and is alive for the entire duration of a blocking operation. #[inline] - pub fn hook(r: &mut T) -> Operation { + pub(crate) fn hook(r: &mut T) -> Operation { let val = (r as *mut T).addr(); // Make sure that the pointer address doesn't equal the numerical representation of // `Selected::{Waiting, Aborted, Disconnected}`. @@ -32,7 +32,7 @@ impl Operation { /// Current state of a blocking operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Selected { +pub(crate) enum Selected { /// Still waiting for an operation. Waiting, diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index e3bcb149f648b..5a9bc7af6c36d 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -67,13 +67,13 @@ use crate::ops::{Deref, DerefMut}; )), repr(align(64)) )] -pub struct CachePadded { +pub(crate) struct CachePadded { value: T, } impl CachePadded { /// Pads and aligns a value to the length of a cache line. - pub fn new(value: T) -> CachePadded { + pub(crate) fn new(value: T) -> CachePadded { CachePadded:: { value } } } @@ -95,13 +95,13 @@ impl DerefMut for CachePadded { const SPIN_LIMIT: u32 = 6; /// Performs quadratic backoff in spin loops. -pub struct Backoff { +pub(crate) struct Backoff { step: Cell, } impl Backoff { /// Creates a new `Backoff`. - pub fn new() -> Self { + pub(crate) fn new() -> Self { Backoff { step: Cell::new(0) } } @@ -110,7 +110,7 @@ impl Backoff { /// This method should be used for retrying an operation because another thread made /// progress. i.e. on CAS failure. #[inline] - pub fn spin_light(&self) { + pub(crate) fn spin_light(&self) { let step = self.step.get().min(SPIN_LIMIT); for _ in 0..step.pow(2) { crate::hint::spin_loop(); @@ -123,7 +123,7 @@ impl Backoff { /// /// This method should be used in blocking loops where parking the thread is not an option. #[inline] - pub fn spin_heavy(&self) { + pub(crate) fn spin_heavy(&self) { if self.step.get() <= SPIN_LIMIT { for _ in 0..self.step.get().pow(2) { crate::hint::spin_loop() diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index 4216fb7ac5902..de913f0d421cc 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -201,7 +201,7 @@ impl Drop for SyncWaker { /// Returns a unique id for the current thread. #[inline] -pub fn current_thread_id() -> usize { +pub(crate) fn current_thread_id() -> usize { // `u8` is not drop so this variable will be available during thread destruction, // whereas `thread::current()` would not be thread_local! { static DUMMY: u8 = const { 0 } } diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 3c32ec34dee5b..62d4c2754effe 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -97,7 +97,7 @@ pub(crate) struct Flag { impl Flag { #[inline] - pub const fn new() -> Flag { + pub(crate) const fn new() -> Flag { Flag { #[cfg(panic = "unwind")] failed: AtomicBool::new(false), @@ -106,13 +106,13 @@ impl Flag { /// Checks the flag for an unguarded borrow, where we only care about existing poison. #[inline] - pub fn borrow(&self) -> LockResult<()> { + pub(crate) fn borrow(&self) -> LockResult<()> { if self.get() { Err(PoisonError::new(())) } else { Ok(()) } } /// Checks the flag for a guarded borrow, where we may also set poison when `done`. #[inline] - pub fn guard(&self) -> LockResult { + pub(crate) fn guard(&self) -> LockResult { let ret = Guard { #[cfg(panic = "unwind")] panicking: thread::panicking(), @@ -122,7 +122,7 @@ impl Flag { #[inline] #[cfg(panic = "unwind")] - pub fn done(&self, guard: &Guard) { + pub(crate) fn done(&self, guard: &Guard) { if !guard.panicking && thread::panicking() { self.failed.store(true, Ordering::Relaxed); } @@ -130,22 +130,22 @@ impl Flag { #[inline] #[cfg(not(panic = "unwind"))] - pub fn done(&self, _guard: &Guard) {} + pub(crate) fn done(&self, _guard: &Guard) {} #[inline] #[cfg(panic = "unwind")] - pub fn get(&self) -> bool { + pub(crate) fn get(&self) -> bool { self.failed.load(Ordering::Relaxed) } #[inline(always)] #[cfg(not(panic = "unwind"))] - pub fn get(&self) -> bool { + pub(crate) fn get(&self) -> bool { false } #[inline] - pub fn clear(&self) { + pub(crate) fn clear(&self) { #[cfg(panic = "unwind")] self.failed.store(false, Ordering::Relaxed) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 9defc24ee2e10..4e5de0209ad24 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -784,13 +784,11 @@ impl UdpSocket { } pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { + let ttl: u8 = multicast_ttl_v4 + .try_into() + .map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?; unsafe { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_TTL, - multicast_ttl_v4 as IpV4MultiCastType, - ) + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL, ttl as IpV4MultiCastType) } } diff --git a/library/std/src/test_helpers.rs b/library/std/src/test_helpers.rs index 7c20f38c863b6..5690a40648ff2 100644 --- a/library/std/src/test_helpers.rs +++ b/library/std/src/test_helpers.rs @@ -27,15 +27,15 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { SeedableRng::from_seed(seed) } -pub struct TempDir(PathBuf); +pub(crate) struct TempDir(PathBuf); impl TempDir { - pub fn join(&self, path: &str) -> PathBuf { + pub(crate) fn join(&self, path: &str) -> PathBuf { let TempDir(ref p) = *self; p.join(path) } - pub fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Path { let TempDir(ref p) = *self; p } @@ -56,7 +56,7 @@ impl Drop for TempDir { } #[track_caller] // for `test_rng` -pub fn tmpdir() -> TempDir { +pub(crate) fn tmpdir() -> TempDir { let p = env::temp_dir(); let mut r = test_rng(); let ret = p.join(&format!("rust-{}", r.next_u32())); diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs index 0dec359ccaec6..c22b97c39ecd5 100644 --- a/library/std/src/thread/lifecycle.rs +++ b/library/std/src/thread/lifecycle.rs @@ -125,7 +125,7 @@ pub(crate) struct ThreadInit { impl ThreadInit { /// Initialize the 'current thread' mechanism on this thread, returning the /// Rust entry point. - pub fn init(self: Box) -> Box { + pub(crate) fn init(self: Box) -> Box { // Set the current thread before any (de)allocations on the global allocator occur, // so that it may call std::thread::current() in its implementation. This is also // why we take Box, to ensure the Box is not destroyed until after this point. diff --git a/library/std/src/thread/thread.rs b/library/std/src/thread/thread.rs index d70c244c65d90..bfcf0fa4953d2 100644 --- a/library/std/src/thread/thread.rs +++ b/library/std/src/thread/thread.rs @@ -28,11 +28,11 @@ mod thread_name_string { } impl ThreadNameString { - pub fn as_cstr(&self) -> &CStr { + pub(crate) fn as_cstr(&self) -> &CStr { &self.inner } - pub fn as_str(&self) -> &str { + pub(crate) fn as_str(&self) -> &str { // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } } diff --git a/tests/ui/derives/derive-enum-repr-packed.rs b/tests/ui/derives/derive-enum-repr-packed.rs new file mode 100644 index 0000000000000..48284438185dc --- /dev/null +++ b/tests/ui/derives/derive-enum-repr-packed.rs @@ -0,0 +1,10 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/133025. + +#[derive(Debug)] +#[repr(packed)] //~ ERROR: the `repr(packed)` attribute cannot be used on enums +enum COption { + None, + Some(T), +} + +fn main() {} diff --git a/tests/ui/derives/derive-enum-repr-packed.stderr b/tests/ui/derives/derive-enum-repr-packed.stderr new file mode 100644 index 0000000000000..0f020bd7934a6 --- /dev/null +++ b/tests/ui/derives/derive-enum-repr-packed.stderr @@ -0,0 +1,10 @@ +error: the `repr(packed)` attribute cannot be used on enums + --> $DIR/derive-enum-repr-packed.rs:4:3 + | +LL | #[repr(packed)] + | ^^^^^^^^^^^^ + | + = help: the `repr(packed)` attribute can be applied to structs and unions + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/feature-gate-ineffective_unstable_reexports.rs b/tests/ui/feature-gates/feature-gate-ineffective_unstable_reexports.rs new file mode 100644 index 0000000000000..c9e13fb1eb7e3 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-ineffective_unstable_reexports.rs @@ -0,0 +1,7 @@ +//@ check-pass +//@ normalize-stderr: "(\n)\n$" -> "$1" +// This lint is only available with `staged_api`. +#![allow(ineffective_unstable_reexports)] +//~^ WARNING unknown lint: `ineffective_unstable_reexports` + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-ineffective_unstable_reexports.stderr b/tests/ui/feature-gates/feature-gate-ineffective_unstable_reexports.stderr new file mode 100644 index 0000000000000..01f6203606938 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-ineffective_unstable_reexports.stderr @@ -0,0 +1,10 @@ +warning: unknown lint: `ineffective_unstable_reexports` + --> $DIR/feature-gate-ineffective_unstable_reexports.rs:4:10 + | +LL | #![allow(ineffective_unstable_reexports)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `ineffective_unstable_reexports` lint is unstable + = note: `#[warn(unknown_lints)]` on by default + +warning: 1 warning emitted diff --git a/tests/ui/stability-attribute/auxiliary/non-staged-reexport-source.rs b/tests/ui/stability-attribute/auxiliary/non-staged-reexport-source.rs new file mode 100644 index 0000000000000..e69760c6090c5 --- /dev/null +++ b/tests/ui/stability-attribute/auxiliary/non-staged-reexport-source.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] +#![crate_name = "non_staged_reexport_source"] + +pub fn stable() {} 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/auxiliary/unstable-glob-source.rs b/tests/ui/stability-attribute/auxiliary/unstable-glob-source.rs new file mode 100644 index 0000000000000..3fd19a7aeb50b --- /dev/null +++ b/tests/ui/stability-attribute/auxiliary/unstable-glob-source.rs @@ -0,0 +1,13 @@ +#![crate_type = "lib"] +#![feature(staged_api)] +#![stable(feature = "unstable_glob_source_crate", since = "1.0.0")] + +#[unstable(feature = "unstable_glob_source", issue = "none")] +pub fn unstable_a() {} + +#[unstable( + feature = "unstable_glob_source", + reason = "different reason", + issue = "none" +)] +pub fn unstable_b() {} diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexports-glob.rs b/tests/ui/stability-attribute/ineffective-unstable-reexports-glob.rs new file mode 100644 index 0000000000000..514f5def02e7c --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexports-glob.rs @@ -0,0 +1,20 @@ +//@ aux-build:stable-glob-source.rs +//@ aux-build:unstable-glob-source.rs +//@ normalize-stderr: "(\n)\n$" -> "$1" + +#![crate_type = "lib"] +#![feature(staged_api)] +#![deny(ineffective_unstable_reexports)] +#![stable(feature = "reexport_test", since = "1.0.0")] + +extern crate stable_glob_source; +extern crate unstable_glob_source; + +// The unstable annotation is ineffective because every target is stable. +#[unstable(feature = "stable_glob_reexport", issue = "none")] +pub use stable_glob_source::*; +//~^ ERROR `#[unstable]` does not make this re-exported path unstable + +// The annotation remains meaningful because these targets are unstable. +#[unstable(feature = "unstable_glob_source", issue = "none")] +pub use unstable_glob_source::*; diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexports-glob.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexports-glob.stderr new file mode 100644 index 0000000000000..5f5af146d2098 --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexports-glob.stderr @@ -0,0 +1,13 @@ +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexports-glob.rs:15:9 + | +LL | pub use stable_glob_source::*; + | ^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/ineffective-unstable-reexports-glob.rs:7:9 + | +LL | #![deny(ineffective_unstable_reexports)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexports-grouped.rs b/tests/ui/stability-attribute/ineffective-unstable-reexports-grouped.rs new file mode 100644 index 0000000000000..6944df0281780 --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexports-grouped.rs @@ -0,0 +1,38 @@ +//@ aux-build:lint-stability.rs +//@ normalize-stderr: "(\n)\n$" -> "$1" + +#![crate_type = "lib"] +#![feature(staged_api)] +#![deny(ineffective_unstable_reexports)] +#![stable(feature = "reexport_test", since = "1.0.0")] + +extern crate lint_stability; + +// The annotation is ineffective when every re-exported target is stable. +#[unstable(feature = "grouped_stable", issue = "none")] +pub use lint_stability::{ + stable as grouped_stable_a, + stable_text as grouped_stable_b, +}; +//~^^^ ERROR `#[unstable]` does not make this re-exported path unstable + +// The annotation is not wholly ineffective if any target is unstable. +#[unstable(feature = "grouped_mixed", issue = "none")] +pub use lint_stability::{ + stable as grouped_mixed_stable, + unstable as grouped_mixed_unstable, +}; + +// Order must not matter. +#[unstable(feature = "unstable_test_feature", issue = "none")] +pub use lint_stability::{ + unstable as grouped_unstable_first, + stable as grouped_stable_second, +}; + +// All unstable targets are fine. +#[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-reexports-grouped.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexports-grouped.stderr new file mode 100644 index 0000000000000..abd32f7a58422 --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexports-grouped.stderr @@ -0,0 +1,13 @@ +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexports-grouped.rs:14:5 + | +LL | stable as grouped_stable_a, + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/ineffective-unstable-reexports-grouped.rs:6:9 + | +LL | #![deny(ineffective_unstable_reexports)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexports.rs b/tests/ui/stability-attribute/ineffective-unstable-reexports.rs new file mode 100644 index 0000000000000..2d1839be3c38f --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexports.rs @@ -0,0 +1,53 @@ +//@ aux-build:lint-stability.rs +//@ aux-build:non-staged-reexport-source.rs +//@ normalize-stderr: "(\n)\n$" -> "$1" + +#![crate_type = "lib"] +#![feature(staged_api)] +#![deny(ineffective_unstable_reexports)] +#![stable(feature = "reexport_test", since = "1.0.0")] + +extern crate core; +extern crate lint_stability; +extern crate non_staged_reexport_source; + +// `#[unstable]` cannot make an otherwise stable re-exported path unstable. +#[unstable(feature = "reexport_test_unstable", issue = "none")] +pub use lint_stability::stable as supposedly_unstable; +//~^ ERROR `#[unstable]` does not make this re-exported path unstable + +// Stable re-exports are outside the scope of this lint. +#[stable(feature = "rust1", since = "1.0.0")] +pub use lint_stability::stable as matching_stable; + +#[stable(feature = "different_stable_feature", since = "1.0.0")] +pub use lint_stability::stable as different_stable_feature; + +// `#[unstable]` remains meaningful when the target is itself unstable. +// The feature and issue do not need to match for this lint. +#[unstable(feature = "unstable_test_feature", issue = "none")] +pub use lint_stability::unstable as matching_unstable; + +#[unstable(feature = "different_unstable_feature", issue = "none")] +pub use lint_stability::unstable as different_unstable_feature; + +#[unstable(feature = "unstable_test_feature", issue = "12345")] +pub use lint_stability::unstable as different_unstable_issue; + +// Items from crates without staged API metadata are effectively stable. +#[unstable(feature = "non_staged_reexport", issue = "none")] +pub use non_staged_reexport_source::stable as supposedly_unstable_external; +//~^ ERROR `#[unstable]` does not make this re-exported path unstable + +// Primitives have no DefId, but they are 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 re-export is already behind an unstable module +// it still needs its own stability annotation but should not trigger the lint +#[unstable(feature = "unstable_module", issue = "none")] +pub mod unstable_module { + #[unstable(feature = "nested_unstable_reexport", issue = "none")] + pub use lint_stability::stable as stable_through_unstable_module; +} diff --git a/tests/ui/stability-attribute/ineffective-unstable-reexports.stderr b/tests/ui/stability-attribute/ineffective-unstable-reexports.stderr new file mode 100644 index 0000000000000..64213d0f39d0f --- /dev/null +++ b/tests/ui/stability-attribute/ineffective-unstable-reexports.stderr @@ -0,0 +1,25 @@ +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexports.rs:16:9 + | +LL | pub use lint_stability::stable as supposedly_unstable; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/ineffective-unstable-reexports.rs:7:9 + | +LL | #![deny(ineffective_unstable_reexports)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexports.rs:39:9 + | +LL | pub use non_staged_reexport_source::stable as supposedly_unstable_external; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[unstable]` does not make this re-exported path unstable + --> $DIR/ineffective-unstable-reexports.rs:44:9 + | +LL | pub use core::primitive::bool as supposedly_unstable_bool; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors