diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index f55479687f8e7..a28487cc79e5d 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -9,7 +9,7 @@ use rustc_attr::{Stability, StabilityLevel}; use rustc_hir::def_id::CRATE_DEF_ID; -use crate::clean::{Crate, Item, ItemId}; +use crate::clean::{Crate, Item, ItemId, ItemKind}; use crate::core::DocContext; use crate::fold::DocFolder; use crate::passes::Pass; @@ -38,22 +38,45 @@ impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> { ItemId::DefId(def_id) => { let own_stability = self.cx.tcx.lookup_stability(def_id); - // If any of the item's parents was stabilized later or is still unstable, - // then use the parent's stability instead. - if let Some(own_stab) = own_stability - && let StabilityLevel::Stable { - since: own_since, - allowed_through_unstable_modules: false, - } = own_stab.level - && let Some(parent_stab) = parent_stability - && (parent_stab.is_unstable() - || parent_stab - .stable_since() - .is_some_and(|parent_since| parent_since > own_since)) - { - parent_stability - } else { - own_stability + let (ItemKind::StrippedItem(box kind) | kind) = &item.kind; + match kind { + ItemKind::ExternCrateItem { .. } + | ItemKind::ImportItem(..) + | ItemKind::StructItem(..) + | ItemKind::UnionItem(..) + | ItemKind::EnumItem(..) + | ItemKind::FunctionItem(..) + | ItemKind::ModuleItem(..) + | ItemKind::TypeAliasItem(..) + | ItemKind::StaticItem(..) + | ItemKind::TraitItem(..) + | ItemKind::TraitAliasItem(..) + | ItemKind::StructFieldItem(..) + | ItemKind::VariantItem(..) + | ItemKind::ForeignFunctionItem(..) + | ItemKind::ForeignStaticItem(..) + | ItemKind::ForeignTypeItem + | ItemKind::MacroItem(..) + | ItemKind::ProcMacroItem(..) + | ItemKind::ConstantItem(..) => { + // If any of the item's parents was stabilized later or is still unstable, + // then use the parent's stability instead. + merge_stability(own_stability, parent_stability) + } + + // Don't inherit the parent's stability for these items, because they + // are potentially accessible even if the parent is more unstable. + ItemKind::ImplItem(..) + | ItemKind::TyMethodItem(..) + | ItemKind::MethodItem(..) + | ItemKind::TyAssocConstItem(..) + | ItemKind::AssocConstItem(..) + | ItemKind::TyAssocTypeItem(..) + | ItemKind::AssocTypeItem(..) + | ItemKind::PrimitiveItem(..) + | ItemKind::KeywordItem => own_stability, + + ItemKind::StrippedItem(..) => unreachable!(), } } ItemId::Auto { .. } | ItemId::Blanket { .. } => { @@ -70,3 +93,20 @@ impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> { Some(item) } } + +fn merge_stability( + own_stability: Option, + parent_stability: Option, +) -> Option { + if let Some(own_stab) = own_stability + && let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: false } = + own_stab.level + && let Some(parent_stab) = parent_stability + && (parent_stab.is_unstable() + || parent_stab.stable_since().is_some_and(|parent_since| parent_since > own_since)) + { + parent_stability + } else { + own_stability + } +} diff --git a/tests/rustdoc/stability.rs b/tests/rustdoc/stability.rs index fc72154cad864..550eb0bc13776 100644 --- a/tests/rustdoc/stability.rs +++ b/tests/rustdoc/stability.rs @@ -1,6 +1,8 @@ #![feature(staged_api)] +#![feature(rustc_attrs)] +#![feature(rustdoc_internals)] -#![stable(feature = "rust1", since = "1.0.0")] +#![stable(feature = "core", since = "1.6.0")] //@ has stability/index.html //@ has - '//ul[@class="item-table"]/li[1]//a' AaStable @@ -26,7 +28,7 @@ pub struct ZzStable; #[unstable(feature = "unstable", issue = "none")] pub mod unstable { //@ !hasraw stability/unstable/struct.StableInUnstable.html \ - // '//span[@class="since"]' + // '//div[@class="main-heading"]//span[@class="since"]' //@ has - '//div[@class="stab unstable"]' 'experimental' #[stable(feature = "rust1", since = "1.0.0")] pub struct StableInUnstable; @@ -34,52 +36,136 @@ pub mod unstable { #[stable(feature = "rust1", since = "1.0.0")] pub mod stable_in_unstable { //@ !hasraw stability/unstable/stable_in_unstable/struct.Inner.html \ - // '//span[@class="since"]' + // '//div[@class="main-heading"]//span[@class="since"]' //@ has - '//div[@class="stab unstable"]' 'experimental' #[stable(feature = "rust1", since = "1.0.0")] pub struct Inner; } + + //@ has stability/struct.AaStable.html \ + // '//*[@id="method.foo"]//span[@class="since"]' '2.2.2' + impl super::AaStable { + #[stable(feature = "rust2", since = "2.2.2")] + pub fn foo() {} + } + + //@ has stability/unstable/struct.StableInUnstable.html \ + // '//*[@id="method.foo"]//span[@class="since"]' '1.0.0' + impl StableInUnstable { + #[stable(feature = "rust1", since = "1.0.0")] + pub fn foo() {} + } +} + +#[unstable(feature = "unstable", issue = "none")] +#[doc(hidden)] +pub mod unstable_stripped { + //@ has stability/struct.AaStable.html \ + // '//*[@id="method.foo"]//span[@class="since"]' '2.2.2' + impl super::AaStable { + #[stable(feature = "rust2", since = "2.2.2")] + pub fn foo() {} + } } #[stable(feature = "rust2", since = "2.2.2")] pub mod stable_later { //@ has stability/stable_later/struct.StableInLater.html \ - // '//span[@class="since"]' '2.2.2' + // '//div[@class="main-heading"]//span[@class="since"]' '2.2.2' #[stable(feature = "rust1", since = "1.0.0")] pub struct StableInLater; #[stable(feature = "rust1", since = "1.0.0")] pub mod stable_in_later { //@ has stability/stable_later/stable_in_later/struct.Inner.html \ - // '//span[@class="since"]' '2.2.2' + // '//div[@class="main-heading"]//span[@class="since"]' '2.2.2' #[stable(feature = "rust1", since = "1.0.0")] pub struct Inner; } } #[stable(feature = "rust1", since = "1.0.0")] -pub mod stable_earlier { - //@ has stability/stable_earlier/struct.StableInUnstable.html \ - // '//span[@class="since"]' '1.0.0' +#[rustc_allowed_through_unstable_modules] +pub mod stable_earlier1 { + //@ has stability/stable_earlier1/struct.StableInUnstable.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.0.0' + //@ has - '//*[@id="method.foo"]//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::unstable::StableInUnstable; + + //@ has stability/stable_earlier1/stable_in_unstable/struct.Inner.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::unstable::stable_in_unstable; + + //@ has stability/stable_earlier1/struct.StableInLater.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::stable_later::StableInLater; + + //@ has stability/stable_earlier1/stable_in_later/struct.Inner.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::stable_later::stable_in_later; +} + +/// These will inherit the crate stability. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod stable_earlier2 { + //@ has stability/stable_earlier2/struct.StableInUnstable.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.6.0' + //@ has - '//*[@id="method.foo"]//span[@class="since"]' '1.0.0' #[doc(inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use crate::unstable::StableInUnstable; - //@ has stability/stable_earlier/stable_in_unstable/struct.Inner.html \ - // '//span[@class="since"]' '1.0.0' + //@ has stability/stable_earlier2/stable_in_unstable/struct.Inner.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.6.0' #[doc(inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use crate::unstable::stable_in_unstable; - //@ has stability/stable_earlier/struct.StableInLater.html \ - // '//span[@class="since"]' '1.0.0' + //@ has stability/stable_earlier2/struct.StableInLater.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.6.0' #[doc(inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use crate::stable_later::StableInLater; - //@ has stability/stable_earlier/stable_in_later/struct.Inner.html \ - // '//span[@class="since"]' '1.0.0' + //@ has stability/stable_earlier2/stable_in_later/struct.Inner.html \ + // '//div[@class="main-heading"]//span[@class="since"]' '1.6.0' #[doc(inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use crate::stable_later::stable_in_later; } + +//@ !hasraw stability/trait.UnstableTraitWithStableMethod.html \ +// '//div[@class="main-heading"]//span[@class="since"]' +//@ has - '//*[@id="tymethod.foo"]//span[@class="since"]' '1.0.0' +//@ has - '//*[@id="method.bar"]//span[@class="since"]' '1.0.0' +#[unstable(feature = "unstable", issue = "none")] +pub trait UnstableTraitWithStableMethod { + #[stable(feature = "rust1", since = "1.0.0")] + fn foo(); + #[stable(feature = "rust1", since = "1.0.0")] + fn bar() {} +} + +//@ has stability/primitive.i32.html \ +// '//div[@class="main-heading"]//span[@class="since"]' '1.0.0' +#[rustc_doc_primitive = "i32"] +// +/// `i32` is always stable in 1.0, even if you look at it from core. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_i32 {} + +//@ has stability/keyword.if.html \ +// '//div[@class="main-heading"]//span[@class="since"]' '1.0.0' +#[doc(keyword = "if")] +// +/// We currently don't document stability for keywords, but let's test it anyway. +#[stable(feature = "rust1", since = "1.0.0")] +mod if_keyword {}