Skip to content

Commit f36c5af

Browse files
committed
rustdoc-search: single result for items with multiple paths
This change uses the same "exact" paths as trait implementors and type alias inlining to track items with multiple reachable paths. This way, if you search for `vec`, you get only the `std` exports of it, and not the one from `alloc`. It still includes all the items in the search index so that you can search for them by all available paths. For example, try `core::option` and `std::option`, and notice that the results page doesn't show duplicates, but still shows all the items in their respective crates.
1 parent ab5bda1 commit f36c5af

13 files changed

Lines changed: 313 additions & 25 deletions

File tree

‎src/librustdoc/formats/cache.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,16 +348,28 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
348348
{
349349
let desc =
350350
short_markdown_summary(&item.doc_value(),&item.link_names(self.cache));
351+
// For searching purposes, a re-export is a duplicate if:
352+
//
353+
// - It's either an inline, or a true re-export
354+
// - It's got the same name
355+
// - Both of them have the same exact path
356+
let defid = (match&*item.kind{
357+
&clean::ItemKind::ImportItem(ref import) => import.source.did,
358+
_ => None,
359+
})
360+
.or_else(|| item.item_id.as_def_id());
351361
// In case this is a field from a tuple struct, we don't add it into
352362
// the search index because its name is something like "0", which is
353363
// not useful for rustdoc search.
354364
self.cache.search_index.push(IndexItem{
355365
ty,
366+
defid,
356367
name: s,
357368
path:join_with_double_colon(path),
358369
desc,
359370
parent,
360371
parent_idx:None,
372+
exact_path:None,
361373
impl_id:ifletSome(ParentStackItem::Impl{ item_id, .. }) =
362374
self.cache.parent_stack.last()
363375
{

‎src/librustdoc/html/render/mod.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,13 @@ pub(crate) enum RenderMode {
111111
#[derive(Debug)]
112112
pub(crate)structIndexItem{
113113
pub(crate)ty:ItemType,
114+
pub(crate)defid:Option<DefId>,
114115
pub(crate)name:Symbol,
115116
pub(crate)path:String,
116117
pub(crate)desc:String,
117118
pub(crate)parent:Option<DefId>,
118119
pub(crate)parent_idx:Option<isize>,
120+
pub(crate)exact_path:Option<String>,
119121
pub(crate)impl_id:Option<DefId>,
120122
pub(crate)search_type:Option<IndexItemFunctionType>,
121123
pub(crate)aliases:Box<[Symbol]>,

‎src/librustdoc/html/render/search_index.rs‎

Lines changed: 134 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,13 @@ pub(crate) fn build_index<'tcx>(
5959
cache:&mutCache,
6060
tcx:TyCtxt<'tcx>,
6161
) -> SerializedSearchIndex{
62+
// Maps from ID to position in the `crate_paths` array.
6263
letmut itemid_to_pathid = FxHashMap::default();
6364
letmut primitives = FxHashMap::default();
6465
letmut associated_types = FxHashMap::default();
65-
letmut crate_paths = vec![];
66+
67+
// item type, display path, re-exported internal path
68+
letmut crate_paths:Vec<(ItemType,Vec<Symbol>,Option<Vec<Symbol>>)> = vec![];
6669

6770
// Attach all orphan items to the type's definition if the type
6871
// has since been learned.
@@ -72,11 +75,13 @@ pub(crate) fn build_index<'tcx>(
7275
let desc = short_markdown_summary(&item.doc_value(),&item.link_names(cache));
7376
cache.search_index.push(IndexItem{
7477
ty: item.type_(),
78+
defid: item.item_id.as_def_id(),
7579
name: item.name.unwrap(),
7680
path:join_with_double_colon(&fqp[..fqp.len() - 1]),
7781
desc,
7882
parent:Some(parent),
7983
parent_idx:None,
84+
exact_path:None,
8085
impl_id,
8186
search_type:get_function_type_for_search(
8287
item,
@@ -126,17 +131,22 @@ pub(crate) fn build_index<'tcx>(
126131
map:&mutFxHashMap<F,isize>,
127132
itemid:F,
128133
lastpathid:&mutisize,
129-
crate_paths:&mutVec<(ItemType,Vec<Symbol>)>,
134+
crate_paths:&mutVec<(ItemType,Vec<Symbol>,Option<Vec<Symbol>>)>,
130135
item_type:ItemType,
131136
path:&[Symbol],
137+
exact_path:Option<&[Symbol]>,
132138
) -> RenderTypeId{
133139
match map.entry(itemid){
134140
Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()),
135141
Entry::Vacant(entry) => {
136142
let pathid = *lastpathid;
137143
entry.insert(pathid);
138144
*lastpathid += 1;
139-
crate_paths.push((item_type, path.to_vec()));
145+
crate_paths.push((
146+
item_type,
147+
path.to_vec(),
148+
exact_path.map(|path| path.to_vec()),
149+
));
140150
RenderTypeId::Index(pathid)
141151
}
142152
}
@@ -149,21 +159,30 @@ pub(crate) fn build_index<'tcx>(
149159
primitives:&mutFxHashMap<Symbol,isize>,
150160
associated_types:&mutFxHashMap<Symbol,isize>,
151161
lastpathid:&mutisize,
152-
crate_paths:&mutVec<(ItemType,Vec<Symbol>)>,
162+
crate_paths:&mutVec<(ItemType,Vec<Symbol>,Option<Vec<Symbol>>)>,
153163
) -> Option<RenderTypeId>{
154-
letCache{ref paths,ref external_paths, .. } = *cache;
164+
letCache{ref paths,ref external_paths,ref exact_paths,.. } = *cache;
155165
match id {
156166
RenderTypeId::DefId(defid) => {
157167
ifletSome(&(ref fqp, item_type)) =
158168
paths.get(&defid).or_else(|| external_paths.get(&defid))
159169
{
170+
let exact_fqp = exact_paths
171+
.get(&defid)
172+
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp))
173+
// re-exports only count if the name is exactly the same
174+
// this is a size optimization, as well as a DWIM attempt
175+
// since if the names are not the same, the intent probably
176+
// isn't, either
177+
.filter(|fqp| fqp.last() == fqp.last());
160178
Some(insert_into_map(
161179
itemid_to_pathid,
162180
ItemId::DefId(defid),
163181
lastpathid,
164182
crate_paths,
165183
item_type,
166184
fqp,
185+
exact_fqp.map(|x| &x[..]).filter(|exact_fqp| exact_fqp != fqp),
167186
))
168187
}else{
169188
None
@@ -178,6 +197,7 @@ pub(crate) fn build_index<'tcx>(
178197
crate_paths,
179198
ItemType::Primitive,
180199
&[sym],
200+
None,
181201
))
182202
}
183203
RenderTypeId::Index(_) => Some(id),
@@ -188,6 +208,7 @@ pub(crate) fn build_index<'tcx>(
188208
crate_paths,
189209
ItemType::AssocType,
190210
&[sym],
211+
None,
191212
)),
192213
}
193214
}
@@ -199,7 +220,7 @@ pub(crate) fn build_index<'tcx>(
199220
primitives:&mutFxHashMap<Symbol,isize>,
200221
associated_types:&mutFxHashMap<Symbol,isize>,
201222
lastpathid:&mutisize,
202-
crate_paths:&mutVec<(ItemType,Vec<Symbol>)>,
223+
crate_paths:&mutVec<(ItemType,Vec<Symbol>,Option<Vec<Symbol>>)>,
203224
){
204225
ifletSome(generics) = &mut ty.generics{
205226
for item in generics {
@@ -296,7 +317,7 @@ pub(crate) fn build_index<'tcx>(
296317
}
297318
}
298319

299-
letCache{ref paths, .. } = *cache;
320+
letCache{ref paths,ref exact_paths,ref external_paths,.. } = *cache;
300321

301322
// Then, on parent modules
302323
let crate_items:Vec<&IndexItem> = search_index
@@ -311,14 +332,54 @@ pub(crate) fn build_index<'tcx>(
311332
lastpathid += 1;
312333

313334
ifletSome(&(ref fqp, short)) = paths.get(&defid){
314-
crate_paths.push((short, fqp.clone()));
335+
let exact_fqp = exact_paths
336+
.get(&defid)
337+
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp))
338+
.filter(|exact_fqp| {
339+
exact_fqp.last() == Some(&item.name) && *exact_fqp != fqp
340+
});
341+
crate_paths.push((short, fqp.clone(), exact_fqp.cloned()));
315342
Some(pathid)
316343
}else{
317344
None
318345
}
319346
}
320347
});
321348

