Skip to content

Commit d3bc6d8

Browse files
committed
rustdoc: report all unstable features needed for items
1 parent 2385b33 commit d3bc6d8

File tree

2 files changed

+47
-20
lines changed

2 files changed

+47
-20
lines changed

src/librustdoc/passes/propagate_stability.rs

+41-20
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,17 @@ pub(crate) const PROPAGATE_STABILITY: Pass = Pass {
2121
};
2222

2323
pub(crate) fn propagate_stability(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
24-
let crate_stability = cx.tcx.lookup_stability(CRATE_DEF_ID);
24+
let crate_stability = cx.tcx.lookup_stability(CRATE_DEF_ID).cloned();
2525
StabilityPropagator { parent_stability: crate_stability, cx }.fold_crate(cr)
2626
}
2727

2828
struct StabilityPropagator<'a, 'tcx> {
29-
parent_stability: Option<&'tcx Stability>,
29+
parent_stability: Option<Stability>,
3030
cx: &'a mut DocContext<'tcx>,
3131
}
3232

3333
impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> {
3434
fn fold_item(&mut self, mut item: Item) -> Option<Item> {
35-
let parent_stability = self.parent_stability;
36-
3735
let stability = match item.item_id {
3836
ItemId::DefId(def_id) => {
3937
let own_stability = self.cx.tcx.lookup_stability(def_id);
@@ -59,9 +57,7 @@ impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> {
5957
| ItemKind::MacroItem(..)
6058
| ItemKind::ProcMacroItem(..)
6159
| ItemKind::ConstantItem(..) => {
62-
// If any of the item's parents was stabilized later or is still unstable,
63-
// then use the parent's stability instead.
64-
merge_stability(own_stability, parent_stability)
60+
merge_stability(own_stability, self.parent_stability.as_ref())
6561
}
6662

6763
// Don't inherit the parent's stability for these items, because they
@@ -74,7 +70,7 @@ impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> {
7470
| ItemKind::TyAssocTypeItem(..)
7571
| ItemKind::AssocTypeItem(..)
7672
| ItemKind::PrimitiveItem(..)
77-
| ItemKind::KeywordItem => own_stability,
73+
| ItemKind::KeywordItem => own_stability.cloned(),
7874

7975
ItemKind::StrippedItem(..) => unreachable!(),
8076
}
@@ -85,28 +81,53 @@ impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> {
8581
}
8682
};
8783

88-
item.inner.stability = stability.cloned();
89-
self.parent_stability = stability;
84+
item.inner.stability = stability.clone();
85+
let parent_stability = std::mem::replace(&mut self.parent_stability, stability);
9086
let item = self.fold_item_recur(item);
9187
self.parent_stability = parent_stability;
9288

9389
Some(item)
9490
}
9591
}
9692

97-
fn merge_stability<'tcx>(
98-
own_stability: Option<&'tcx Stability>,
99-
parent_stability: Option<&'tcx Stability>,
100-
) -> Option<&'tcx Stability> {
93+
fn merge_stability(
94+
own_stability: Option<&Stability>,
95+
parent_stability: Option<&Stability>,
96+
) -> Option<Stability> {
10197
if let Some(own_stab) = own_stability
102-
&& let &StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: false } =
103-
&own_stab.level
10498
&& let Some(parent_stab) = parent_stability
105-
&& (parent_stab.is_unstable()
106-
|| parent_stab.stable_since().is_some_and(|parent_since| parent_since > own_since))
10799
{
108-
parent_stability
100+
match own_stab.level {
101+
// If any of a stable item's parents were stabilized later or are still unstable,
102+
// then use the parent's stability instead.
103+
StabilityLevel::Stable {
104+
since: own_since,
105+
allowed_through_unstable_modules: false,
106+
..
107+
} if parent_stab.is_unstable()
108+
|| parent_stab
109+
.stable_since()
110+
.is_some_and(|parent_since| parent_since > own_since) =>
111+
{
112+
parent_stability.cloned()
113+
}
114+
115+
// If any of an unstable item's parents depend on other unstable features,
116+
// then use those as well.
117+
StabilityLevel::Unstable { unstables: ref own_gates, reason, is_soft }
118+
if let StabilityLevel::Unstable { unstables: parent_gates, .. } =
119+
&parent_stab.level =>
120+
{
121+
let missing_unstables = parent_gates
122+
.iter()
123+
.filter(|p| !own_gates.iter().any(|u| u.feature == p.feature));
124+
let unstables = own_gates.iter().chain(missing_unstables).cloned().collect();
125+
Some(Stability { level: StabilityLevel::Unstable { unstables, reason, is_soft } })
126+
}
127+
128+
_ => own_stability.cloned(),
129+
}
109130
} else {
110-
own_stability
131+
own_stability.cloned()
111132
}
112133
}

tests/rustdoc/stability.rs

+6
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ pub mod unstable {
5555
#[stable(feature = "rust1", since = "1.0.0")]
5656
pub fn foo() {}
5757
}
58+
59+
//@ has stability/unstable/fn.nested_unstable.html \
60+
// '//span[@class="item-info"]//div[@class="stab unstable"]' \
61+
// 'This is a nightly-only experimental API. (test, unstable)'
62+
#[unstable(feature = "test", issue = "none")]
63+
pub fn nested_unstable() {}
5864
}
5965

6066
#[unstable(feature = "unstable", issue = "none")]

0 commit comments

Comments
 (0)