about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbinarycat <binarycat@envs.net>2025-05-27 12:42:25 -0500
committerbinarycat <binarycat@envs.net>2025-06-10 12:56:58 -0500
commit1140e90074b0cbcfdea8535e4b51877e2838227e (patch)
treef3f3b850ab68799f03620f47e021b9e4163bb398
parentccf3198de316b488ee17441935182e9d5292b4d3 (diff)
downloadrust-1140e90074b0cbcfdea8535e4b51877e2838227e.tar.gz
rust-1140e90074b0cbcfdea8535e4b51877e2838227e.zip
rustdoc search: prefer stable items in search results
fixes https://github.com/rust-lang/rust/issues/138067
-rw-r--r--src/librustdoc/formats/cache.rs1
-rw-r--r--src/librustdoc/html/render/mod.rs9
-rw-r--r--src/librustdoc/html/render/search_index.rs6
-rw-r--r--src/librustdoc/html/static/js/rustdoc.d.ts5
-rw-r--r--src/librustdoc/html/static/js/search.js21
-rw-r--r--tests/rustdoc-js-std/core-transmute.js2
-rw-r--r--tests/rustdoc-js-std/transmute-fail.js2
-rw-r--r--tests/rustdoc-js-std/transmute.js2
-rw-r--r--tests/rustdoc-js/sort-stability.js9
-rw-r--r--tests/rustdoc-js/sort-stability.rs16
10 files changed, 67 insertions, 6 deletions
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 4989bd718c9..c3251ca7806 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -586,6 +586,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
         search_type,
         aliases,
         deprecation,
+        stability: item.stability(tcx),
     };
     cache.search_index.push(index_item);
 }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 66d5aafa3c1..29917bb0fca 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -50,7 +50,8 @@ use std::{fs, str};
 use askama::Template;
 use itertools::Either;
 use rustc_attr_data_structures::{
-    ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince,
+    ConstStability, DeprecatedSince, Deprecation, RustcVersion, Stability, StabilityLevel,
+    StableSince,
 };
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::Mutability;
@@ -140,6 +141,12 @@ pub(crate) struct IndexItem {
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
     pub(crate) deprecation: Option<Deprecation>,
+    pub(crate) stability: Option<Stability>,
+}
+impl IndexItem {
+    fn is_unstable(&self) -> bool {
+        matches!(&self.stability, Some(Stability { level: StabilityLevel::Unstable { .. }, .. }))
+    }
 }
 
 /// A type used for the search index.
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index aff8684ee3a..6635aa02e97 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -93,6 +93,7 @@ pub(crate) fn build_index(
                 ),
                 aliases: item.attrs.get_doc_aliases(),
                 deprecation: item.deprecation(tcx),
+                stability: item.stability(tcx),
             });
         }
     }
@@ -642,6 +643,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();
 
@@ -698,6 +700,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 {
@@ -736,6 +741,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 {
diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts
index 0d2e19e019f..8110f1f5a5e 100644
--- a/src/librustdoc/html/static/js/rustdoc.d.ts
+++ b/src/librustdoc/html/static/js/rustdoc.d.ts
@@ -129,7 +129,7 @@ declare namespace rustdoc {
 
     /**
      * A single parsed "atom" in a search query. For example,
-     * 
+     *
      *     std::fmt::Formatter, Write -> Result<()>
      *     ┏━━━━━━━━━━━━━━━━━━  ┌────    ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
      *     ┃                    │        ┗ QueryElement {        ┊
@@ -442,6 +442,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,
@@ -456,6 +458,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]>,
     };
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index dce5fddb317..da15433622a 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1464,6 +1464,11 @@ class DocSearch {
          */
         this.searchIndexEmptyDesc = new Map();
         /**
+         * @type {Map<String, RoaringBitmap>}
+         */
+        this.searchIndexUnstable = new Map();
+
+        /**
          *  @type {Uint32Array}
          */
         this.functionTypeFingerprint = new Uint32Array(0);
@@ -2048,9 +2053,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;
 
             /**
@@ -3284,6 +3290,19 @@ class DocSearch {
                     return a - b;
                 }
 
+                // sort unstable items later
+                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);
diff --git a/tests/rustdoc-js-std/core-transmute.js b/tests/rustdoc-js-std/core-transmute.js
index 8c9910a32d7..b15f398902c 100644
--- a/tests/rustdoc-js-std/core-transmute.js
+++ b/tests/rustdoc-js-std/core-transmute.js
@@ -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' },
         ],
     },
 ];
diff --git a/tests/rustdoc-js-std/transmute-fail.js b/tests/rustdoc-js-std/transmute-fail.js
index ddfb2761948..459e8dc3f00 100644
--- a/tests/rustdoc-js-std/transmute-fail.js
+++ b/tests/rustdoc-js-std/transmute-fail.js
@@ -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' },
         ],
     },
 ];
diff --git a/tests/rustdoc-js-std/transmute.js b/tests/rustdoc-js-std/transmute.js
index f52e0ab1436..a85b02e2994 100644
--- a/tests/rustdoc-js-std/transmute.js
+++ b/tests/rustdoc-js-std/transmute.js
@@ -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' },
         ],
     },
 ];
diff --git a/tests/rustdoc-js/sort-stability.js b/tests/rustdoc-js/sort-stability.js
new file mode 100644
index 00000000000..8c095619a08
--- /dev/null
+++ b/tests/rustdoc-js/sort-stability.js
@@ -0,0 +1,9 @@
+const EXPECTED = [
+    {
+        'query': 'foo',
+        'others': [
+            {"path": "sort_stability::old", "name": "foo"},
+            {"path": "sort_stability::new", "name": "foo"},
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/sort-stability.rs b/tests/rustdoc-js/sort-stability.rs
new file mode 100644
index 00000000000..68662bb3aab
--- /dev/null
+++ b/tests/rustdoc-js/sort-stability.rs
@@ -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() {}
+}