Skip to content

Commit 42b461e

Browse files
committed
rustdoc: support multiple unstable attributes
1 parent 487f740 commit 42b461e

File tree

11 files changed

+75
-55
lines changed

11 files changed

+75
-55
lines changed

compiler/rustc_attr/src/builtin.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ impl Stability {
8989
pub fn unstable_features(&self) -> impl Iterator<Item = Symbol> + use<'_> {
9090
self.level.unstable_features()
9191
}
92+
93+
pub fn is_rustc_private(&self) -> bool {
94+
self.level.has_unstable_feature(sym::rustc_private)
95+
}
9296
}
9397

9498
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.

src/librustdoc/clean/inline.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -438,10 +438,8 @@ pub(crate) fn build_impl(
438438
let associated_trait = tcx.impl_trait_ref(did).map(ty::EarlyBinder::skip_binder);
439439

440440
// Do not inline compiler-internal items unless we're a compiler-internal crate.
441-
let is_compiler_internal = |did| {
442-
tcx.lookup_stability(did)
443-
.is_some_and(|stab| stab.is_unstable() && stab.feature == sym::rustc_private)
444-
};
441+
let is_compiler_internal =
442+
|did| tcx.lookup_stability(did).is_some_and(|stab| stab.is_rustc_private());
445443
let document_compiler_internal = is_compiler_internal(LOCAL_CRATE.as_def_id());
446444
let is_directly_public = |cx: &mut DocContext<'_>, did| {
447445
cx.cache.effective_visibilities.is_directly_public(tcx, did)

src/librustdoc/clean/types.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use arrayvec::ArrayVec;
88
use rustc_ast::MetaItemInner;
99
use rustc_ast_pretty::pprust;
1010
use rustc_attr::{ConstStability, Deprecation, Stability, StableSince};
11-
use rustc_const_eval::const_eval::is_unstable_const_fn;
1211
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
1312
use rustc_hir::def::{CtorKind, DefKind, Res};
1413
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@@ -387,8 +386,8 @@ impl Item {
387386
/// Returns the effective stability of the item.
388387
///
389388
/// This method should only be called after the `propagate-stability` pass has been run.
390-
pub(crate) fn stability(&self, tcx: TyCtxt<'_>) -> Option<Stability> {
391-
let stability = self.inner.stability;
389+
pub(crate) fn stability(&self, tcx: TyCtxt<'_>) -> Option<&Stability> {
390+
let stability = self.inner.stability.as_ref();
392391
debug_assert!(
393392
stability.is_some()
394393
|| self.def_id().is_none_or(|did| tcx.lookup_stability(did).is_none()),
@@ -397,7 +396,7 @@ impl Item {
397396
stability
398397
}
399398

400-
pub(crate) fn const_stability(&self, tcx: TyCtxt<'_>) -> Option<ConstStability> {
399+
pub(crate) fn const_stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ConstStability> {
401400
self.def_id().and_then(|did| tcx.lookup_const_stability(did))
402401
}
403402

@@ -641,12 +640,11 @@ impl Item {
641640
asyncness: ty::Asyncness,
642641
) -> hir::FnHeader {
643642
let sig = tcx.fn_sig(def_id).skip_binder();
644-
let constness =
645-
if tcx.is_const_fn(def_id) || is_unstable_const_fn(tcx, def_id).is_some() {
646-
hir::Constness::Const
647-
} else {
648-
hir::Constness::NotConst
649-
};
643+
let constness = if tcx.is_const_fn(def_id) || tcx.is_unstable_const_fn(def_id) {
644+
hir::Constness::Const
645+
} else {
646+
hir::Constness::NotConst
647+
};
650648
let asyncness = match asyncness {
651649
ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP),
652650
ty::Asyncness::No => hir::IsAsync::NotAsync,
@@ -664,9 +662,7 @@ impl Item {
664662
safety
665663
},
666664
abi,
667-
constness: if tcx.is_const_fn(def_id)
668-
|| is_unstable_const_fn(tcx, def_id).is_some()
669-
{
665+
constness: if tcx.is_const_fn(def_id) || tcx.is_unstable_const_fn(def_id) {
670666
hir::Constness::Const
671667
} else {
672668
hir::Constness::NotConst

src/librustdoc/html/format.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1675,7 +1675,7 @@ impl PrintWithSpace for hir::Mutability {
16751675
pub(crate) fn print_constness_with_space(
16761676
c: &hir::Constness,
16771677
overall_stab: Option<StableSince>,
1678-
const_stab: Option<ConstStability>,
1678+
const_stab: Option<&ConstStability>,
16791679
) -> &'static str {
16801680
match c {
16811681
hir::Constness::Const => match (overall_stab, const_stab) {

src/librustdoc/html/render/mod.rs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use rustc_hir::def_id::{DefId, DefIdSet};
5353
use rustc_middle::ty::print::PrintTraitRefExt;
5454
use rustc_middle::ty::{self, TyCtxt};
5555
use rustc_session::RustcVersion;
56-
use rustc_span::symbol::{Symbol, sym};
56+
use rustc_span::symbol::Symbol;
5757
use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
5858
use serde::ser::SerializeMap;
5959
use serde::{Serialize, Serializer};
@@ -674,17 +674,23 @@ enum ShortItemInfo {
674674
Deprecation {
675675
message: String,
676676
},
677-
/// The feature corresponding to an unstable item, and optionally
678-
/// a tracking issue URL and number.
677+
/// The features corresponding to an unstable item, and optionally
678+
/// a tracking issue URL and number for each.
679679
Unstable {
680-
feature: String,
681-
tracking: Option<(String, u32)>,
680+
features: Vec<UnstableFeature>,
682681
},
683682
Portability {
684683
message: String,
685684
},
686685
}
687686

687+
#[derive(Template)]
688+
#[template(path = "unstable_feature.html")]
689+
struct UnstableFeature {
690+
feature: String,
691+
tracking: Option<(String, u32)>,
692+
}
693+
688694
/// Render the stability, deprecation and portability information that is displayed at the top of
689695
/// the item's documentation.
690696
fn short_item_info(
@@ -723,19 +729,24 @@ fn short_item_info(
723729

724730
// Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
725731
// Those crates are permanently unstable so it makes no sense to render "unstable" everywhere.
726-
if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item
732+
if let Some(StabilityLevel::Unstable { unstables, .. }) = item
727733
.stability(cx.tcx())
728734
.as_ref()
729-
.filter(|stab| stab.feature != sym::rustc_private)
730-
.map(|stab| (stab.level, stab.feature))
735+
.filter(|stab| !stab.is_rustc_private())
736+
.map(|stab| &stab.level)
731737
{
732-
let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
733-
{
734-
Some((url.clone(), issue.get()))
735-
} else {
736-
None
738+
let track = |issue: Option<std::num::NonZero<u32>>| {
739+
if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
740+
Some((url.clone(), issue.get()))
741+
} else {
742+
None
743+
}
737744
};
738-
extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
745+
let features = unstables
746+
.iter()
747+
.map(|u| UnstableFeature { feature: u.feature.to_string(), tracking: track(u.issue) })
748+
.collect();
749+
extra_info.push(ShortItemInfo::Unstable { features });
739750
}
740751

741752
if let Some(message) = portability(item, parent) {
@@ -989,7 +1000,7 @@ fn assoc_method(
9891000
fn render_stability_since_raw_with_extra(
9901001
w: &mut Buffer,
9911002
stable_version: Option<StableSince>,
992-
const_stability: Option<ConstStability>,
1003+
const_stability: Option<&ConstStability>,
9931004
extra_class: &str,
9941005
) -> bool {
9951006
let mut title = String::new();
@@ -1005,12 +1016,16 @@ fn render_stability_since_raw_with_extra(
10051016
since_to_string(&since)
10061017
.map(|since| (format!("const since {since}"), format!("const: {since}")))
10071018
}
1008-
Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
1019+
Some(ConstStability { level: StabilityLevel::Unstable { unstables, .. }, .. }) => {
10091020
if stable_version.is_none() {
10101021
// don't display const unstable if entirely unstable
10111022
None
10121023
} else {
1013-
let unstable = if let Some(n) = issue {
1024+
// if constness depends on multiple unstable features, only link to the first
1025+
// tracking issue found, to save space. the issue description should link to issues
1026+
// for any features it can intersect with
1027+
let feature_issue = unstables.iter().find_map(|u| u.issue.map(|n| (u.feature, n)));
1028+
let unstable = if let Some((feature, n)) = feature_issue {
10141029
format!(
10151030
"<a \
10161031
href=\"https://github.com/rust-lang/rust/issues/{n}\" \
@@ -1060,7 +1075,7 @@ fn since_to_string(since: &StableSince) -> Option<String> {
10601075
fn render_stability_since_raw(
10611076
w: &mut Buffer,
10621077
ver: Option<StableSince>,
1063-
const_stability: Option<ConstStability>,
1078+
const_stability: Option<&ConstStability>,
10641079
) -> bool {
10651080
render_stability_since_raw_with_extra(w, ver, const_stability, "")
10661081
}

src/librustdoc/html/render/print_item.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_hir::def_id::DefId;
1313
use rustc_index::IndexVec;
1414
use rustc_middle::ty::{self, TyCtxt};
1515
use rustc_span::hygiene::MacroKind;
16-
use rustc_span::symbol::{Symbol, kw, sym};
16+
use rustc_span::symbol::{Symbol, kw};
1717
use rustc_target::abi::VariantIdx;
1818
use tracing::{debug, info};
1919

@@ -568,7 +568,7 @@ fn extra_info_tags<'a, 'tcx: 'a>(
568568
// to render "unstable" everywhere.
569569
let stability = import_def_id
570570
.map_or_else(|| item.stability(tcx), |import_did| tcx.lookup_stability(import_did));
571-
if stability.is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private) {
571+
if stability.is_some_and(|s| s.is_unstable() && !s.is_rustc_private()) {
572572
write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
573573
}
574574

src/librustdoc/html/templates/short_item_info.html

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,11 @@
44
<span class="emoji">👎</span> {# #}
55
<span>{{message|safe}}</span> {# #}
66
</div>
7-
{% when Self::Unstable with { feature, tracking } %}
7+
{% when Self::Unstable with { features } %}
88
<div class="stab unstable"> {# #}
99
<span class="emoji">🔬</span> {# #}
1010
<span> {# #}
11-
This is a nightly-only experimental API. ({# #}
12-
<code>{{feature}}</code>
13-
{% match tracking %}
14-
{% when Some with ((url, num)) %}
15-
&nbsp;<a href="{{url}}{{num}}">#{{num}}</a>
16-
{% when None %}
17-
{% endmatch %}
18-
) {# #}
11+
This is a nightly-only experimental API. ({{features|join(", ")|safe}}) {# #}
1912
</span> {# #}
2013
</div>
2114
{% when Self::Portability with { message } %}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<code>{{feature}}</code>
2+
{% match tracking %}
3+
{% when Some with ((url, num)) %}
4+
&nbsp;<a href="{{url}}{{num}}">#{{num}}</a>
5+
{% when None %}
6+
{% endmatch %}

src/librustdoc/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ extern crate rustc_abi;
3737
extern crate rustc_ast;
3838
extern crate rustc_ast_pretty;
3939
extern crate rustc_attr;
40-
extern crate rustc_const_eval;
4140
extern crate rustc_data_structures;
4241
extern crate rustc_driver;
4342
extern crate rustc_errors;

src/librustdoc/passes/propagate_stability.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub(crate) const PROPAGATE_STABILITY: Pass = Pass {
2222

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

2828
struct StabilityPropagator<'a, 'tcx> {
@@ -32,28 +32,29 @@ struct StabilityPropagator<'a, 'tcx> {
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;
35+
let parent_stability = self.parent_stability.clone();
3636

3737
let stability = match item.item_id {
3838
ItemId::DefId(def_id) => {
3939
let own_stability = self.cx.tcx.lookup_stability(def_id);
4040

4141
// If any of the item's parents was stabilized later or is still unstable,
4242
// then use the parent's stability instead.
43-
if let Some(own_stab) = own_stability
43+
if let Some(ref own_stab) = own_stability
4444
&& let StabilityLevel::Stable {
4545
since: own_since,
4646
allowed_through_unstable_modules: false,
47+
..
4748
} = own_stab.level
48-
&& let Some(parent_stab) = parent_stability
49+
&& let Some(ref parent_stab) = parent_stability
4950
&& (parent_stab.is_unstable()
5051
|| parent_stab
5152
.stable_since()
5253
.is_some_and(|parent_since| parent_since > own_since))
5354
{
54-
parent_stability
55+
parent_stability.clone()
5556
} else {
56-
own_stability
57+
own_stability.cloned()
5758
}
5859
}
5960
ItemId::Auto { .. } | ItemId::Blanket { .. } => {
@@ -62,7 +63,7 @@ impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> {
6263
}
6364
};
6465

65-
item.inner.stability = stability;
66+
item.inner.stability = stability.clone();
6667
self.parent_stability = stability;
6768
let item = self.fold_item_recur(item);
6869
self.parent_stability = parent_stability;

tests/rustdoc/stability.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,11 @@ pub mod stable_earlier {
8383
#[stable(feature = "rust1", since = "1.0.0")]
8484
pub use crate::stable_later::stable_in_later;
8585
}
86+
87+
#[unstable(feature = "test", issue = "none")]
88+
#[unstable(feature = "test2", issue = "none")]
89+
pub trait UnstableTrait {
90+
//@ has stability/trait.UnstableTrait.html \
91+
// '//span[@class="item-info"]//div[@class="stab unstable"]' \
92+
// 'This is a nightly-only experimental API. (test, test2)'
93+
}

0 commit comments

Comments
 (0)