diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 68ec7870811d2..94df0fc270255 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -97,7 +97,7 @@ pub(crate) mod Enzyme_AD { use rustc_session::filesearch; use super::{CConcreteType, CTypeTreeRef, Context}; - use crate::llvm::{EnzymeTypeTree, LLVMRustVersionMajor}; + use crate::llvm::{self, EnzymeTypeTree}; type EnzymeSetCLBoolFn = unsafe extern "C" fn(*mut c_void, u8); type EnzymeSetCLStringFn = unsafe extern "C" fn(*mut c_void, *const c_char); @@ -434,7 +434,7 @@ pub(crate) mod Enzyme_AD { } fn get_enzyme_path(sysroot: &Sysroot) -> Result { - let llvm_version_major = unsafe { LLVMRustVersionMajor() }; + let llvm_version_major = llvm::LLVMRustVersionMajor(); let path_buf = sysroot .all_paths() diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c20b4ccd776da..abacef3710f4e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -894,7 +894,11 @@ unsafe extern "C" { SLen: c_uint, ) -> MetadataKindId; - pub(crate) fn LLVMGetVersion(major: &mut c_uint, minor: &mut c_uint, patch: &mut c_uint); + /// Gets the actual version of LLVM that we are linked to at runtime. + /// + /// # Safety + /// Can be called without initializing LLVM. + pub(crate) safe fn LLVMGetVersion(major: &mut c_uint, minor: &mut c_uint, patch: &mut c_uint); pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull); @@ -2176,9 +2180,9 @@ unsafe extern "C" { /// Returns the LLVM major version that the compiler was built with. /// /// Note that this is hard-coded as `LLVM_VERSION_MAJOR` when `RustWrapper.cpp` is built. This - /// could be different than what the runtime LLVM library reports in `LLVMGetVersion`, so we + /// could be different than what the runtime LLVM library reports in [`LLVMGetVersion`], so we /// assert their equality in `configure_llvm`. - pub(crate) fn LLVMRustVersionMajor() -> u32; + pub(crate) safe fn LLVMRustVersionMajor() -> u32; /// Add LLVM module flags. /// diff --git a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs index 7ecf450ab1dba..fecb9e40eab88 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs @@ -14,7 +14,7 @@ use rustc_fs_util::path_to_c_string; use rustc_session::config::host_tuple; use rustc_session::filesearch; -use crate::llvm::LLVMRustVersionMajor; +use crate::llvm; pub(crate) struct RustOffloadWrapper { LLVMRustBundleImages: LLVMRustBundleImagesFn, @@ -119,7 +119,7 @@ impl RustOffloadWrapper { fn get_offload_and_lld_paths( sysroot: &rustc_session::config::Sysroot, ) -> Result<(PathBuf, Option), RustOffloadLibraryError> { - let llvm_version_major = unsafe { LLVMRustVersionMajor() }; + let llvm_version_major = llvm::LLVMRustVersionMajor(); let mut searched = Vec::new(); for root in sysroot.all_paths() { diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index a5441b1ef1135..223cbf2aae31f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -459,15 +459,13 @@ pub(crate) fn print_version() { println!("LLVM version: {major}.{minor}.{patch}"); } +/// Returns the version of LLVM that we are actually linked to at runtime. pub(crate) fn get_version() -> (u32, u32, u32) { - // Can be called without initializing LLVM - unsafe { - let mut llvm_major = 0; - let mut llvm_minor = 0; - let mut llvm_patch = 0; - llvm::LLVMGetVersion(&mut llvm_major, &mut llvm_minor, &mut llvm_patch); - (llvm_major, llvm_minor, llvm_patch) - } + let mut llvm_major = 0; + let mut llvm_minor = 0; + let mut llvm_patch = 0; + llvm::LLVMGetVersion(&mut llvm_major, &mut llvm_minor, &mut llvm_patch); + (llvm_major, llvm_minor, llvm_patch) } pub(crate) fn print_passes() { 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..e8393efdcad75 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,38 @@ 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 + /// + /// ```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() {} + /// ``` + /// + /// {{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/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/lib.rs b/library/std/src/lib.rs index ef23cb402b5af..18ff174af2375 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -712,9 +712,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; @@ -750,6 +750,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/src/doc/rustc/src/platform-support/riscv64a23-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/riscv64a23-unknown-linux-gnu.md index df0c971f249a4..b50ee50db9b11 100644 --- a/src/doc/rustc/src/platform-support/riscv64a23-unknown-linux-gnu.md +++ b/src/doc/rustc/src/platform-support/riscv64a23-unknown-linux-gnu.md @@ -12,7 +12,7 @@ This target will enable all mandatory features of rva23u64 by default. ## Requirements -This target can be sucessfully build on the following platform: ubuntu 24.04 (Linux Kernel version 6.8.0, glibc 2.39). +This target can be successfully built on the following platform: ubuntu 24.04 (Linux Kernel version 6.8.0, glibc 2.39). Other platforms may work, but are not tested. Please contact us if you encounter any issues. diff --git a/tests/ui/associated-types/associated-types-match-despite-generic.rs b/tests/ui/associated-types/associated-types-match-despite-generic.rs new file mode 100644 index 0000000000000..04fc374e59a0c --- /dev/null +++ b/tests/ui/associated-types/associated-types-match-despite-generic.rs @@ -0,0 +1,21 @@ +//! Regression test for . +//@compile-flags: -Znext-solver=globally +//@ check-pass + +struct Outer; +struct Inner; +trait Id { + type This; +} +impl Id for T { + type This = T; +} + +fn free(x: T) -> >::This +where + >::This: Id>::This>, +{ + x +} + +fn main() {} diff --git a/tests/ui/attributes/positions/link-section.rs b/tests/ui/attributes/positions/link-section.rs new file mode 100644 index 0000000000000..a7d39b690664c --- /dev/null +++ b/tests/ui/attributes/positions/link-section.rs @@ -0,0 +1,11 @@ +//! Checks that `link_section` cannot be used on foreign statics. +#![crate_type = "lib"] + +//@ edition:2024 +//@ check-pass +// Regression test for . +unsafe extern "C" { + #[unsafe(link_section = "__DATA,__buffer")] //~ WARN attribute cannot be used on foreign statics + //~| WARN previously accepted + pub static mut a: [u8; 1024]; +} diff --git a/tests/ui/attributes/positions/link-section.stderr b/tests/ui/attributes/positions/link-section.stderr new file mode 100644 index 0000000000000..56aa525e72387 --- /dev/null +++ b/tests/ui/attributes/positions/link-section.stderr @@ -0,0 +1,12 @@ +warning: the `link_section` attribute cannot be used on foreign statics + --> $DIR/link-section.rs:8:14 + | +LL | #[unsafe(link_section = "__DATA,__buffer")] + | ^^^^^^^^^^^^ + | + = help: the `link_section` attribute can be applied to functions and statics + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: requested on the command line with `-W unused-attributes` + +warning: 1 warning emitted + 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