summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-01 04:30:33 +0000
committerbors <bors@rust-lang.org>2024-10-01 04:30:33 +0000
commit07f08ffb2dbc864d2127abedf7a5917b965c0a4b (patch)
treeb579b11e0e9571be6526e16ff7b0a8342465e4e1
parentf79ef02e4bc87eb6dc8374cd4bde8e86f9695cf9 (diff)
parentcd31b3acb3c963aff8d226030ac02379d741f3bd (diff)
Auto merge of #131076 - lukas-code:doc-stab2, r=notriddle
rustdoc: rewrite stability inheritance as a doc pass Since doc inlining can almost arbitrarily change the module hierarchy, we can't just use the HIR ancestors of an item to compute its effective stability. This PR moves the stability inheritance that I implemented in https://github.com/rust-lang/rust/pull/130798 into a new doc pass `propagate-stability` that runs after doc inlining and uses the post-inlining ancestors of an item to correctly compute its effective stability. fixes https://github.com/rust-lang/rust/issues/131020 r? `@notriddle`
-rw-r--r--compiler/rustc_attr/src/builtin.rs10
-rw-r--r--src/librustdoc/clean/auto_trait.rs1
-rw-r--r--src/librustdoc/clean/blanket_impl.rs1
-rw-r--r--src/librustdoc/clean/inline.rs1
-rw-r--r--src/librustdoc/clean/types.rs60
-rw-r--r--src/librustdoc/html/render/print_item.rs31
-rw-r--r--src/librustdoc/passes/mod.rs5
-rw-r--r--src/librustdoc/passes/propagate_stability.rs72
-rw-r--r--tests/rustdoc-ui/issues/issue-91713.stdout2
-rw-r--r--tests/rustdoc/stability.rs49
10 files changed, 160 insertions, 72 deletions
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 762ebc47ca9..28d73fbe9f3 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -80,6 +80,10 @@ impl Stability {
pub fn is_stable(&self) -> bool {
self.level.is_stable()
}
+
+ pub fn stable_since(&self) -> Option<StableSince> {
+ self.level.stable_since()
+ }
}
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
@@ -170,6 +174,12 @@ impl StabilityLevel {
pub fn is_stable(&self) -> bool {
matches!(self, StabilityLevel::Stable { .. })
}
+ pub fn stable_since(&self) -> Option<StableSince> {
+ match *self {
+ StabilityLevel::Stable { since, .. } => Some(since),
+ StabilityLevel::Unstable { .. } => None,
+ }
+ }
}
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 08c88fc950d..d966f993104 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -117,6 +117,7 @@ fn synthesize_auto_trait_impl<'tcx>(
name: None,
inner: Box::new(clean::ItemInner {
attrs: Default::default(),
+ stability: None,
kind: clean::ImplItem(Box::new(clean::Impl {
safety: hir::Safety::Safe,
generics,
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 36821294885..1f3cb4a61b8 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -87,6 +87,7 @@ pub(crate) fn synthesize_blanket_impls(
item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
inner: Box::new(clean::ItemInner {
attrs: Default::default(),
+ stability: None,
kind: clean::ImplItem(Box::new(clean::Impl {
safety: hir::Safety::Safe,
generics: clean_ty_generics(
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index d3c4ef4dc90..e7f921eef7f 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -672,6 +672,7 @@ fn build_module_items(
item_id: ItemId::DefId(did),
inner: Box::new(clean::ItemInner {
attrs: Default::default(),
+ stability: None,
kind: clean::ImportItem(clean::Import::new_simple(
item.ident.name,
clean::ImportSource {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index b9c5e8e787b..a3277e8ca92 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -6,7 +6,7 @@ use std::{fmt, iter};
use arrayvec::ArrayVec;
use rustc_ast_pretty::pprust;
-use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince};
+use rustc_attr::{ConstStability, Deprecation, Stability, StableSince};
use rustc_const_eval::const_eval::is_unstable_const_fn;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -333,6 +333,8 @@ pub(crate) struct ItemInner {
/// E.g., struct vs enum vs function.
pub(crate) kind: ItemKind,
pub(crate) attrs: Attributes,
+ /// The effective stability, filled out by the `propagate-stability` pass.
+ pub(crate) stability: Option<Stability>,
}
impl std::ops::Deref for Item {
@@ -381,46 +383,17 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
}
impl Item {
+ /// Returns the effective stability of the item.
+ ///
+ /// This method should only be called after the `propagate-stability` pass has been run.
pub(crate) fn stability(&self, tcx: TyCtxt<'_>) -> Option<Stability> {
- let (mut def_id, mut stability) = if let Some(inlined) = self.inline_stmt_id {
- let inlined_def_id = inlined.to_def_id();
- if let Some(stability) = tcx.lookup_stability(inlined_def_id) {
- (inlined_def_id, stability)
- } else {
- // For re-exports into crates without `staged_api`, reuse the original stability.
- // This is necessary, because we always want to mark unstable items.
- let def_id = self.def_id()?;
- return tcx.lookup_stability(def_id);
- }
- } else {
- let def_id = self.def_id()?;
- let stability = tcx.lookup_stability(def_id)?;
- (def_id, stability)
- };
-
- let StabilityLevel::Stable { mut since, allowed_through_unstable_modules: false } =
- stability.level
- else {
- return Some(stability);
- };
-
- // If any of the item's ancestors was stabilized later or is still unstable,
- // then report the ancestor's stability instead.
- while let Some(parent_def_id) = tcx.opt_parent(def_id) {
- if let Some(parent_stability) = tcx.lookup_stability(parent_def_id) {
- match parent_stability.level {
- StabilityLevel::Unstable { .. } => return Some(parent_stability),
- StabilityLevel::Stable { since: parent_since, .. } => {
- if parent_since > since {
- stability = parent_stability;
- since = parent_since;
- }
- }
- }
- }
- def_id = parent_def_id;
- }
- Some(stability)
+ let stability = self.inner.stability;
+ debug_assert!(
+ stability.is_some()
+ || self.def_id().is_none_or(|did| tcx.lookup_stability(did).is_none()),
+ "missing stability for cleaned item: {self:?}",
+ );
+ stability
}
pub(crate) fn const_stability(&self, tcx: TyCtxt<'_>) -> Option<ConstStability> {
@@ -502,7 +475,7 @@ impl Item {
Item {
item_id: def_id.into(),
- inner: Box::new(ItemInner { kind, attrs }),
+ inner: Box::new(ItemInner { kind, attrs, stability: None }),
name,
cfg,
inline_stmt_id: None,
@@ -638,10 +611,7 @@ impl Item {
}
pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
- match self.stability(tcx)?.level {
- StabilityLevel::Stable { since, .. } => Some(since),
- StabilityLevel::Unstable { .. } => None,
- }
+ self.stability(tcx).and_then(|stability| stability.stable_since())
}
pub(crate) fn is_non_exhaustive(&self) -> bool {
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index d120e7f36eb..38276e4d20c 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -436,16 +436,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
}
clean::ImportItem(ref import) => {
- let stab_tags = if let Some(import_def_id) = import.source.did {
- // Just need an item with the correct def_id and attrs
- let import_item =
- clean::Item { item_id: import_def_id.into(), ..(*myitem).clone() };
-
- let stab_tags = Some(extra_info_tags(&import_item, item, tcx).to_string());
- stab_tags
- } else {
- None
- };
+ let stab_tags = import.source.did.map_or_else(String::new, |import_def_id| {
+ extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
+ });
w.write_str(ITEM_TABLE_ROW_OPEN);
let id = match import.kind {
@@ -454,7 +447,6 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
}
clean::ImportKind::Glob => String::new(),
};
- let stab_tags = stab_tags.unwrap_or_default();
let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() {
("", "")
} else {
@@ -521,7 +513,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
{docs_before}{docs}{docs_after}",
name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
visibility_and_hidden = visibility_and_hidden,
- stab_tags = extra_info_tags(myitem, item, tcx),
+ stab_tags = extra_info_tags(tcx, myitem, item, None),
class = myitem.type_(),
unsafety_flag = unsafety_flag,
href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
@@ -544,9 +536,10 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
/// Render the stability, deprecation and portability tags that are displayed in the item's summary
/// at the module level.
fn extra_info_tags<'a, 'tcx: 'a>(
+ tcx: TyCtxt<'tcx>,
item: &'a clean::Item,
parent: &'a clean::Item,
- tcx: TyCtxt<'tcx>,
+ import_def_id: Option<DefId>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
fn tag_html<'a>(
@@ -564,18 +557,18 @@ fn extra_info_tags<'a, 'tcx: 'a>(
}
// The trailing space after each tag is to space it properly against the rest of the docs.
- if let Some(depr) = &item.deprecation(tcx) {
+ let deprecation = import_def_id
+ .map_or_else(|| item.deprecation(tcx), |import_did| tcx.lookup_deprecation(import_did));
+ if let Some(depr) = deprecation {
let message = if depr.is_in_effect() { "Deprecated" } else { "Deprecation planned" };
write!(f, "{}", tag_html("deprecated", "", message))?;
}
// The "rustc_private" crates are permanently unstable so it makes no sense
// to render "unstable" everywhere.
- if item
- .stability(tcx)
- .as_ref()
- .is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private)
- {
+ let stability = import_def_id
+ .map_or_else(|| item.stability(tcx), |import_did| tcx.lookup_stability(import_did));
+ if stability.is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private) {
write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
}
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index b1dc766049d..f5b78023721 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -23,6 +23,9 @@ pub(crate) use self::strip_priv_imports::STRIP_PRIV_IMPORTS;
mod propagate_doc_cfg;
pub(crate) use self::propagate_doc_cfg::PROPAGATE_DOC_CFG;
+mod propagate_stability;
+pub(crate) use self::propagate_stability::PROPAGATE_STABILITY;
+
pub(crate) mod collect_intra_doc_links;
pub(crate) use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS;
@@ -75,6 +78,7 @@ pub(crate) const PASSES: &[Pass] = &[
STRIP_PRIVATE,
STRIP_PRIV_IMPORTS,
PROPAGATE_DOC_CFG,
+ PROPAGATE_STABILITY,
COLLECT_INTRA_DOC_LINKS,
COLLECT_TRAIT_IMPLS,
CALCULATE_DOC_COVERAGE,
@@ -91,6 +95,7 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
ConditionalPass::always(PROPAGATE_DOC_CFG),
+ ConditionalPass::always(PROPAGATE_STABILITY),
ConditionalPass::always(RUN_LINTS),
];
diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs
new file mode 100644
index 00000000000..f51e993bfa5
--- /dev/null
+++ b/src/librustdoc/passes/propagate_stability.rs
@@ -0,0 +1,72 @@
+//! Propagates stability to child items.
+//!
+//! The purpose of this pass is to make items whose parents are "more unstable"
+//! than the item itself inherit the parent's stability.
+//! For example, [`core::error::Error`] is marked as stable since 1.0.0, but the
+//! [`core::error`] module is marked as stable since 1.81.0, so we want to show
+//! [`core::error::Error`] as stable since 1.81.0 as well.
+
+use rustc_attr::{Stability, StabilityLevel};
+use rustc_hir::def_id::CRATE_DEF_ID;
+
+use crate::clean::{Crate, Item, ItemId};
+use crate::core::DocContext;
+use crate::fold::DocFolder;
+use crate::passes::Pass;
+
+pub(crate) const PROPAGATE_STABILITY: Pass = Pass {
+ name: "propagate-stability",
+ run: propagate_stability,
+ description: "propagates stability to child items",
+};
+
+pub(crate) fn propagate_stability(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
+ let crate_stability = cx.tcx.lookup_stability(CRATE_DEF_ID);
+ StabilityPropagator { parent_stability: crate_stability, cx }.fold_crate(cr)
+}
+
+struct StabilityPropagator<'a, 'tcx> {
+ parent_stability: Option<Stability>,
+ cx: &'a mut DocContext<'tcx>,
+}
+
+impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> {
+ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
+ let parent_stability = self.parent_stability;
+
+ let stability = match item.item_id {
+ 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
+ }
+ }
+ ItemId::Auto { .. } | ItemId::Blanket { .. } => {
+ // For now, we do now show stability for synthesized impls.
+ None
+ }
+ };
+
+ item.inner.stability = stability;
+ self.parent_stability = stability;
+ let item = self.fold_item_recur(item);
+ self.parent_stability = parent_stability;
+
+ Some(item)
+ }
+}
diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout
index 1ea3dbfb59f..790e58b0df9 100644
--- a/tests/rustdoc-ui/issues/issue-91713.stdout
+++ b/tests/rustdoc-ui/issues/issue-91713.stdout
@@ -5,6 +5,7 @@ strip-aliased-non-local - strips all non-local private aliased items from the ou
strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports
strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate
propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items
+ propagate-stability - propagates stability to child items
collect-intra-doc-links - resolves intra-doc links
collect-trait-impls - retrieves trait impls for items in the crate
calculate-doc-coverage - counts the number of items with and without documentation
@@ -19,6 +20,7 @@ strip-aliased-non-local
strip-priv-imports (when --document-private-items)
collect-intra-doc-links
propagate-doc-cfg
+ propagate-stability
run-lints
Passes run with `--show-coverage`:
diff --git a/tests/rustdoc/stability.rs b/tests/rustdoc/stability.rs
index de855b43ba5..fc72154cad8 100644
--- a/tests/rustdoc/stability.rs
+++ b/tests/rustdoc/stability.rs
@@ -25,28 +25,61 @@ pub struct ZzStable;
#[unstable(feature = "unstable", issue = "none")]
pub mod unstable {
- //@ !hasraw stability/unstable/struct.Foo.html '//span[@class="since"]'
+ //@ !hasraw stability/unstable/struct.StableInUnstable.html \
+ // '//span[@class="since"]'
//@ has - '//div[@class="stab unstable"]' 'experimental'
#[stable(feature = "rust1", since = "1.0.0")]
- pub struct Foo;
+ pub struct StableInUnstable;
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub mod stable_in_unstable {
+ //@ !hasraw stability/unstable/stable_in_unstable/struct.Inner.html \
+ // '//span[@class="since"]'
+ //@ has - '//div[@class="stab unstable"]' 'experimental'
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub struct Inner;
+ }
}
#[stable(feature = "rust2", since = "2.2.2")]
pub mod stable_later {
- //@ has stability/stable_later/struct.Bar.html '//span[@class="since"]' '2.2.2'
+ //@ has stability/stable_later/struct.StableInLater.html \
+ // '//span[@class="since"]' '2.2.2'
#[stable(feature = "rust1", since = "1.0.0")]
- pub struct Bar;
+ 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'
+ #[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.Foo.html '//span[@class="since"]' '1.0.0'
+ //@ has stability/stable_earlier/struct.StableInUnstable.html \
+ // '//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'
+ #[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'
#[doc(inline)]
#[stable(feature = "rust1", since = "1.0.0")]
- pub use crate::unstable::Foo;
+ pub use crate::stable_later::StableInLater;
- //@ has stability/stable_earlier/struct.Bar.html '//span[@class="since"]' '1.0.0'
+ //@ has stability/stable_earlier/stable_in_later/struct.Inner.html \
+ // '//span[@class="since"]' '1.0.0'
#[doc(inline)]
#[stable(feature = "rust1", since = "1.0.0")]
- pub use crate::stable_later::Bar;
+ pub use crate::stable_later::stable_in_later;
}