Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5f510a3

Browse files
aDotInTheVoidfmease
andcommittedMar 12, 2025
rustdoc-json: Clean up & Document id handling.
Co-authored-by: León Orell Valerian Liehr <me@fmease.dev>
1 parent 6e0e4df commit 5f510a3

File tree

1 file changed

+85
-44
lines changed

1 file changed

+85
-44
lines changed
 

‎src/librustdoc/json/ids.rs

+85-44
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,120 @@
1+
//! Id handling for rustdoc-json.
2+
//!
3+
//! Manages the creation of [`rustdoc_json_types::Id`] and the
4+
//! fact that these don't correspond exactly to [`DefId`], because
5+
//! [`rustdoc_json_types::Item`] doesn't correspond exactly to what
6+
//! other phases think of as an "item".
7+
18
use rustc_data_structures::fx::FxHashMap;
29
use rustc_hir::def::DefKind;
310
use rustc_hir::def_id::DefId;
411
use rustc_span::{Symbol, sym};
5-
use rustdoc_json_types::{self as types, Id}; // FIXME: Consistant.
12+
use rustdoc_json_types as types;
613

714
use super::JsonRenderer;
8-
use crate::clean::{self, ItemId};
15+
use crate::clean;
16+
17+
pub(super) type IdInterner = FxHashMap<FullItemId, types::Id>;
918

1019
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
20+
/// An uninterned id.
21+
///
22+
/// Each one corresponds to exactly one of both:
23+
/// 1. [`rustdoc_json_types::Item`].
24+
/// 2. [`rustdoc_json_types::Id`] transitively (as each `Item` has an `Id`).
25+
///
26+
/// It's *broadly* equivalent to a [`DefId`], but needs slightly more information
27+
/// to fully disambiguate items, because sometimes we choose to split a single HIR
28+
/// item into multiple JSON items, or have items with no corresponding HIR item.
1129
pub(super) struct FullItemId {
30+
/// The "main" id of the item.
31+
///
32+
/// In most cases this uniquely identifies the item, the other fields are just
33+
/// used for edge-cases.
1234
def_id: DefId,
35+
36+
/// An extra [`DefId`], which we need for:
37+
///
38+
/// 1. Auto-trait impls synthesized by rustdoc.
39+
/// 2. Blanket impls synthesized by rustdoc.
40+
/// 3. Splitting of reexports of multiple items.
41+
///
42+
/// E.g:
43+
///
44+
/// ```rust
45+
/// mod module {
46+
/// pub struct Foo {} // Exists in type namespace
47+
/// pub fn Foo(){} // Exists in value namespace
48+
/// }
49+
///
50+
/// pub use module::Foo; // Imports both items
51+
/// ```
52+
///
53+
/// In HIR, the `pub use` is just 1 item, but in rustdoc-json it's 2, so
54+
/// we need to disambiguate.
55+
extra_id: Option<DefId>,
56+
57+
/// Needed for `#[rustc_doc_primitive]` modules.
58+
///
59+
/// For these, 1 [`DefId`] is used for both the primitive and the fake-module
60+
/// that holds its docs.
61+
///
62+
/// N.B. This only matters when documenting the standard library with
63+
/// `--document-private-items`. Maybe we should delete that module, and
64+
/// remove this.
1365
name: Option<Symbol>,
14-
/// Used to distinguish imports of different items with the same name
15-
extra: Option<types::Id>,
1666
}
1767

18-
pub(super) type IdInterner = FxHashMap<(FullItemId, Option<FullItemId>), types::Id>;
19-
2068
impl JsonRenderer<'_> {
21-
pub(crate) fn id_from_item_default(&self, item_id: ItemId) -> Id {
69+
pub(crate) fn id_from_item_default(&self, item_id: clean::ItemId) -> types::Id {
2270
self.id_from_item_inner(item_id, None, None)
2371
}
2472

25-
pub(crate) fn id_from_item_inner(
73+
fn id_from_item_inner(
2674
&self,
27-
item_id: ItemId,
75+
item_id: clean::ItemId,
2876
name: Option<Symbol>,
29-
extra: Option<Id>,
30-
) -> Id {
31-
let make_part = |def_id: DefId, name: Option<Symbol>, extra: Option<Id>| {
32-
let name = match name {
33-
Some(name) => Some(name),
34-
None => {
35-
// We need this workaround because primitive types' DefId actually refers to
36-
// their parent module, which isn't present in the output JSON items. So
37-
// instead, we directly get the primitive symbol
38-
if matches!(self.tcx.def_kind(def_id), DefKind::Mod)
39-
&& let Some(prim) = self
40-
.tcx
41-
.get_attrs(def_id, sym::rustc_doc_primitive)
42-
.find_map(|attr| attr.value_str())
43-
{
44-
Some(prim)
45-
} else {
46-
self.tcx.opt_item_name(def_id)
47-
}
48-
}
49-
};
50-
51-
FullItemId { def_id, name, extra }
77+
imported_id: Option<DefId>,
78+
) -> types::Id {
79+
let (def_id, extra_id) = match item_id {
80+
clean::ItemId::DefId(did) => (did, imported_id),
81+
clean::ItemId::Blanket { for_, impl_id } => (for_, Some(impl_id)),
82+
clean::ItemId::Auto { for_, trait_ } => (for_, Some(trait_)),
5283
};
5384

54-
let key = match item_id {
55-
ItemId::DefId(did) => (make_part(did, name, extra), None),
56-
ItemId::Blanket { for_, impl_id } => {
57-
(make_part(impl_id, None, None), Some(make_part(for_, name, extra)))
58-
}
59-
ItemId::Auto { for_, trait_ } => {
60-
(make_part(trait_, None, None), Some(make_part(for_, name, extra)))
85+
let name = match name {
86+
Some(name) => Some(name),
87+
None => {
88+
// We need this workaround because primitive types' DefId actually refers to
89+
// their parent module, which isn't present in the output JSON items. So
90+
// instead, we directly get the primitive symbol
91+
if matches!(self.tcx.def_kind(def_id), DefKind::Mod)
92+
&& let Some(prim) = self
93+
.tcx
94+
.get_attrs(def_id, sym::rustc_doc_primitive)
95+
.find_map(|attr| attr.value_str())
96+
{
97+
Some(prim)
98+
} else {
99+
self.tcx.opt_item_name(def_id)
100+
}
61101
}
62102
};
63103

104+
let key = FullItemId { def_id, extra_id, name };
105+
64106
let mut interner = self.id_interner.borrow_mut();
65107
let len = interner.len();
66108
*interner
67109
.entry(key)
68-
.or_insert_with(|| Id(len.try_into().expect("too many items in a crate")))
110+
.or_insert_with(|| types::Id(len.try_into().expect("too many items in a crate")))
69111
}
70112

71-
pub(crate) fn id_from_item(&self, item: &clean::Item) -> Id {
113+
pub(crate) fn id_from_item(&self, item: &clean::Item) -> types::Id {
72114
match item.kind {
73115
clean::ItemKind::ImportItem(ref import) => {
74-
let extra =
75-
import.source.did.map(ItemId::from).map(|i| self.id_from_item_default(i));
76-
self.id_from_item_inner(item.item_id, item.name, extra)
116+
let imported_id = import.source.did;
117+
self.id_from_item_inner(item.item_id, item.name, imported_id)
77118
}
78119
_ => self.id_from_item_inner(item.item_id, item.name, None),
79120
}

0 commit comments

Comments
 (0)
Failed to load comments.