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