Rollup merge of #141658 - lolbinarycat:rustdoc-search-stability-rank-138067, r=GuillaumeGomez

rustdoc search: prefer stable items in search results

fixes https://github.com/rust-lang/rust/issues/138067

this does add a new field to the search index, but since we're only listing unstable items instead of adding a boolean flag to every item, it should only increase the search index size of sysroot crates, since those are the only ones using the `staged_api` feature, at least as far as the rust project is concerned.
This commit is contained in:
Stuart Cook 2025-08-09 13:58:42 +10:00 committed by GitHub
commit 48f5929604
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 65 additions and 4 deletions

View file

@ -602,6 +602,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
search_type,
aliases,
deprecation,
is_unstable: item.stability(tcx).map(|x| x.is_unstable()).unwrap_or(false),
};
cache.search_index.push(index_item);
}

View file

@ -139,6 +139,7 @@ pub(crate) struct IndexItem {
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
pub(crate) is_unstable: bool,
}
/// A type used for the search index.

View file

@ -93,6 +93,7 @@ pub(crate) fn build_index(
),
aliases: item.attrs.get_doc_aliases(),
deprecation: item.deprecation(tcx),
is_unstable: item.stability(tcx).is_some_and(|x| x.is_unstable()),
});
}
}
@ -655,6 +656,7 @@ pub(crate) fn build_index(
let mut parents_backref_queue = VecDeque::new();
let mut functions = String::with_capacity(self.items.len());
let mut deprecated = Vec::with_capacity(self.items.len());
let mut unstable = Vec::with_capacity(self.items.len());
let mut type_backref_queue = VecDeque::new();
@ -711,6 +713,9 @@ pub(crate) fn build_index(
// bitmasks always use 1-indexing for items, with 0 as the crate itself
deprecated.push(u32::try_from(index + 1).unwrap());
}
if item.is_unstable {
unstable.push(u32::try_from(index + 1).unwrap());
}
}
for (index, path) in &revert_extra_paths {
@ -749,6 +754,7 @@ pub(crate) fn build_index(
crate_data.serialize_field("r", &re_exports)?;
crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?;
crate_data.serialize_field("u", &bitmap_to_string(&unstable))?;
crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?;
crate_data.serialize_field("P", &param_names)?;
if has_aliases {

View file

@ -449,6 +449,8 @@ declare namespace rustdoc {
* of `p`) but is used for modules items like free functions.
*
* `c` is an array of item indices that are deprecated.
*
* `u` is an array of item indices that are unstable.
*/
type RawSearchIndexCrate = {
doc: string,
@ -463,6 +465,7 @@ declare namespace rustdoc {
p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>,
b: Array<[number, String]>,
c: string,
u: string,
r: Array<[number, number]>,
P: Array<[number, string]>,
};

View file

@ -1464,6 +1464,11 @@ class DocSearch {
* @type {Map<String, RoaringBitmap>}
*/
this.searchIndexEmptyDesc = new Map();
/**
* @type {Map<String, RoaringBitmap>}
*/
this.searchIndexUnstable = new Map();
/**
* @type {Uint32Array}
*/
@ -2052,9 +2057,10 @@ class DocSearch {
};
const descShardList = [descShard];
// Deprecated items and items with no description
// Deprecated and unstable items and items with no description
this.searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c));
this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e));
this.searchIndexUnstable.set(crate, new RoaringBitmap(crateCorpus.u));
let descIndex = 0;
/**
@ -3326,6 +3332,25 @@ class DocSearch {
return a - b;
}
// sort unstable items later
// FIXME: there is some doubt if this is the most effecient way to implement this.
// alternative options include:
// * put is_unstable on each item when the index is built.
// increases memory usage but avoids a hashmap lookup.
// * put is_unstable on each item before sorting.
// better worst case performance but worse average case performance.
a = Number(
// @ts-expect-error
this.searchIndexUnstable.get(aaa.item.crate).contains(aaa.item.bitIndex),
);
b = Number(
// @ts-expect-error
this.searchIndexUnstable.get(bbb.item.crate).contains(bbb.item.bitIndex),
);
if (a !== b) {
return a - b;
}
// sort by crate (current crate comes first)
a = Number(aaa.item.crate !== preferredCrate);
b = Number(bbb.item.crate !== preferredCrate);

View file

@ -3,9 +3,9 @@ const EXPECTED = [
{
'query': 'generic:T -> generic:U',
'others': [
{ 'path': 'core::mem', 'name': 'transmute' },
{ 'path': 'core::intrinsics::simd', 'name': 'simd_as' },
{ 'path': 'core::intrinsics::simd', 'name': 'simd_cast' },
{ 'path': 'core::mem', 'name': 'transmute' },
],
},
];

View file

@ -6,9 +6,9 @@ const EXPECTED = [
// should-fail tag and the search query below:
'query': 'generic:T -> generic:T',
'others': [
{ 'path': 'std::mem', 'name': 'transmute' },
{ 'path': 'std::intrinsics::simd', 'name': 'simd_as' },
{ 'path': 'std::intrinsics::simd', 'name': 'simd_cast' },
{ 'path': 'std::mem', 'name': 'transmute' },
],
},
];

View file

@ -5,9 +5,9 @@ const EXPECTED = [
// should-fail tag and the search query below:
'query': 'generic:T -> generic:U',
'others': [
{ 'path': 'std::mem', 'name': 'transmute' },
{ 'path': 'std::intrinsics::simd', 'name': 'simd_as' },
{ 'path': 'std::intrinsics::simd', 'name': 'simd_cast' },
{ 'path': 'std::mem', 'name': 'transmute' },
],
},
];

View file

@ -0,0 +1,9 @@
const EXPECTED = [
{
'query': 'foo',
'others': [
{"path": "sort_stability::old", "name": "foo"},
{"path": "sort_stability::new", "name": "foo"},
],
},
];

View file

@ -0,0 +1,16 @@
#![feature(staged_api)]
#![stable(feature = "foo_lib", since = "1.0.0")]
#[stable(feature = "old_foo", since = "1.0.1")]
pub mod old {
/// Old, stable foo
#[stable(feature = "old_foo", since = "1.0.1")]
pub fn foo() {}
}
#[unstable(feature = "new_foo", issue = "none")]
pub mod new {
/// New, unstable foo
#[unstable(feature = "new_foo", issue = "none")]
pub fn foo() {}
}