Skip to content

Commit 6365ca2

Browse files
authored
Unrolled build for rust-lang#128578
Rollup merge of rust-lang#128578 - camelid:cache-index-cleanup, r=notriddle rustdoc: Cleanup `CacheBuilder` code for building search index This code was very convoluted and hard to reason about. It is now (I hope) much clearer and more suitable for both future enhancements and future cleanups. I'm doing this as a precursor, with no UI changes, to changing rustdoc to [ignore blanket impls][1] in type-based search. [1]: rust-lang#128471 (comment) r? ``@notriddle``
2 parents 58fb508 + 007d9e1 commit 6365ca2

File tree

2 files changed

+167
-157
lines changed

2 files changed

+167
-157
lines changed

src/librustdoc/clean/types.rs

+8-12
Original file line numberDiff line numberDiff line change
@@ -1677,13 +1677,16 @@ impl Type {
16771677
}
16781678
}
16791679

1680-
fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
1680+
/// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s.
1681+
///
1682+
/// [clean]: crate::clean
1683+
pub(crate) fn def_id(&self, cache: &Cache) -> Option<DefId> {
16811684
let t: PrimitiveType = match *self {
16821685
Type::Path { ref path } => return Some(path.def_id()),
16831686
DynTrait(ref bounds, _) => return bounds.get(0).map(|b| b.trait_.def_id()),
1684-
Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()),
1687+
Primitive(p) => return cache.primitive_locations.get(&p).cloned(),
16851688
BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference,
1686-
BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache),
1689+
BorrowedRef { ref type_, .. } => return type_.def_id(cache),
16871690
Tuple(ref tys) => {
16881691
if tys.is_empty() {
16891692
PrimitiveType::Unit
@@ -1696,17 +1699,10 @@ impl Type {
16961699
Array(..) => PrimitiveType::Array,
16971700
Type::Pat(..) => PrimitiveType::Pat,
16981701
RawPointer(..) => PrimitiveType::RawPointer,
1699-
QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache),
1702+
QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache),
17001703
Generic(_) | Infer | ImplTrait(_) => return None,
17011704
};
1702-
cache.and_then(|c| Primitive(t).def_id(c))
1703-
}
1704-
1705-
/// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s.
1706-
///
1707-
/// [clean]: crate::clean
1708-
pub(crate) fn def_id(&self, cache: &Cache) -> Option<DefId> {
1709-
self.inner_def_id(Some(cache))
1705+
Primitive(t).def_id(cache)
17101706
}
17111707
}
17121708

src/librustdoc/formats/cache.rs

+159-145
Original file line numberDiff line numberDiff line change
@@ -260,153 +260,21 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
260260
}
261261

