Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e3181b0

Browse files
committedApr 18, 2024
Auto merge of rust-lang#119912 - notriddle:notriddle/reexport-dedup, r=GuillaumeGomez
rustdoc-search: single result for items with multiple paths Part of rust-lang#15723 Preview: https://notriddle.com/rustdoc-html-demo-9/reexport-dup/std/index.html?search=hashmap 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.
2 parents 3412f01 + 226c77d commit e3181b0

File tree

14 files changed

+330
-31
lines changed

14 files changed

+330
-31
lines changed
 

‎src/librustdoc/formats/cache.rs

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

‎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) struct IndexItem {
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: 138 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,13 @@ pub(crate) fn build_index<'tcx>(
5959
cache: &mut Cache,
6060
tcx: TyCtxt<'tcx>,
6161
) -> SerializedSearchIndex {
62+
// Maps from ID to position in the `crate_paths` array.
6263
let mut itemid_to_pathid = FxHashMap::default();
6364
let mut primitives = FxHashMap::default();
6465
let mut associated_types = FxHashMap::default();
65-
let mut crate_paths = vec![];
66+
67+
// item type, display path, re-exported internal path
68+
let mut 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: &mut FxHashMap<F, isize>,
127132
itemid: F,
128133
lastpathid: &mut isize,
129-
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
134+
crate_paths: &mut Vec<(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,32 @@ pub(crate) fn build_index<'tcx>(
149159
primitives: &mut FxHashMap<Symbol, isize>,
150160
associated_types: &mut FxHashMap<Symbol, isize>,
151161
lastpathid: &mut isize,
152-
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
162+
crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
153163
) -> Option<RenderTypeId> {
154-
let Cache { ref paths, ref external_paths, .. } = *cache;
164+
let Cache { ref paths, ref external_paths, ref exact_paths, .. } = *cache;
155165
match id {
156166
RenderTypeId::DefId(defid) => {
157167
if let Some(&(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, since it means we only need
175+
// to store the name once (and the path is re-used for everything
176+
// exported from this same module). It's also likely to Do
177+
// What I Mean, since if a re-export changes the name, it might
178+
// also be a change in semantic meaning.
179+
.filter(|fqp| fqp.last() == fqp.last());
160180
Some(insert_into_map(
161181
itemid_to_pathid,
162182
ItemId::DefId(defid),
163183
lastpathid,
164184
crate_paths,
165185
item_type,
166186
fqp,
187+
exact_fqp.map(|x| &x[..]).filter(|exact_fqp| exact_fqp != fqp),
167188
))
168189
} else {
169190
None
@@ -178,6 +199,7 @@ pub(crate) fn build_index<'tcx>(
178199
crate_paths,
179200
ItemType::Primitive,
180201
&[sym],
202+
None,
181203
))
182204
}
183205
RenderTypeId::Index(_) => Some(id),
@@ -188,6 +210,7 @@ pub(crate) fn build_index<'tcx>(
188210
crate_paths,
189211
ItemType::AssocType,
190212
&[sym],
213+
None,
191214
)),
192215
}
193216
}
@@ -199,7 +222,7 @@ pub(crate) fn build_index<'tcx>(
199222
primitives: &mut FxHashMap<Symbol, isize>,
200223
associated_types: &mut FxHashMap<Symbol, isize>,
201224
lastpathid: &mut isize,
202-
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
225+
crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
203226
) {
204227
if let Some(generics) = &mut ty.generics {
205228
for item in generics {
@@ -296,7 +319,7 @@ pub(crate) fn build_index<'tcx>(
296319
}
297320
}
298321

299-
let Cache { ref paths, .. } = *cache;
322+
let Cache { ref paths, ref exact_paths, ref external_paths, .. } = *cache;
300323

301324
// Then, on parent modules
302325
let crate_items: Vec<&IndexItem> = search_index
@@ -311,14 +334,56 @@ pub(crate) fn build_index<'tcx>(
311334
lastpathid += 1;
312335

313336
if let Some(&(ref fqp, short)) = paths.get(&defid) {
314-
crate_paths.push((short, fqp.clone()));
337+
let exact_fqp = exact_paths
338+
.get(&defid)
339+
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp))
340+
.filter(|exact_fqp| {
341+
exact_fqp.last() == Some(&item.name) && *exact_fqp != fqp
342+
});
343+
crate_paths.push((short, fqp.clone(), exact_fqp.cloned()));
315344
Some(pathid)
316345
} else {
317346
None
318347
}
319348
}
320349
});
321350

