From 7dd5ad282c4aab165236c8ba9404929d7dd337ac Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 1 Aug 2024 22:54:22 -0700 Subject: [PATCH 1/7] rustdoc: Extract helper function to add item to search index --- src/librustdoc/formats/cache.rs | 272 ++++++++++++++++---------------- 1 file changed, 135 insertions(+), 137 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 9f284486616..8c0ed6cddf0 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -260,7 +260,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } // Index this method for searching later on. - if let Some(s) = item.name.or_else(|| { + if let Some(name) = item.name.or_else(|| { if item.is_stripped() { None } else if let clean::ImportItem(ref i) = *item.kind @@ -271,142 +271,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { None } }) { - let (parent, is_inherent_impl_item) = match *item.kind { - clean::StrippedItem(..) => ((None, None), false), - clean::AssocConstItem(..) | clean::AssocTypeItem(..) - if self - .cache - .parent_stack - .last() - .is_some_and(|parent| parent.is_trait_impl()) => - { - // skip associated items in trait impls - ((None, None), false) - } - clean::TyMethodItem(..) - | clean::TyAssocConstItem(..) - | clean::TyAssocTypeItem(..) - | clean::StructFieldItem(..) - | clean::VariantItem(..) => ( - ( - Some( - self.cache - .parent_stack - .last() - .expect("parent_stack is empty") - .item_id() - .expect_def_id(), - ), - Some(&self.cache.stack[..self.cache.stack.len() - 1]), - ), - false, - ), - clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { - if self.cache.parent_stack.is_empty() { - ((None, None), false) - } else { - let last = self.cache.parent_stack.last().expect("parent_stack is empty 2"); - let did = match &*last { - ParentStackItem::Impl { - // impl Trait for &T { fn method(self); } - // - // When generating a function index with the above shape, we want it - // associated with `T`, not with the primitive reference type. It should - // show up as `T::method`, rather than `reference::method`, in the search - // results page. - for_: clean::Type::BorrowedRef { type_, .. }, - .. - } => type_.def_id(&self.cache), - ParentStackItem::Impl { for_, .. } => for_.def_id(&self.cache), - ParentStackItem::Type(item_id) => item_id.as_def_id(), - }; - let path = did - .and_then(|did| self.cache.paths.get(&did)) - // The current stack not necessarily has correlation - // for where the type was defined. On the other - // hand, `paths` always has the right - // information if present. - .map(|(fqp, _)| &fqp[..fqp.len() - 1]); - ((did, path), true) - } - } - _ => ((None, Some(&*self.cache.stack)), false), - }; - - match parent { - (parent, Some(path)) if is_inherent_impl_item || !self.cache.stripped_mod => { - debug_assert!(!item.is_stripped()); - - // A crate has a module at its root, containing all items, - // which should not be indexed. The crate-item itself is - // inserted later on when serializing the search-index. - if item.item_id.as_def_id().is_some_and(|idx| !idx.is_crate_root()) - && let ty = item.type_() - && (ty != ItemType::StructField - || u16::from_str_radix(s.as_str(), 10).is_err()) - { - let desc = - short_markdown_summary(&item.doc_value(), &item.link_names(self.cache)); - // For searching purposes, a re-export is a duplicate if: - // - // - It's either an inline, or a true re-export - // - It's got the same name - // - Both of them have the same exact path - let defid = (match &*item.kind { - &clean::ItemKind::ImportItem(ref import) => import.source.did, - _ => None, - }) - .or_else(|| item.item_id.as_def_id()); - // In case this is a field from a tuple struct, we don't add it into - // the search index because its name is something like "0", which is - // not useful for rustdoc search. - self.cache.search_index.push(IndexItem { - ty, - defid, - name: s, - path: join_with_double_colon(path), - desc, - parent, - parent_idx: None, - exact_path: None, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = - self.cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }, - search_type: get_function_type_for_search( - &item, - self.tcx, - clean_impl_generics(self.cache.parent_stack.last()).as_ref(), - parent, - self.cache, - ), - aliases: item.attrs.get_doc_aliases(), - deprecation: item.deprecation(self.tcx), - }); - } - } - (Some(parent), None) if is_inherent_impl_item => { - // We have a parent, but we don't know where they're - // defined yet. Wait for later to index this item. - let impl_generics = clean_impl_generics(self.cache.parent_stack.last()); - self.cache.orphan_impl_items.push(OrphanImplItem { - parent, - item: item.clone(), - impl_generics, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = - self.cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }, - }); - } - _ => {} - } + add_item_to_search_index(self.tcx, &mut self.cache, &item, name) } // Keep track of the fully qualified path for this item. @@ -573,6 +438,139 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } } +fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::Item, name: Symbol) { + let (parent, is_inherent_impl_item) = match *item.kind { + clean::StrippedItem(..) => ((None, None), false), + clean::AssocConstItem(..) | clean::AssocTypeItem(..) + if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) => + { + // skip associated items in trait impls + ((None, None), false) + } + clean::TyMethodItem(..) + | clean::TyAssocConstItem(..) + | clean::TyAssocTypeItem(..) + | clean::StructFieldItem(..) + | clean::VariantItem(..) => ( + ( + Some( + cache + .parent_stack + .last() + .expect("parent_stack is empty") + .item_id() + .expect_def_id(), + ), + Some(&cache.stack[..cache.stack.len() - 1]), + ), + false, + ), + clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { + if cache.parent_stack.is_empty() { + ((None, None), false) + } else { + let last = cache.parent_stack.last().expect("parent_stack is empty 2"); + let did = match &*last { + ParentStackItem::Impl { + // impl Trait for &T { fn method(self); } + // + // When generating a function index with the above shape, we want it + // associated with `T`, not with the primitive reference type. It should + // show up as `T::method`, rather than `reference::method`, in the search + // results page. + for_: clean::Type::BorrowedRef { type_, .. }, + .. + } => type_.def_id(&cache), + ParentStackItem::Impl { for_, .. } => for_.def_id(&cache), + ParentStackItem::Type(item_id) => item_id.as_def_id(), + }; + let path = did + .and_then(|did| cache.paths.get(&did)) + // The current stack not necessarily has correlation + // for where the type was defined. On the other + // hand, `paths` always has the right + // information if present. + .map(|(fqp, _)| &fqp[..fqp.len() - 1]); + ((did, path), true) + } + } + _ => ((None, Some(&*cache.stack)), false), + }; + + match parent { + (parent, Some(path)) if is_inherent_impl_item || !cache.stripped_mod => { + debug_assert!(!item.is_stripped()); + + // A crate has a module at its root, containing all items, + // which should not be indexed. The crate-item itself is + // inserted later on when serializing the search-index. + if item.item_id.as_def_id().is_some_and(|idx| !idx.is_crate_root()) + && let ty = item.type_() + && (ty != ItemType::StructField || u16::from_str_radix(name.as_str(), 10).is_err()) + { + let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); + // For searching purposes, a re-export is a duplicate if: + // + // - It's either an inline, or a true re-export + // - It's got the same name + // - Both of them have the same exact path + let defid = (match &*item.kind { + &clean::ItemKind::ImportItem(ref import) => import.source.did, + _ => None, + }) + .or_else(|| item.item_id.as_def_id()); + // In case this is a field from a tuple struct, we don't add it into + // the search index because its name is something like "0", which is + // not useful for rustdoc search. + cache.search_index.push(IndexItem { + ty, + defid, + name, + path: join_with_double_colon(path), + desc, + parent, + parent_idx: None, + exact_path: None, + impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = + cache.parent_stack.last() + { + item_id.as_def_id() + } else { + None + }, + search_type: get_function_type_for_search( + &item, + tcx, + clean_impl_generics(cache.parent_stack.last()).as_ref(), + parent, + cache, + ), + aliases: item.attrs.get_doc_aliases(), + deprecation: item.deprecation(tcx), + }); + } + } + (Some(parent), None) if is_inherent_impl_item => { + // We have a parent, but we don't know where they're + // defined yet. Wait for later to index this item. + let impl_generics = clean_impl_generics(cache.parent_stack.last()); + cache.orphan_impl_items.push(OrphanImplItem { + parent, + item: item.clone(), + impl_generics, + impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = + cache.parent_stack.last() + { + item_id.as_def_id() + } else { + None + }, + }); + } + _ => {} + } +} + pub(crate) struct OrphanImplItem { pub(crate) parent: DefId, pub(crate) impl_id: Option, From 2721e97c5d9a4e5a001461e499a751e81e10df4c Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 1 Aug 2024 23:20:20 -0700 Subject: [PATCH 2/7] rustdoc: Clarify construction of name for search index --- src/librustdoc/formats/cache.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 8c0ed6cddf0..13b91f483eb 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -260,17 +260,20 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } // Index this method for searching later on. - if let Some(name) = item.name.or_else(|| { - if item.is_stripped() { - None - } else if let clean::ImportItem(ref i) = *item.kind - && let clean::ImportKind::Simple(s) = i.kind - { - Some(s) - } else { - None - } - }) { + let search_name = if !item.is_stripped() { + item.name.or_else(|| { + if let clean::ImportItem(ref i) = *item.kind + && let clean::ImportKind::Simple(s) = i.kind + { + Some(s) + } else { + None + } + }) + } else { + None + }; + if let Some(name) = search_name { add_item_to_search_index(self.tcx, &mut self.cache, &item, name) } From 4e2084769b8e6de0dd43453315b7466305e9d312 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 1 Aug 2024 23:33:42 -0700 Subject: [PATCH 3/7] rustdoc: Simplify some search index code --- src/librustdoc/clean/types.rs | 20 +++++------ src/librustdoc/formats/cache.rs | 60 +++++++++++++++------------------ 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1ec5f38b6ec..cf01ffb2e75 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1680,13 +1680,16 @@ impl Type { } } - fn inner_def_id(&self, cache: Option<&Cache>) -> Option { + /// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s. + /// + /// [clean]: crate::clean + pub(crate) fn def_id(&self, cache: &Cache) -> Option { let t: PrimitiveType = match *self { Type::Path { ref path } => return Some(path.def_id()), DynTrait(ref bounds, _) => return bounds.get(0).map(|b| b.trait_.def_id()), - Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), + Primitive(p) => return cache.primitive_locations.get(&p).cloned(), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, - BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache), + BorrowedRef { ref type_, .. } => return type_.def_id(cache), Tuple(ref tys) => { if tys.is_empty() { PrimitiveType::Unit @@ -1699,17 +1702,10 @@ impl Type { Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, - QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache), + QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache), Generic(_) | Infer | ImplTrait(_) => return None, }; - cache.and_then(|c| Primitive(t).def_id(c)) - } - - /// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s. - /// - /// [clean]: crate::clean - pub(crate) fn def_id(&self, cache: &Cache) -> Option { - self.inner_def_id(Some(cache)) + Primitive(t).def_id(cache) } } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 13b91f483eb..0788543d89a 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -442,13 +442,13 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::Item, name: Symbol) { - let (parent, is_inherent_impl_item) = match *item.kind { - clean::StrippedItem(..) => ((None, None), false), + let (parent, is_impl_child) = match *item.kind { + clean::StrippedItem(..) => return, clean::AssocConstItem(..) | clean::AssocTypeItem(..) if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) => { // skip associated items in trait impls - ((None, None), false) + return; } clean::TyMethodItem(..) | clean::TyAssocConstItem(..) @@ -469,39 +469,35 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It false, ), clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { - if cache.parent_stack.is_empty() { - ((None, None), false) - } else { - let last = cache.parent_stack.last().expect("parent_stack is empty 2"); - let did = match &*last { - ParentStackItem::Impl { - // impl Trait for &T { fn method(self); } - // - // When generating a function index with the above shape, we want it - // associated with `T`, not with the primitive reference type. It should - // show up as `T::method`, rather than `reference::method`, in the search - // results page. - for_: clean::Type::BorrowedRef { type_, .. }, - .. - } => type_.def_id(&cache), - ParentStackItem::Impl { for_, .. } => for_.def_id(&cache), - ParentStackItem::Type(item_id) => item_id.as_def_id(), - }; - let path = did - .and_then(|did| cache.paths.get(&did)) - // The current stack not necessarily has correlation - // for where the type was defined. On the other - // hand, `paths` always has the right - // information if present. - .map(|(fqp, _)| &fqp[..fqp.len() - 1]); - ((did, path), true) - } + let last = cache.parent_stack.last().expect("parent_stack is empty 2"); + let did = match &*last { + ParentStackItem::Impl { + // impl Trait for &T { fn method(self); } + // + // When generating a function index with the above shape, we want it + // associated with `T`, not with the primitive reference type. It should + // show up as `T::method`, rather than `reference::method`, in the search + // results page. + for_: clean::Type::BorrowedRef { type_, .. }, + .. + } => type_.def_id(&cache), + ParentStackItem::Impl { for_, .. } => for_.def_id(&cache), + ParentStackItem::Type(item_id) => item_id.as_def_id(), + }; + let path = did + .and_then(|did| cache.paths.get(&did)) + // The current stack not necessarily has correlation + // for where the type was defined. On the other + // hand, `paths` always has the right + // information if present. + .map(|(fqp, _)| &fqp[..fqp.len() - 1]); + ((did, path), true) } _ => ((None, Some(&*cache.stack)), false), }; match parent { - (parent, Some(path)) if is_inherent_impl_item || !cache.stripped_mod => { + (parent, Some(path)) if is_impl_child || !cache.stripped_mod => { debug_assert!(!item.is_stripped()); // A crate has a module at its root, containing all items, @@ -553,7 +549,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It }); } } - (Some(parent), None) if is_inherent_impl_item => { + (Some(parent), None) if is_impl_child => { // We have a parent, but we don't know where they're // defined yet. Wait for later to index this item. let impl_generics = clean_impl_generics(cache.parent_stack.last()); From 015aa8d0fba2247da33967a93fad9a2bea16037f Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 2 Aug 2024 12:58:59 -0700 Subject: [PATCH 4/7] Restructure a confusing `match` --- src/librustdoc/formats/cache.rs | 124 ++++++++++++++++---------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 0788543d89a..5c18cebb965 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -442,7 +442,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::Item, name: Symbol) { - let (parent, is_impl_child) = match *item.kind { + let ((parent_did, parent_path), is_impl_child) = match *item.kind { clean::StrippedItem(..) => return, clean::AssocConstItem(..) | clean::AssocTypeItem(..) if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) => @@ -496,67 +496,59 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It _ => ((None, Some(&*cache.stack)), false), }; - match parent { - (parent, Some(path)) if is_impl_child || !cache.stripped_mod => { - debug_assert!(!item.is_stripped()); - - // A crate has a module at its root, containing all items, - // which should not be indexed. The crate-item itself is - // inserted later on when serializing the search-index. - if item.item_id.as_def_id().is_some_and(|idx| !idx.is_crate_root()) - && let ty = item.type_() - && (ty != ItemType::StructField || u16::from_str_radix(name.as_str(), 10).is_err()) + if let Some(parent_did) = parent_did + && parent_path.is_none() + && is_impl_child + { + // We have a parent, but we don't know where they're + // defined yet. Wait for later to index this item. + let impl_generics = clean_impl_generics(cache.parent_stack.last()); + cache.orphan_impl_items.push(OrphanImplItem { + parent: parent_did, + item: item.clone(), + impl_generics, + impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { - let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); - // For searching purposes, a re-export is a duplicate if: - // - // - It's either an inline, or a true re-export - // - It's got the same name - // - Both of them have the same exact path - let defid = (match &*item.kind { - &clean::ItemKind::ImportItem(ref import) => import.source.did, - _ => None, - }) - .or_else(|| item.item_id.as_def_id()); - // In case this is a field from a tuple struct, we don't add it into - // the search index because its name is something like "0", which is - // not useful for rustdoc search. - cache.search_index.push(IndexItem { - ty, - defid, - name, - path: join_with_double_colon(path), - desc, - parent, - parent_idx: None, - exact_path: None, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = - cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }, - search_type: get_function_type_for_search( - &item, - tcx, - clean_impl_generics(cache.parent_stack.last()).as_ref(), - parent, - cache, - ), - aliases: item.attrs.get_doc_aliases(), - deprecation: item.deprecation(tcx), - }); - } - } - (Some(parent), None) if is_impl_child => { - // We have a parent, but we don't know where they're - // defined yet. Wait for later to index this item. - let impl_generics = clean_impl_generics(cache.parent_stack.last()); - cache.orphan_impl_items.push(OrphanImplItem { - parent, - item: item.clone(), - impl_generics, + item_id.as_def_id() + } else { + None + }, + }); + } else if let Some(path) = parent_path + && (is_impl_child || !cache.stripped_mod) + { + debug_assert!(!item.is_stripped()); + + // A crate has a module at its root, containing all items, + // which should not be indexed. The crate-item itself is + // inserted later on when serializing the search-index. + if item.item_id.as_def_id().is_some_and(|idx| !idx.is_crate_root()) + && let ty = item.type_() + && (ty != ItemType::StructField || u16::from_str_radix(name.as_str(), 10).is_err()) + { + let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); + // For searching purposes, a re-export is a duplicate if: + // + // - It's either an inline, or a true re-export + // - It's got the same name + // - Both of them have the same exact path + let defid = (match &*item.kind { + &clean::ItemKind::ImportItem(ref import) => import.source.did, + _ => None, + }) + .or_else(|| item.item_id.as_def_id()); + // In case this is a field from a tuple struct, we don't add it into + // the search index because its name is something like "0", which is + // not useful for rustdoc search. + cache.search_index.push(IndexItem { + ty, + defid, + name, + path: join_with_double_colon(path), + desc, + parent: parent_did, + parent_idx: None, + exact_path: None, impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { @@ -564,9 +556,17 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It } else { None }, + search_type: get_function_type_for_search( + &item, + tcx, + clean_impl_generics(cache.parent_stack.last()).as_ref(), + parent_did, + cache, + ), + aliases: item.attrs.get_doc_aliases(), + deprecation: item.deprecation(tcx), }); } - _ => {} } } From 08f4d54ea91b1092161e9bb77b5b6aa3f9d48714 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 2 Aug 2024 14:01:38 -0700 Subject: [PATCH 5/7] Extract local variables --- src/librustdoc/formats/cache.rs | 63 +++++++++++++++++---------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 5c18cebb965..effdce54015 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -503,17 +503,15 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It // We have a parent, but we don't know where they're // defined yet. Wait for later to index this item. let impl_generics = clean_impl_generics(cache.parent_stack.last()); - cache.orphan_impl_items.push(OrphanImplItem { - parent: parent_did, - item: item.clone(), - impl_generics, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }, - }); + let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() + { + item_id.as_def_id() + } else { + None + }; + let orphan_item = + OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id }; + cache.orphan_impl_items.push(orphan_item); } else if let Some(path) = parent_path && (is_impl_child || !cache.stripped_mod) { @@ -540,32 +538,37 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It // In case this is a field from a tuple struct, we don't add it into // the search index because its name is something like "0", which is // not useful for rustdoc search. - cache.search_index.push(IndexItem { + let path = join_with_double_colon(path); + let impl_id = + if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { + item_id.as_def_id() + } else { + None + }; + let search_type = get_function_type_for_search( + &item, + tcx, + clean_impl_generics(cache.parent_stack.last()).as_ref(), + parent_did, + cache, + ); + let aliases = item.attrs.get_doc_aliases(); + let deprecation = item.deprecation(tcx); + let index_item = IndexItem { ty, defid, name, - path: join_with_double_colon(path), + path, desc, parent: parent_did, parent_idx: None, exact_path: None, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = - cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }, - search_type: get_function_type_for_search( - &item, - tcx, - clean_impl_generics(cache.parent_stack.last()).as_ref(), - parent_did, - cache, - ), - aliases: item.attrs.get_doc_aliases(), - deprecation: item.deprecation(tcx), - }); + impl_id, + search_type, + aliases, + deprecation, + }; + cache.search_index.push(index_item); } } } From 220c2d8c9bc07d0a7b81104edb14f892e0a5f0aa Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 2 Aug 2024 14:48:36 -0700 Subject: [PATCH 6/7] Restructure `add_item_to_search_index` to eliminate code paths Many of the code paths it handled were actually impossible. In other cases, the various checks and transformations were spread around in such a way that it was hard to tell what was going on. --- src/librustdoc/formats/cache.rs | 211 +++++++++++++++----------------- 1 file changed, 102 insertions(+), 109 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index effdce54015..d2d0f5a4380 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -442,7 +442,9 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::Item, name: Symbol) { - let ((parent_did, parent_path), is_impl_child) = match *item.kind { + // Item has a name, so it must also have a DefId (can't be an impl, let alone a blanket or auto impl). + let item_def_id = item.item_id.as_def_id().unwrap(); + let (parent_did, parent_path) = match *item.kind { clean::StrippedItem(..) => return, clean::AssocConstItem(..) | clean::AssocTypeItem(..) if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) => @@ -454,123 +456,114 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) | clean::StructFieldItem(..) - | clean::VariantItem(..) => ( - ( - Some( - cache - .parent_stack - .last() - .expect("parent_stack is empty") - .item_id() - .expect_def_id(), - ), - Some(&cache.stack[..cache.stack.len() - 1]), - ), - false, - ), + | clean::VariantItem(..) => { + // Don't index if containing module is stripped (i.e., private), + // or if item is tuple struct/variant field (name is a number -> not useful for search). + if cache.stripped_mod + || item.type_() == ItemType::StructField + && name.as_str().chars().all(|c| c.is_digit(10)) + { + return; + } + let parent_did = + cache.parent_stack.last().expect("parent_stack is empty").item_id().expect_def_id(); + let parent_path = &cache.stack[..cache.stack.len() - 1]; + (Some(parent_did), parent_path) + } clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { let last = cache.parent_stack.last().expect("parent_stack is empty 2"); - let did = match &*last { - ParentStackItem::Impl { - // impl Trait for &T { fn method(self); } - // - // When generating a function index with the above shape, we want it - // associated with `T`, not with the primitive reference type. It should - // show up as `T::method`, rather than `reference::method`, in the search - // results page. - for_: clean::Type::BorrowedRef { type_, .. }, - .. - } => type_.def_id(&cache), + let parent_did = match &*last { + // impl Trait for &T { fn method(self); } + // + // When generating a function index with the above shape, we want it + // associated with `T`, not with the primitive reference type. It should + // show up as `T::method`, rather than `reference::method`, in the search + // results page. + ParentStackItem::Impl { for_: clean::Type::BorrowedRef { type_, .. }, .. } => { + type_.def_id(&cache) + } ParentStackItem::Impl { for_, .. } => for_.def_id(&cache), ParentStackItem::Type(item_id) => item_id.as_def_id(), }; - let path = did - .and_then(|did| cache.paths.get(&did)) - // The current stack not necessarily has correlation - // for where the type was defined. On the other - // hand, `paths` always has the right - // information if present. - .map(|(fqp, _)| &fqp[..fqp.len() - 1]); - ((did, path), true) + let Some(parent_did) = parent_did else { return }; + // The current stack not necessarily has correlation + // for where the type was defined. On the other + // hand, `paths` always has the right + // information if present. + match cache.paths.get(&parent_did) { + Some((fqp, _)) => (Some(parent_did), &fqp[..fqp.len() - 1]), + None => { + handle_orphan_impl_child(cache, item, parent_did); + return; + } + } + } + _ => { + // Don't index if item is crate root, which is inserted later on when serializing the index. + if item_def_id.is_crate_root() { + return; + } + (None, &*cache.stack) } - _ => ((None, Some(&*cache.stack)), false), }; - if let Some(parent_did) = parent_did - && parent_path.is_none() - && is_impl_child - { - // We have a parent, but we don't know where they're - // defined yet. Wait for later to index this item. - let impl_generics = clean_impl_generics(cache.parent_stack.last()); - let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }; - let orphan_item = - OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id }; - cache.orphan_impl_items.push(orphan_item); - } else if let Some(path) = parent_path - && (is_impl_child || !cache.stripped_mod) - { - debug_assert!(!item.is_stripped()); + debug_assert!(!item.is_stripped()); - // A crate has a module at its root, containing all items, - // which should not be indexed. The crate-item itself is - // inserted later on when serializing the search-index. - if item.item_id.as_def_id().is_some_and(|idx| !idx.is_crate_root()) - && let ty = item.type_() - && (ty != ItemType::StructField || u16::from_str_radix(name.as_str(), 10).is_err()) - { - let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); - // For searching purposes, a re-export is a duplicate if: - // - // - It's either an inline, or a true re-export - // - It's got the same name - // - Both of them have the same exact path - let defid = (match &*item.kind { - &clean::ItemKind::ImportItem(ref import) => import.source.did, - _ => None, - }) - .or_else(|| item.item_id.as_def_id()); - // In case this is a field from a tuple struct, we don't add it into - // the search index because its name is something like "0", which is - // not useful for rustdoc search. - let path = join_with_double_colon(path); - let impl_id = - if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { - item_id.as_def_id() - } else { - None - }; - let search_type = get_function_type_for_search( - &item, - tcx, - clean_impl_generics(cache.parent_stack.last()).as_ref(), - parent_did, - cache, - ); - let aliases = item.attrs.get_doc_aliases(); - let deprecation = item.deprecation(tcx); - let index_item = IndexItem { - ty, - defid, - name, - path, - desc, - parent: parent_did, - parent_idx: None, - exact_path: None, - impl_id, - search_type, - aliases, - deprecation, - }; - cache.search_index.push(index_item); - } - } + let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); + // For searching purposes, a re-export is a duplicate if: + // + // - It's either an inline, or a true re-export + // - It's got the same name + // - Both of them have the same exact path + let defid = match &*item.kind { + clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id), + _ => item_def_id, + }; + let path = join_with_double_colon(parent_path); + let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { + item_id.as_def_id() + } else { + None + }; + let search_type = get_function_type_for_search( + &item, + tcx, + clean_impl_generics(cache.parent_stack.last()).as_ref(), + parent_did, + cache, + ); + let aliases = item.attrs.get_doc_aliases(); + let deprecation = item.deprecation(tcx); + let index_item = IndexItem { + ty: item.type_(), + defid: Some(defid), + name, + path, + desc, + parent: parent_did, + parent_idx: None, + exact_path: None, + impl_id, + search_type, + aliases, + deprecation, + }; + cache.search_index.push(index_item); +} + +/// We have a parent, but we don't know where they're +/// defined yet. Wait for later to index this item. +/// See [`Cache::orphan_impl_items`]. +fn handle_orphan_impl_child(cache: &mut Cache, item: &clean::Item, parent_did: DefId) { + let impl_generics = clean_impl_generics(cache.parent_stack.last()); + let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { + item_id.as_def_id() + } else { + None + }; + let orphan_item = + OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id }; + cache.orphan_impl_items.push(orphan_item); } pub(crate) struct OrphanImplItem { From 007d9e1c26da1e509ff7887604e6685f1445982a Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Sat, 3 Aug 2024 20:51:28 -0700 Subject: [PATCH 7/7] rustdoc: Re-add missing `stripped_mod` check; explain orphan impls Co-authored-by: Michael Howell --- src/librustdoc/formats/cache.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index d2d0f5a4380..3fdc8786471 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -486,10 +486,30 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It ParentStackItem::Type(item_id) => item_id.as_def_id(), }; let Some(parent_did) = parent_did else { return }; - // The current stack not necessarily has correlation - // for where the type was defined. On the other - // hand, `paths` always has the right - // information if present. + // The current stack reflects the CacheBuilder's recursive + // walk over HIR. For associated items, this is the module + // where the `impl` block is defined. That's an implementation + // detail that we don't want to affect the search engine. + // + // In particular, you can arrange things like this: + // + // #![crate_name="me"] + // mod private_mod { + // impl Clone for MyThing { fn clone(&self) -> MyThing { MyThing } } + // } + // pub struct MyThing; + // + // When that happens, we need to: + // - ignore the `cache.stripped_mod` flag, since the Clone impl is actually + // part of the public API even though it's defined in a private module + // - present the method as `me::MyThing::clone`, its publicly-visible path + // - deal with the fact that the recursive walk hasn't actually reached `MyThing` + // until it's already past `private_mod`, since that's first, and doesn't know + // yet if `MyThing` will actually be public or not (it could be re-exported) + // + // We accomplish the last two points by recording children of "orphan impls" + // in a field of the cache whose elements are added to the search index later, + // after cache building is complete (see `handle_orphan_impl_child`). match cache.paths.get(&parent_did) { Some((fqp, _)) => (Some(parent_did), &fqp[..fqp.len() - 1]), None => { @@ -500,7 +520,8 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It } _ => { // Don't index if item is crate root, which is inserted later on when serializing the index. - if item_def_id.is_crate_root() { + // Don't index if containing module is stripped (i.e., private), + if item_def_id.is_crate_root() || cache.stripped_mod { return; } (None, &*cache.stack)