262262
// Index this method for searching later on.
263-
if let Some(s) = item.name.or_else(|| {
264-
if item.is_stripped() {
265-
None
266-
} else if let clean::ImportItem(ref i) = *item.kind
267-
&& let clean::ImportKind::Simple(s) = i.kind
268-
{
269-
Some(s)
270-
} else {
271-
None
272-
}
273-
}) {
274-
let (parent, is_inherent_impl_item) = match *item.kind {
275-
clean::StrippedItem(..) => ((None, None), false),
276-
clean::AssocConstItem(..) | clean::AssocTypeItem(..)
277-
if self
278-
.cache
279-
.parent_stack
280-
.last()
281-
.is_some_and(|parent| parent.is_trait_impl()) =>
263+
let search_name = if !item.is_stripped() {
264+
item.name.or_else(|| {
265+
if let clean::ImportItem(ref i) = *item.kind
266+
&& let clean::ImportKind::Simple(s) = i.kind
282267
{
283-
// skip associated items in trait impls
284-
((None, None), false)
285-
}
286-
clean::TyMethodItem(..)
287-
| clean::TyAssocConstItem(..)
288-
| clean::TyAssocTypeItem(..)
289-
| clean::StructFieldItem(..)
290-
| clean::VariantItem(..) => (
291-
(
292-
Some(
293-
self.cache
294-
.parent_stack
295-
.last()
296-
.expect("parent_stack is empty")
297-
.item_id()
298-
.expect_def_id(),
299-
),
300-
Some(&self.cache.stack[..self.cache.stack.len() - 1]),
301-
),
302-
false,
303-
),
304-
clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
305-
if self.cache.parent_stack.is_empty() {
306-
((None, None), false)
307-
} else {
308-
let last = self.cache.parent_stack.last().expect("parent_stack is empty 2");
309-
let did = match &*last {
310-
ParentStackItem::Impl {
311-
// impl Trait for &T { fn method(self); }
312-
//
313-
// When generating a function index with the above shape, we want it
314-
// associated with `T`, not with the primitive reference type. It should
315-
// show up as `T::method`, rather than `reference::method`, in the search
316-
// results page.
317-
for_: clean::Type::BorrowedRef { type_, .. },
318-
..
319-
} => type_.def_id(&self.cache),
320-
ParentStackItem::Impl { for_, .. } => for_.def_id(&self.cache),
321-
ParentStackItem::Type(item_id) => item_id.as_def_id(),
322-
};
323-
let path = did
324-
.and_then(|did| self.cache.paths.get(&did))
325-
// The current stack not necessarily has correlation
326-
// for where the type was defined. On the other
327-
// hand, `paths` always has the right
328-
// information if present.
329-
.map(|(fqp, _)| &fqp[..fqp.len() - 1]);
330-
((did, path), true)
331-
}
332-
}
333-
_ => ((None, Some(&*self.cache.stack)), false),
334-
};
335-
336-
match parent {
337-
(parent, Some(path)) if is_inherent_impl_item || !self.cache.stripped_mod => {
338-
debug_assert!(!item.is_stripped());
339-
340-
// A crate has a module at its root, containing all items,
341-
// which should not be indexed. The crate-item itself is
342-
// inserted later on when serializing the search-index.
343-
if item.item_id.as_def_id().is_some_and(|idx| !idx.is_crate_root())
344-
&& let ty = item.type_()
345-
&& (ty != ItemType::StructField
346-
|| u16::from_str_radix(s.as_str(), 10).is_err())
347-
{
348-
let desc =
349-
short_markdown_summary(&item.doc_value(), &item.link_names(self.cache));
350-
// For searching purposes, a re-export is a duplicate if:
351-
//
352-
// - It's either an inline, or a true re-export
353-
// - It's got the same name
354-
// - Both of them have the same exact path
355-
let defid = (match &*item.kind {
356-
&clean::ItemKind::ImportItem(ref import) => import.source.did,
357-
_ => None,
358-
})
359-
.or_else(|| item.item_id.as_def_id());
360-
// In case this is a field from a tuple struct, we don't add it into
361-
// the search index because its name is something like "0", which is
362-
// not useful for rustdoc search.
363-
self.cache.search_index.push(IndexItem {
364-
ty,
365-
defid,
366-
name: s,
367-
path: join_with_double_colon(path),
368-
desc,
369-
parent,
370-
parent_idx: None,
371-
exact_path: None,
372-
impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
373-
self.cache.parent_stack.last()
374-
{
375-
item_id.as_def_id()
376-
} else {
377-
None
378-
},
379-
search_type: get_function_type_for_search(
380-
&item,
381-
self.tcx,
382-
clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
383-
parent,
384-
self.cache,
385-
),
386-
aliases: item.attrs.get_doc_aliases(),
387-
deprecation: item.deprecation(self.tcx),
388-
});
389-
}
390-
}
391-
(Some(parent), None) if is_inherent_impl_item => {
392-
// We have a parent, but we don't know where they're
393-
// defined yet. Wait for later to index this item.
394-
let impl_generics = clean_impl_generics(self.cache.parent_stack.last());
395-
self.cache.orphan_impl_items.push(OrphanImplItem {
396-
parent,
397-
item: item.clone(),
398-
impl_generics,
399-
impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
400-
self.cache.parent_stack.last()
401-
{
402-
item_id.as_def_id()
403-
} else {
404-
None
405-
},
406-
});
268+
Some(s)
269+
} else {
270+
None
407271
}
408-
_ => {}
409-
}
272+
})
273+
} else {
274+
None
275+
};
276+
if let Some(name) = search_name {
277+
add_item_to_search_index(self.tcx, &mut self.cache, &item, name)
410278
}
411279

412280
// Keep track of the fully qualified path for this item.
@@ -572,6 +440,152 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
572440
}
573441
}
574442