349+
ifletSome(defid) = item.defid
350+
&& item.parent_idx.is_none()
351+
{
352+
// If this is a re-export, retain the original path.
353+
// Associated items don't use this.
354+
// Their parent carries the exact fqp instead.
355+
let exact_fqp = exact_paths
356+
.get(&defid)
357+
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp));
358+
item.exact_path = exact_fqp.and_then(|fqp| {
359+
// re-exports only count if the name is exactly the same
360+
// this is a size optimization, as well as a DWIM attempt
361+
// since if the names are not the same, the intent probably
362+
// isn't, either
363+
if fqp.last() != Some(&item.name){
364+
returnNone;
365+
}
366+
let path =
367+
if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export){
368+
// `#[macro_export]` always exports to the crate root.
369+
tcx.crate_name(defid.krate).to_string()
370+
}else{
371+
if fqp.len() < 2{
372+
returnNone;
373+
}
374+
join_with_double_colon(&fqp[..fqp.len() - 1])
375+
};
376+
if path == item.path{
377+
returnNone;
378+
}
379+
Some(path)
380+
});
381+
}
382+
322383
// Omit the parent path if it is same to that of the prior item.
323384
if lastpath == &item.path{
324385
item.path.clear();
@@ -356,7 +417,7 @@ pub(crate) fn build_index<'tcx>(
356417

357418
structCrateData<'a>{
358419
items:Vec<&'aIndexItem>,
359-
paths:Vec<(ItemType,Vec<Symbol>)>,
420+
paths:Vec<(ItemType,Vec<Symbol>,Option<Vec<Symbol>>)>,
360421
// The String is alias name and the vec is the list of the elements with this alias.
361422
//
362423
// To be noted: the `usize` elements are indexes to `items`.
@@ -374,6 +435,7 @@ pub(crate) fn build_index<'tcx>(
374435
ty:ItemType,
375436
name:Symbol,
376437
path:Option<usize>,
438+
exact_path:Option<usize>,
377439
}
378440

