|
| 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 | +
|
1 | 8 | use rustc_data_structures::fx::FxHashMap;
|
2 | 9 | use rustc_hir::def::DefKind;
|
3 | 10 | use rustc_hir::def_id::DefId;
|
4 | 11 | use rustc_span::{Symbol, sym};
|
5 |
| -use rustdoc_json_types::{self as types, Id}; // FIXME: Consistant. |
| 12 | +use rustdoc_json_types as types; |
6 | 13 |
|
7 | 14 | use super::JsonRenderer;
|
8 |
| -use crate::clean::{self, ItemId}; |
| 15 | +use crate::clean; |
| 16 | + |
| 17 | +pub(super) type IdInterner = FxHashMap<FullItemId, types::Id>; |
9 | 18 |
|
10 | 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. |
11 | 29 | 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. |
12 | 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. |
13 | 65 | name: Option<Symbol>,
|
14 |
| - /// Used to distinguish imports of different items with the same name |
15 |
| - extra: Option<types::Id>, |
16 | 66 | }
|
17 | 67 |
|
18 |
| -pub(super) type IdInterner = FxHashMap<(FullItemId, Option<FullItemId>), types::Id>; |
19 |
| - |
20 | 68 | 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 { |
22 | 70 | self.id_from_item_inner(item_id, None, None)
|
23 | 71 | }
|
24 | 72 |
|
25 |
| - pub(crate) fn id_from_item_inner( |
| 73 | + fn id_from_item_inner( |
26 | 74 | &self,
|
27 |
| - item_id: ItemId, |
| 75 | + item_id: clean::ItemId, |
28 | 76 | 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_)), |
52 | 83 | };
|
53 | 84 |
|
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 | + } |
61 | 101 | }
|
62 | 102 | };
|
63 | 103 |
|
| 104 | + let key = FullItemId { def_id, extra_id, name }; |
| 105 | + |
64 | 106 | let mut interner = self.id_interner.borrow_mut();
|
65 | 107 | let len = interner.len();
|
66 | 108 | *interner
|
67 | 109 | .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"))) |
69 | 111 | }
|
70 | 112 |
|
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 { |
72 | 114 | match item.kind {
|
73 | 115 | 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) |
77 | 118 | }
|
78 | 119 | _ => self.id_from_item_inner(item.item_id, item.name, None),
|
79 | 120 | }
|
|
0 commit comments