351+
if let Some(defid) = item.defid
352+
&& item.parent_idx.is_none()
353+
{
354+
// If this is a re-export, retain the original path.
355+
// Associated items don't use this.
356+
// Their parent carries the exact fqp instead.
357+
let exact_fqp = exact_paths
358+
.get(&defid)
359+
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp));
360+
item.exact_path = exact_fqp.and_then(|fqp| {
361+
// Re-exports only count if the name is exactly the same.
362+
// This is a size optimization, since it means we only need
363+
// to store the name once (and the path is re-used for everything
364+
// exported from this same module). It's also likely to Do
365+
// What I Mean, since if a re-export changes the name, it might
366+
// also be a change in semantic meaning.
367+
if fqp.last() != Some(&item.name) {
368+
return None;
369+
}
370+
let path =
371+
if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) {
372+
// `#[macro_export]` always exports to the crate root.
373+
tcx.crate_name(defid.krate).to_string()
374+
} else {
375+
if fqp.len() < 2 {
376+
return None;
377+
}
378+
join_with_double_colon(&fqp[..fqp.len() - 1])
379+
};
380+
if path == item.path {
381+
return None;
382+
}
383+
Some(path)
384+
});
385+
}
386+
322387
// Omit the parent path if it is same to that of the prior item.
323388
if lastpath == &item.path {
324389
item.path.clear();
@@ -356,7 +421,7 @@ pub(crate) fn build_index<'tcx>(
356421

357422
struct CrateData<'a> {
358423
items: Vec<&'a IndexItem>,
359-
paths: Vec<(ItemType, Vec<Symbol>)>,
424+
paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
360425
// The String is alias name and the vec is the list of the elements with this alias.
361426
//
362427
// To be noted: the `usize` elements are indexes to `items`.
@@ -374,6 +439,7 @@ pub(crate) fn build_index<'tcx>(
374439
ty: ItemType,
375440
name: Symbol,
376441
path: Option<usize>,
442+
exact_path: Option<usize>,
377443
}
378444

379445
impl Serialize for Paths {
@@ -387,6 +453,10 @@ pub(crate) fn build_index<'tcx>(
387453
if let Some(ref path) = self.path {
388454
seq.serialize_element(path)?;
389455
}
456+
if let Some(ref path) = self.exact_path {
457+
assert!(self.path.is_some());
458+
seq.serialize_element(path)?;
459+
}
390460
seq.end()
391461
}
392462
}
@@ -409,43 +479,94 @@ pub(crate) fn build_index<'tcx>(
409479
mod_paths.insert(&item.path, index);
410480
}
411481
let mut paths = Vec::with_capacity(self.paths.len());
412-
for (ty, path) in &self.paths {
482+
for (ty, path, exact) in &self.paths {
413483
if path.len() < 2 {
414-
paths.push(Paths { ty: *ty, name: path[0], path: None });
484+
paths.push(Paths { ty: *ty, name: path[0], path: None, exact_path: None });
415485
continue;
416486
}
417487
let full_path = join_with_double_colon(&path[..path.len() - 1]);
488+
let full_exact_path = exact
489+
.as_ref()
490+
.filter(|exact| exact.last() == path.last() && exact.len() >= 2)
491+
.map(|exact| join_with_double_colon(&exact[..exact.len() - 1]));
492+
let exact_path = extra_paths.len() + self.items.len();
493+
let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths
494+
.entry(full_exact_path.clone())
495+
{
496+
Entry::Occupied(entry) => *entry.get(),
497+
Entry::Vacant(entry) => {
498+
if let Some(index) = mod_paths.get(&full_exact_path) {
499+
return *index;
500+
}
501+
entry.insert(exact_path);
502+
if !revert_extra_paths.contains_key(&exact_path) {
503+
revert_extra_paths.insert(exact_path, full_exact_path.clone());
504+
}
505+
exact_path
506+
}
507+
});
418508
if let Some(index) = mod_paths.get(&full_path) {
419-
paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) });
509+
paths.push(Paths {
510+
ty: *ty,
511+
name: *path.last().unwrap(),
512+
path: Some(*index),
513+
exact_path,
514+
});
420515
continue;
421516
}
422517
// It means it comes from an external crate so the item and its path will be
423518
// stored into another array.
424519
//
425520
// `index` is put after the last `mod_paths`
426521
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) {
522+
match extra_paths.entry(full_path.clone()) {
431523
Entry::Occupied(entry) => {
432524
paths.push(Paths {
433525
ty: *ty,
434526
name: *path.last().unwrap(),
435527
path: Some(*entry.get()),
528+
exact_path,
436529
});
437530
}
438531
Entry::Vacant(entry) => {
439532
entry.insert(index);
533+
if !revert_extra_paths.contains_key(&index) {
534+
revert_extra_paths.insert(index, full_path);
535+
}
440536
paths.push(Paths {
441537
ty: *ty,
442538
name: *path.last().unwrap(),
443539
path: Some(index),
540+
exact_path,
444541
});
445542
}
446543
}
447544
}
448545

