rustdoc: rewrite stability inheritance as a pass
This commit is contained in:
parent
19252bde65
commit
cd31b3acb3
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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 @@ pub(crate) fn from_def_id_and_attrs_and_parts(
|
||||
|
||||
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,
|
||||
|
@ -436,16 +436,9 @@ fn cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
|
||||
}
|
||||
|
||||
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 cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
|
||||
}
|
||||
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 cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
|
||||
{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 cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
|
||||
/// 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 tag_html<'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"))?;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,9 @@
|
||||
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) enum Condition {
|
||||
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) enum Condition {
|
||||
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),
|
||||
];
|
||||
|
||||
|
72
src/librustdoc/passes/propagate_stability.rs
Normal file
72
src/librustdoc/passes/propagate_stability.rs
Normal file
@ -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)
|
||||
}
|
||||
}
|
@ -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`:
|
||||
|
@ -25,28 +25,61 @@ pub struct Unstable {
|
||||
|
||||
#[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::Foo;
|
||||
pub use crate::unstable::StableInUnstable;
|
||||
|
||||
//@ has stability/stable_earlier/struct.Bar.html '//span[@class="since"]' '1.0.0'
|
||||
//@ 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::stable_later::Bar;
|
||||
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::stable_later::StableInLater;
|
||||
|
||||
//@ 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::stable_in_later;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user