379441
implSerializeforPaths{
@@ -387,6 +449,10 @@ pub(crate) fn build_index<'tcx>(
387449
ifletSome(ref path) = self.path{
388450
seq.serialize_element(path)?;
389451
}
452+
ifletSome(ref path) = self.exact_path{
453+
assert!(self.path.is_some());
454+
seq.serialize_element(path)?;
455+
}
390456
seq.end()
391457
}
392458
}
@@ -409,43 +475,94 @@ pub(crate) fn build_index<'tcx>(
409475
mod_paths.insert(&item.path, index);
410476
}
411477
letmut paths = Vec::with_capacity(self.paths.len());
412-
for(ty, path)in&self.paths{
478+
for(ty, path, exact)in&self.paths{
413479
if path.len() < 2{
414-
paths.push(Paths{ty:*ty,name: path[0],path:None});
480+
paths.push(Paths{ty:*ty,name: path[0],path:None,exact_path:None});
415481
continue;
416482
}
417483
let full_path = join_with_double_colon(&path[..path.len() - 1]);
484+
let full_exact_path = exact
485+
.as_ref()
486+
.filter(|exact| exact.last() == path.last() && exact.len() >= 2)
487+
.map(|exact| join_with_double_colon(&exact[..exact.len() - 1]));
488+
let exact_path = extra_paths.len() + self.items.len();
489+
let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths
490+
.entry(full_exact_path.clone())
491+
{
492+
Entry::Occupied(entry) => *entry.get(),
493+
Entry::Vacant(entry) => {
494+
ifletSome(index) = mod_paths.get(&full_exact_path){
495+
return*index;
496+
}
497+
entry.insert(exact_path);
498+
if !revert_extra_paths.contains_key(&exact_path){
499+
revert_extra_paths.insert(exact_path, full_exact_path.clone());
500+
}
501+
exact_path
502+
}
503+
});
418504
ifletSome(index) = mod_paths.get(&full_path){
419-
paths.push(Paths{ty:*ty,name:*path.last().unwrap(),path:Some(*index)});
505+
paths.push(Paths{
506+
ty:*ty,
507+
name:*path.last().unwrap(),
508+
path:Some(*index),
509+
exact_path,
510+
});
420511
continue;
421512
}
422513
// It means it comes from an external crate so the item and its path will be
423514
// stored into another array.
424515
//
425516
// `index` is put after the last `mod_paths`
426517
let index = extra_paths.len() + self.items.len();
427-
if !revert_extra_paths.contains_key(&index){
428-
revert_extra_paths.insert(index, full_path.clone());
429-
}
430-
match extra_paths.entry(full_path){
518+
match extra_paths.entry(full_path.clone()){
431519
Entry::Occupied(entry) => {
432520
paths.push(Paths{
433521
ty:*ty,
434522
name:*path.last().unwrap(),
435523
path:Some(*entry.get()),
524+
exact_path,
436525
});
437526
}
438527
Entry::Vacant(entry) => {
439528
entry.insert(index);
529+
if !revert_extra_paths.contains_key(&index){
530+
revert_extra_paths.insert(index, full_path);
531+
}
440532
paths.push(Paths{
441533
ty:*ty,
442534
name:*path.last().unwrap(),
443535
path:Some(index),
536+
exact_path,
444537
});
445538
}
446539
}
447540
}
448541

542+
// Direct exports use adjacent arrays for the current crate's items,
543+
// but re-exported exact paths don't.
544+
letmut re_exports = Vec::new();
545+
for(item_index, item)inself.items.iter().enumerate(){
546+
ifletSome(exact_path) = item.exact_path.as_ref(){
547+
ifletSome(path_index) = mod_paths.get(&exact_path){
548+
re_exports.push((item_index,*path_index));
549+
}else{
550+
let path_index = extra_paths.len() + self.items.len();
551+
let path_index = match extra_paths.entry(exact_path.clone()){
552+
Entry::Occupied(entry) => *entry.get(),
553+
Entry::Vacant(entry) => {
554+
entry.insert(path_index);
555+
if !revert_extra_paths.contains_key(&path_index){
556+
revert_extra_paths.insert(path_index, exact_path.clone());
557+
}
558+
path_index
559+
}
560+
};
561+
re_exports.push((item_index, path_index));
562+
}
563+
}
564+
}
565+
449566
letmut names = Vec::with_capacity(self.items.len());
450567
letmut types = String::with_capacity(self.items.len());
451568
letmut full_paths = Vec::with_capacity(self.items.len());
@@ -501,6 +618,7 @@ pub(crate) fn build_index<'tcx>(
501618
crate_data.serialize_field("f",&functions)?;
502619
crate_data.serialize_field("D",&self.desc_index)?;
503620
crate_data.serialize_field("p",&paths)?;
621+
crate_data.serialize_field("r",&re_exports)?;
504622
crate_data.serialize_field("b",&self.associated_item_disambiguators)?;
505623
crate_data.serialize_field("c",&bitmap_to_string(&deprecated))?;
506624
crate_data.serialize_field("e",&bitmap_to_string(&self.empty_desc))?;

0 commit comments

Comments
 (0)