rustdoc/json/
ids.rs

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
8use rustc_data_structures::fx::FxHashMap;
9use rustc_hir::def::DefKind;
10use rustc_hir::def_id::DefId;
11use rustc_span::{Symbol, sym};
12use rustdoc_json_types as types;
13
14use super::JsonRenderer;
15use crate::clean;
16
17pub(super) type IdInterner = FxHashMap<FullItemId, types::Id>;
18
19#[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.
29pub(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.
34    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.
65    name: Option<Symbol>,
66}
67
68impl JsonRenderer<'_> {
69    pub(crate) fn id_from_item_default(&self, item_id: clean::ItemId) -> types::Id {
70        self.id_from_item_inner(item_id, None, None)
71    }
72
73    fn id_from_item_inner(
74        &self,
75        item_id: clean::ItemId,
76        name: Option<Symbol>,
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_)),
83        };
84
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                }
101            }
102        };
103
104        let key = FullItemId { def_id, extra_id, name };
105
106        let mut interner = self.id_interner.borrow_mut();
107        let len = interner.len();
108        *interner
109            .entry(key)
110            .or_insert_with(|| types::Id(len.try_into().expect("too many items in a crate")))
111    }
112
113    pub(crate) fn id_from_item(&self, item: &clean::Item) -> types::Id {
114        match item.kind {
115            clean::ItemKind::ImportItem(ref import) => {
116                let imported_id = import.source.did;
117                self.id_from_item_inner(item.item_id, item.name, imported_id)
118            }
119            _ => self.id_from_item_inner(item.item_id, item.name, None),
120        }
121    }
122}