443+
fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::Item, name: Symbol) {
444+
// Item has a name, so it must also have a DefId (can't be an impl, let alone a blanket or auto impl).
445+
let item_def_id = item.item_id.as_def_id().unwrap();
446+
let (parent_did, parent_path) = match *item.kind {
447+
clean::StrippedItem(..) => return,
448+
clean::AssocConstItem(..) | clean::AssocTypeItem(..)
449+
if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) =>
450+
{
451+
// skip associated items in trait impls
452+
return;
453+
}
454+
clean::TyMethodItem(..)
455+
| clean::TyAssocConstItem(..)
456+
| clean::TyAssocTypeItem(..)
457+
| clean::StructFieldItem(..)
458+
| clean::VariantItem(..) => {
459+
// Don't index if containing module is stripped (i.e., private),
460+
// or if item is tuple struct/variant field (name is a number -> not useful for search).
461+
if cache.stripped_mod
462+
|| item.type_() == ItemType::StructField
463+
&& name.as_str().chars().all(|c| c.is_digit(10))
464+
{
465+
return;
466+
}
467+
let parent_did =
468+
cache.parent_stack.last().expect("parent_stack is empty").item_id().expect_def_id();
469+
let parent_path = &cache.stack[..cache.stack.len() - 1];
470+
(Some(parent_did), parent_path)
471+
}
472+
clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
473+
let last = cache.parent_stack.last().expect("parent_stack is empty 2");
474+
let parent_did = match &*last {
475+
// impl Trait for &T { fn method(self); }
476+
//
477+
// When generating a function index with the above shape, we want it
478+
// associated with `T`, not with the primitive reference type. It should
479+
// show up as `T::method`, rather than `reference::method`, in the search
480+
// results page.
481+
ParentStackItem::Impl { for_: clean::Type::BorrowedRef { type_, .. }, .. } => {
482+
type_.def_id(&cache)
483+
}
484+
ParentStackItem::Impl { for_, .. } => for_.def_id(&cache),
485+
ParentStackItem::Type(item_id) => item_id.as_def_id(),
486+
};
487+
let Some(parent_did) = parent_did else { return };
488+
// The current stack reflects the CacheBuilder's recursive
489+
// walk over HIR. For associated items, this is the module
490+
// where the `impl` block is defined. That's an implementation
491+
// detail that we don't want to affect the search engine.
492+
//
493+
// In particular, you can arrange things like this:
494+
//
495+
// #![crate_name="me"]
496+
// mod private_mod {
497+
// impl Clone for MyThing { fn clone(&self) -> MyThing { MyThing } }
498+
// }
499+
// pub struct MyThing;
500+
//
501+
// When that happens, we need to:
502+
// - ignore the `cache.stripped_mod` flag, since the Clone impl is actually
503+
// part of the public API even though it's defined in a private module
504+
// - present the method as `me::MyThing::clone`, its publicly-visible path
505+
// - deal with the fact that the recursive walk hasn't actually reached `MyThing`
506+
// until it's already past `private_mod`, since that's first, and doesn't know
507+
// yet if `MyThing` will actually be public or not (it could be re-exported)
508+
//
509+
// We accomplish the last two points by recording children of "orphan impls"
510+
// in a field of the cache whose elements are added to the search index later,
511+
// after cache building is complete (see `handle_orphan_impl_child`).
512+
match cache.paths.get(&parent_did) {
513+
Some((fqp, _)) => (Some(parent_did), &fqp[..fqp.len() - 1]),
514+
None => {
515+
handle_orphan_impl_child(cache, item, parent_did);
516+
return;
517+
}
518+
}
519+
}
520+
_ => {
521+
// Don't index if item is crate root, which is inserted later on when serializing the index.
522+
// Don't index if containing module is stripped (i.e., private),
523+
if item_def_id.is_crate_root() || cache.stripped_mod {
524+
return;
525+
}
526+
(None, &*cache.stack)
527+
}
528+
};
529+
530+
debug_assert!(!item.is_stripped());
531+
532+
let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
533+
// For searching purposes, a re-export is a duplicate if:
534+
//
535+
// - It's either an inline, or a true re-export
536+
// - It's got the same name
537+
// - Both of them have the same exact path
538+
let defid = match &*item.kind {
539+
clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id),
540+
_ => item_def_id,
541+
};
542+
let path = join_with_double_colon(parent_path);
543+
let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() {
544+
item_id.as_def_id()
545+
} else {
546+
None
547+
};
548+
let search_type = get_function_type_for_search(
549+
&item,
550+
tcx,
551+
clean_impl_generics(cache.parent_stack.last()).as_ref(),
552+
parent_did,
553+
cache,
554+
);
555+
let aliases = item.attrs.get_doc_aliases();
556+
let deprecation = item.deprecation(tcx);
557+
let index_item = IndexItem {
558+
ty: item.type_(),
559+
defid: Some(defid),
560+
name,
561+
path,
562+
desc,
563+
parent: parent_did,
564+
parent_idx: None,
565+
exact_path: None,
566+
impl_id,
567+
search_type,
568+
aliases,
569+
deprecation,
570+
};
571+
cache.search_index.push(index_item);
572+
}
573+
574+
/// We have a parent, but we don't know where they're
575+
/// defined yet. Wait for later to index this item.
576+
/// See [`Cache::orphan_impl_items`].
577+
fn handle_orphan_impl_child(cache: &mut Cache, item: &clean::Item, parent_did: DefId) {
578+
let impl_generics = clean_impl_generics(cache.parent_stack.last());
579+
let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() {
580+
item_id.as_def_id()
581+
} else {
582+
None
583+
};
584+
let orphan_item =
585+
OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id };
586+
cache.orphan_impl_items.push(orphan_item);
587+
}
588+
575589
pub(crate) struct OrphanImplItem {
576590
pub(crate) parent: DefId,
577591
pub(crate) impl_id: Option<DefId>,

0 commit comments

Comments
 (0)