546+
// Direct exports use adjacent arrays for the current crate's items,
547+
// but re-exported exact paths don't.
548+
let mut re_exports = Vec::new();
549+
for (item_index, item) in self.items.iter().enumerate() {
550+
if let Some(exact_path) = item.exact_path.as_ref() {
551+
if let Some(path_index) = mod_paths.get(&exact_path) {
552+
re_exports.push((item_index, *path_index));
553+
} else {
554+
let path_index = extra_paths.len() + self.items.len();
555+
let path_index = match extra_paths.entry(exact_path.clone()) {
556+
Entry::Occupied(entry) => *entry.get(),
557+
Entry::Vacant(entry) => {
558+
entry.insert(path_index);
559+
if !revert_extra_paths.contains_key(&path_index) {
560+
revert_extra_paths.insert(path_index, exact_path.clone());
561+
}
562+
path_index
563+
}
564+
};
565+
re_exports.push((item_index, path_index));
566+
}
567+
}
568+
}
569+
449570
let mut names = Vec::with_capacity(self.items.len());
450571
let mut types = String::with_capacity(self.items.len());
451572
let mut full_paths = Vec::with_capacity(self.items.len());
@@ -501,6 +622,7 @@ pub(crate) fn build_index<'tcx>(
501622
crate_data.serialize_field("f", &functions)?;
502623
crate_data.serialize_field("D", &self.desc_index)?;
503624
crate_data.serialize_field("p", &paths)?;
625+
crate_data.serialize_field("r", &re_exports)?;
504626
crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
505627
crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?;
506628
crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?;

‎src/librustdoc/html/static/js/externs.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,20 +239,27 @@ let FunctionType;
239239
* `doc` contains the description of the crate.
240240
*
241241
* `p` is a list of path/type pairs. It is used for parents and function parameters.
242+
* The first item is the type, the second is the name, the third is the visible path (if any) and
243+
* the fourth is the canonical path used for deduplication (if any).
244+
*
245+
* `r` is the canonical path used for deduplication of re-exported items.
246+
* It is not used for associated items like methods (that's the fourth element
247+
* of `p`) but is used for modules items like free functions.
242248
*
243249
* `c` is an array of item indices that are deprecated.
244250
* @typedef {{
245251
* doc: string,
246252
* a: Object,
247253
* n: Array<string>,
248-
* t: String,
254+
* t: string,
249255
* d: Array<string>,
250-
* q: Array<[Number, string]>,
251-
* i: Array<Number>,
256+
* q: Array<[number, string]>,
257+
* i: Array<number>,
252258
* f: string,
253-
* p: Array<Object>,
254-
* b: Array<[Number, String]>,
255-
* c: Array<Number>
259+
* p: Array<[number, string] | [number, string, number] | [number, string, number, number]>,
260+
* b: Array<[number, String]>,
261+
* c: Array<number>,
262+
* r: Array<[number, number]>,
256263
* }}
257264
*/
258265
let RawSearchIndexCrate;

