rustdoc/passes/
propagate_stability.rs1use rustc_hir::def_id::CRATE_DEF_ID;
10use rustc_hir::{Stability, StabilityLevel};
11
12use crate::clean::{Crate, Item, ItemId, ItemKind};
13use crate::core::DocContext;
14use crate::fold::DocFolder;
15
16pub(super) fn propagate_stability(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
17 let crate_stability = cx.tcx.lookup_stability(CRATE_DEF_ID);
18 StabilityPropagator { parent_stability: crate_stability, cx }.fold_crate(cr)
19}
20
21struct StabilityPropagator<'a, 'tcx> {
22 parent_stability: Option<Stability>,
23 cx: &'a mut DocContext<'tcx>,
24}
25
26impl DocFolder for StabilityPropagator<'_, '_> {
27 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
28 let parent_stability = self.parent_stability;
29
30 let stability = match item.item_id {
31 ItemId::DefId(def_id) => {
32 let item_stability = self.cx.tcx.lookup_stability(def_id);
33 let inline_stability =
34 item.inline_stmt_id.and_then(|did| self.cx.tcx.lookup_stability(did));
35 let is_glob_export = item.inline_stmt_id.map(|id| {
36 let hir_id = self.cx.tcx.local_def_id_to_hir_id(id);
37 matches!(
38 self.cx.tcx.hir_node(hir_id),
39 rustc_hir::Node::Item(rustc_hir::Item {
40 kind: rustc_hir::ItemKind::Use(_, rustc_hir::UseKind::Glob),
41 ..
42 })
43 )
44 });
45 let own_stability = if let Some(item_stab) = item_stability
46 && let StabilityLevel::Stable { since: _, allowed_through_unstable_modules } =
47 item_stab.level
48 && let Some(mut inline_stab) = inline_stability
49 && let StabilityLevel::Stable {
50 since: inline_since,
51 allowed_through_unstable_modules: _,
52 } = inline_stab.level
53 && let Some(is_global_export) = is_glob_export
54 && !is_global_export
55 {
56 inline_stab.level = StabilityLevel::Stable {
57 since: inline_since,
58 allowed_through_unstable_modules,
59 };
60 Some(inline_stab)
61 } else {
62 item_stability
63 };
64
65 let kind = match &item.kind {
66 ItemKind::StrippedItem(kind) => kind,
67 kind => kind,
68 };
69 match kind {
70 ItemKind::ExternCrateItem { .. }
71 | ItemKind::ImportItem(..)
72 | ItemKind::StructItem(..)
73 | ItemKind::UnionItem(..)
74 | ItemKind::EnumItem(..)
75 | ItemKind::FunctionItem(..)
76 | ItemKind::ModuleItem(..)
77 | ItemKind::TypeAliasItem(..)
78 | ItemKind::StaticItem(..)
79 | ItemKind::TraitItem(..)
80 | ItemKind::TraitAliasItem(..)
81 | ItemKind::StructFieldItem(..)
82 | ItemKind::VariantItem(..)
83 | ItemKind::ForeignFunctionItem(..)
84 | ItemKind::ForeignStaticItem(..)
85 | ItemKind::ForeignTypeItem
86 | ItemKind::MacroItem(..)
87 | ItemKind::ProcMacroItem(..)
88 | ItemKind::ConstantItem(..) => {
89 merge_stability(own_stability, parent_stability)
92 }
93
94 ItemKind::ImplItem(..)
97 | ItemKind::RequiredMethodItem(..)
98 | ItemKind::MethodItem(..)
99 | ItemKind::RequiredAssocConstItem(..)
100 | ItemKind::ProvidedAssocConstItem(..)
101 | ItemKind::ImplAssocConstItem(..)
102 | ItemKind::RequiredAssocTypeItem(..)
103 | ItemKind::AssocTypeItem(..)
104 | ItemKind::PrimitiveItem(..)
105 | ItemKind::KeywordItem
106 | ItemKind::AttributeItem
107 | ItemKind::PlaceholderImplItem => own_stability,
108
109 ItemKind::StrippedItem(..) => unreachable!(),
110 }
111 }
112 ItemId::Auto { .. } | ItemId::Blanket { .. } => {
113 None
115 }
116 };
117
118 item.inner.stability = stability;
119 self.parent_stability = stability;
120 let item = self.fold_item_recur(item);
121 self.parent_stability = parent_stability;
122
123 Some(item)
124 }
125}
126
127fn merge_stability(
128 own_stability: Option<Stability>,
129 parent_stability: Option<Stability>,
130) -> Option<Stability> {
131 if let Some(own_stab) = own_stability
132 && let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: None } =
133 own_stab.level
134 && let Some(parent_stab) = parent_stability
135 && (parent_stab.is_unstable()
136 || parent_stab.stable_since().is_some_and(|parent_since| parent_since > own_since))
137 {
138 parent_stability
139 } else if let Some(mut own_stab) = own_stability
140 && let StabilityLevel::Stable { since, allowed_through_unstable_modules: Some(_) } =
141 own_stab.level
142 && parent_stability.is_some_and(|stab| stab.is_stable())
143 {
144 own_stab.level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
146 Some(own_stab)
147 } else {
148 own_stability
149 }
150}