‎src/librustdoc/html/static/js/search.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const longItemTypes = [
7979

8080
// used for special search precedence
8181
const TY_GENERIC = itemTypes.indexOf("generic");
82+
const TY_IMPORT = itemTypes.indexOf("import");
8283
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
8384

8485
// Hard limit on how deep to recurse into generics when doing type-driven search.
@@ -1324,14 +1325,23 @@ function initSearch(rawSearchIndex) {
13241325
obj.dist = result.dist;
13251326
const res = buildHrefAndPath(obj);
13261327
obj.displayPath = pathSplitter(res[0]);
1327-
obj.fullPath = obj.displayPath + obj.name;
1328-
// To be sure than it some items aren't considered as duplicate.
1329-
obj.fullPath += "|" + obj.ty;
13301328

1329+
// To be sure than it some items aren't considered as duplicate.
1330+
obj.fullPath = res[2] + "|" + obj.ty;
13311331
if (duplicates.has(obj.fullPath)) {
13321332
continue;
13331333
}
1334+
1335+
// Exports are specifically not shown if the items they point at
1336+
// are already in the results.
1337+
if (obj.ty === TY_IMPORT && duplicates.has(res[2])) {
1338+
continue;
1339+
}
1340+
if (duplicates.has(res[2] + "|" + TY_IMPORT)) {
1341+
continue;
1342+
}
13341343
duplicates.add(obj.fullPath);
1344+
duplicates.add(res[2]);
13351345

13361346
obj.href = res[1];
13371347
out.push(obj);
@@ -2085,6 +2095,7 @@ function initSearch(rawSearchIndex) {
20852095
path: item.path,
20862096
descShard: item.descShard,
20872097
descIndex: item.descIndex,
2098+
exactPath: item.exactPath,
20882099
ty: item.ty,
20892100
parent: item.parent,
20902101
type: item.type,
@@ -2538,6 +2549,7 @@ function initSearch(rawSearchIndex) {
25382549
const type = itemTypes[item.ty];
25392550
const name = item.name;
25402551
let path = item.path;
2552+
let exactPath = item.exactPath;
25412553

25422554
if (type === "mod") {
25432555
displayPath = path + "::";
@@ -2559,6 +2571,7 @@ function initSearch(rawSearchIndex) {
25592571
const parentType = itemTypes[myparent.ty];
25602572
let pageType = parentType;
25612573
let pageName = myparent.name;
2574+
exactPath = `${myparent.exactPath}::${myparent.name}`;
25622575

25632576
if (parentType === "primitive") {
25642577
displayPath = myparent.name + "::";
@@ -2587,7 +2600,7 @@ function initSearch(rawSearchIndex) {
25872600
href = ROOT_PATH + item.path.replace(/::/g, "/") +
25882601
"/" + type + "." + name + ".html";
25892602
}
2590-
return [displayPath, href];
2603+
return [displayPath, href, `${exactPath}::${name}`];
25912604
}
25922605

25932606
function pathSplitter(path) {
@@ -2980,6 +2993,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
29802993
id: pathIndex,
29812994
ty: TY_GENERIC,
29822995
path: null,
2996+
exactPath: null,
29832997
generics,
29842998
bindings,
29852999
};
@@ -2989,6 +3003,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
29893003
id: null,
29903004
ty: null,
29913005
path: null,
3006+
exactPath: null,
29923007
generics,
29933008
bindings,
29943009
};
@@ -2998,6 +3013,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
29983013
id: buildTypeMapIndex(item.name, isAssocType),
29993014
ty: item.ty,
30003015
path: item.path,
3016+
exactPath: item.exactPath,
30013017
generics,
30023018
bindings,
30033019
};
@@ -3453,6 +3469,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
34533469
path: "",
34543470
descShard,
34553471
descIndex,
3472+
exactPath: "",
3473+
desc: crateCorpus.doc,
34563474
parent: undefined,
34573475
type: null,
34583476
id,
@@ -3478,6 +3496,9 @@ ${item.displayPath}<span class="${type}">${name}</span>\
34783496
// i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
34793497
// 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
34803498
const itemPaths = new Map(crateCorpus.q);
3499+
// An array of [(Number) item index, (Number) path index]
3500+
// Used to de-duplicate inlined and re-exported stuff
3501+
const itemReexports = new Map(crateCorpus.r);
34813502
// an array of (Number) the parent path index + 1 to `paths`, or 0 if none
34823503
const itemParentIdxs = crateCorpus.i;
34833504
// a map Number, string for impl disambiguators
@@ -3511,9 +3532,10 @@ ${item.displayPath}<span class="${type}">${name}</span>\
35113532
path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
35123533
lastPath = path;
35133534
}
3535+
const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path;
35143536

3515-
lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
3516-
paths[i] = {ty: ty, name: name, path: path};
3537+
lowercasePaths.push({ty, name: name.toLowerCase(), path, exactPath});
3538+
paths[i] = {ty, name, path, exactPath};
35173539
}
35183540

35193541
// convert `item*` into an object form, and construct word indices.
@@ -3572,6 +3594,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
35723594
path,
35733595
descShard,
35743596
descIndex,
3597+
exactPath: itemReexports.has(i) ? itemPaths.get(itemReexports.get(i)) : path,
35753598
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
35763599
type,
35773600
id,

‎tests/rustdoc-js-std/vec-new.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,47 @@ const EXPECTED = [
33
'query': 'Vec::new',
44
'others': [
55
{ 'path': 'std::vec::Vec', 'name': 'new' },
6-
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
76
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
8-
{ 'path': 'alloc::vec::Vec', 'name': 'new_in' },
7+
],
8+
},
9+
{
10+
'query': 'prelude::vec',
11+
'others': [
12+
{ 'path': 'std::prelude::rust_2024', 'name': 'Vec' },
913
],
1014
},
1115
{
1216
'query': 'Vec new',
1317
'others': [
1418
{ 'path': 'std::vec::Vec', 'name': 'new' },
15-
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
1619
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
20+
],
21+
},
22+
{
23+
'query': 'std::Vec::new',
24+
'others': [
25+
{ 'path': 'std::vec::Vec', 'name': 'new' },
26+
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
27+
],
28+
},
29+
{
30+
'query': 'std Vec new',
31+
'others': [
32+
{ 'path': 'std::vec::Vec', 'name': 'new' },
33+
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
34+
],
35+
},
36+
{
37+
'query': 'alloc::Vec::new',
38+
'others': [
39+
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
40+
{ 'path': 'alloc::vec::Vec', 'name': 'new_in' },
41+
],
42+
},
43+
{
44+
'query': 'alloc Vec new',
45+
'others': [
46+
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
1747
{ 'path': 'alloc::vec::Vec', 'name': 'new_in' },
1848
],
1949
},
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#[macro_use]
2+
mod hidden_macro_module {
3+
#[macro_export]
4+
macro_rules! vec {
5+
() => {};
6+
}
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// exact-check
2+
3+
const EXPECTED = [
4+
{
5+
'query': 'vec',
6+
'others': [
7+
{ 'path': 'foo', 'name': 'vec', 'exactPath': 'macro_in_module' },
8+
{ 'path': 'foo', 'name': 'myspecialvec', 'exactPath': 'foo' },
9+
],
10+
},
11+
];
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ aux-crate: macro_in_module=macro-in-module.rs
2+
#![crate_name="foo"]
3+
extern crate macro_in_module;
4+
5+
// Test case based on the relationship between alloc and std.
6+
#[doc(inline)]
7+
pub use macro_in_module::vec;
8+
9+
#[macro_use]
10+
mod hidden_macro_module {
11+
#[macro_export]
12+
macro_rules! myspecialvec {
13+
() => {};
14+
}
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// exact-check
2+
3+
const EXPECTED = [
4+
{
5+
'query': 'Subscriber dostuff',
6+
'others': [
7+
{ 'path': 'foo::fmt::Subscriber', 'name': 'dostuff' },
8+
],
9+
},
10+
{
11+
'query': 'AnotherOne dostuff',
12+
'others': [
13+
{ 'path': 'foo::AnotherOne', 'name': 'dostuff' },
14+
],
15+
},
16+
];
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This test enforces that the (renamed) reexports are present in the search results.
2+
#![crate_name="foo"]
3+
4+
pub mod fmt {
5+
pub struct Subscriber;
6+
impl Subscriber {
7+
pub fn dostuff(&self) {}
8+
}
9+
}
10+
mod foo {
11+
pub struct AnotherOne;
12+
impl AnotherOne {
13+
pub fn dostuff(&self) {}
14+
}
15+
}
16+
17+
pub use foo::AnotherOne;
18+
pub use fmt::Subscriber;

‎tests/rustdoc-js/reexport-dedup.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// exact-check
2+
3+
const EXPECTED = [
4+
{
5+
'query': 'Subscriber',
6+
'others': [
7+
{ 'path': 'foo', 'name': 'Subscriber' },
8+
],
9+
},
10+
{
11+
'query': 'fmt Subscriber',
12+
'others': [
13+
{ 'path': 'foo::fmt', 'name': 'Subscriber' },
14+
],
15+
},
16+
{
17+
'query': 'AnotherOne',
18+
'others': [
19+
{ 'path': 'foo', 'name': 'AnotherOne' },
20+
],
21+
},
22+
];

‎tests/rustdoc-js/reexport-dedup.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// This test enforces that the (renamed) reexports are present in the search results.
2+
#![crate_name="foo"]
3+
4+
pub mod fmt {
5+
pub struct Subscriber;
6+
}
7+
mod foo {
8+
pub struct AnotherOne;
9+
}
10+
11+
pub use foo::AnotherOne;
12+
pub use fmt::Subscriber;

‎tests/rustdoc-js/reexport.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// This test enforces that the (renamed) reexports are present in the search results.
2+
// This is a DWIM case, since renaming the export probably means the intent is also different.
3+
// For the de-duplication case of exactly the same name, see reexport-dedup
24

35
pub mod fmt {
46
pub struct Subscriber;

0 commit comments

Comments
 (0)
Please sign in to comment.