about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_passes/messages.ftl3
-rw-r--r--compiler/rustc_passes/src/check_attr.rs27
-rw-r--r--compiler/rustc_passes/src/errors.rs7
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/alloc/src/boxed.rs1
-rw-r--r--library/alloc/src/rc.rs1
-rw-r--r--library/alloc/src/sync.rs1
-rw-r--r--library/core/src/future/future.rs1
-rw-r--r--library/core/src/option.rs1
-rw-r--r--library/core/src/result.rs1
-rw-r--r--src/doc/rustdoc/src/read-documentation/search.md12
-rw-r--r--src/librustdoc/html/render/search_index.rs74
-rw-r--r--src/librustdoc/html/static/js/search.js352
-rw-r--r--tests/rustdoc-gui/search-about-this-result.goml4
-rw-r--r--tests/rustdoc-js-std/bufread-fill-buf.js11
-rw-r--r--tests/rustdoc-js-std/option-type-signatures.js31
-rw-r--r--tests/rustdoc-js-std/vec-type-signatures.js12
-rw-r--r--tests/rustdoc-js/assoc-type-backtrack.js48
-rw-r--r--tests/rustdoc-js/assoc-type-backtrack.rs4
-rw-r--r--tests/rustdoc-js/assoc-type-unbound.js4
-rw-r--r--tests/rustdoc-js/assoc-type.rs12
-rw-r--r--tests/rustdoc-js/generics-impl.js8
-rw-r--r--tests/rustdoc-js/generics-impl.rs4
-rw-r--r--tests/rustdoc-js/generics-match-ambiguity-no-unbox.js68
-rw-r--r--tests/rustdoc-js/generics-match-ambiguity-no-unbox.rs18
-rw-r--r--tests/rustdoc-js/generics-match-ambiguity.js12
-rw-r--r--tests/rustdoc-js/generics-match-ambiguity.rs6
-rw-r--r--tests/rustdoc-js/generics-nested.js5
-rw-r--r--tests/rustdoc-js/generics-unbox.js4
-rw-r--r--tests/rustdoc-js/generics-unbox.rs8
-rw-r--r--tests/rustdoc-js/generics.js16
-rw-r--r--tests/rustdoc-js/hof.js25
-rw-r--r--tests/rustdoc-js/looks-like-rustc-interner.js18
-rw-r--r--tests/rustdoc-js/nested-unboxed.js9
-rw-r--r--tests/rustdoc-js/nested-unboxed.rs3
-rw-r--r--tests/rustdoc-js/reference.js15
-rw-r--r--tests/rustdoc-js/tuple-unit.js4
-rw-r--r--tests/ui/feature-gates/feature-gate-rustdoc_internals.rs3
-rw-r--r--tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr12
40 files changed, 630 insertions, 217 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index d646150a620..396aac34515 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -204,6 +204,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                     "meant for internal use only" {
                         keyword => rustdoc_internals
                         fake_variadic => rustdoc_internals
+                        search_unbox => rustdoc_internals
                     }
                 );
             }
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index e5e70ba2033..3536b3fc4cd 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -234,6 +234,9 @@ passes_doc_masked_only_extern_crate =
 passes_doc_rust_logo =
     the `#[doc(rust_logo)]` attribute is used for Rust branding
 
+passes_doc_search_unbox_invalid =
+    `#[doc(search_unbox)]` should be used on generic structs and enums
+
 passes_doc_test_literal = `#![doc(test(...)]` does not take a literal
 
 passes_doc_test_takes_list =
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 0a2926c0404..4cf523b0556 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -16,8 +16,8 @@ use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, B
 use rustc_hir::def_id::LocalModDefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{
-    self as hir, self, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, Item, ItemKind,
-    MethodKind, Safety, Target, TraitItem,
+    self as hir, self, AssocItemKind, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId,
+    Item, ItemKind, MethodKind, Safety, Target, TraitItem,
 };
 use rustc_macros::LintDiagnostic;
 use rustc_middle::hir::nested_filter;
@@ -940,6 +940,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
+    fn check_doc_search_unbox(&self, meta: &MetaItemInner, hir_id: HirId) {
+        let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else {
+            self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
+            return;
+        };
+        match item.kind {
+            ItemKind::Enum(_, generics) | ItemKind::Struct(_, generics)
+                if generics.params.len() != 0 => {}
+            ItemKind::Trait(_, _, generics, _, items)
+                if generics.params.len() != 0
+                    || items.iter().any(|item| matches!(item.kind, AssocItemKind::Type)) => {}
+            _ => {
+                self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
+            }
+        }
+    }
+
     /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes.
     ///
     /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
@@ -1152,6 +1169,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             }
                         }
 
+                        sym::search_unbox => {
+                            if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
+                                self.check_doc_search_unbox(meta, hir_id);
+                            }
+                        }
+
                         sym::test => {
                             if self.check_attr_crate_level(attr, meta, hir_id) {
                                 self.check_test_attr(meta, hir_id);
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 8bd767c1243..494be13b86a 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -245,6 +245,13 @@ pub(crate) struct DocKeywordOnlyImpl {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_doc_search_unbox_invalid)]
+pub(crate) struct DocSearchUnboxInvalid {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_doc_inline_conflict)]
 #[help]
 pub(crate) struct DocKeywordConflict {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 890c4fdafef..b2e8adb0604 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1762,6 +1762,7 @@ symbols! {
         saturating_add,
         saturating_div,
         saturating_sub,
+        search_unbox,
         select_unpredictable,
         self_in_typedefs,
         self_struct_ctor,
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index e4956c7c53c..c5024a05ed6 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -225,6 +225,7 @@ pub use thin::ThinBox;
 #[fundamental]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_insignificant_dtor]
+#[cfg_attr(not(bootstrap), doc(search_unbox))]
 // The declaration of the `Box` struct must be kept in sync with the
 // compiler or ICEs will happen.
 pub struct Box<
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index fc8646e96d9..06addb4edb2 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -307,6 +307,7 @@ fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout {
 /// `value.get_mut()`. This avoids conflicts with methods of the inner type `T`.
 ///
 /// [get_mut]: Rc::get_mut
+#[cfg_attr(not(bootstrap), doc(search_unbox))]
 #[cfg_attr(not(test), rustc_diagnostic_item = "Rc")]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_insignificant_dtor]
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index 98a2fe24257..e6a2cf009ce 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -235,6 +235,7 @@ macro_rules! acquire {
 /// counting in general.
 ///
 /// [rc_examples]: crate::rc#examples
+#[cfg_attr(not(bootstrap), doc(search_unbox))]
 #[cfg_attr(not(test), rustc_diagnostic_item = "Arc")]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_insignificant_dtor]
diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs
index ca1c2d1ca1f..234914c20fc 100644
--- a/library/core/src/future/future.rs
+++ b/library/core/src/future/future.rs
@@ -25,6 +25,7 @@ use crate::task::{Context, Poll};
 /// [`async`]: ../../std/keyword.async.html
 /// [`Waker`]: crate::task::Waker
 #[doc(notable_trait)]
+#[cfg_attr(not(bootstrap), doc(search_unbox))]
 #[must_use = "futures do nothing unless you `.await` or poll them"]
 #[stable(feature = "futures_api", since = "1.36.0")]
 #[lang = "future_trait"]
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index 2aa4f172368..46187938622 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -563,6 +563,7 @@ use crate::pin::Pin;
 use crate::{cmp, convert, hint, mem, slice};
 
 /// The `Option` type. See [the module level documentation](self) for more.
+#[cfg_attr(not(bootstrap), doc(search_unbox))]
 #[derive(Copy, Eq, Debug, Hash)]
 #[rustc_diagnostic_item = "Option"]
 #[lang = "Option"]
diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index 330d1eb14ed..b450123c5aa 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -520,6 +520,7 @@ use crate::{convert, fmt, hint};
 /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).
 ///
 /// See the [module documentation](self) for details.
+#[cfg_attr(not(bootstrap), doc(search_unbox))]
 #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
 #[must_use = "this `Result` may be an `Err` variant, which should be handled"]
 #[rustc_diagnostic_item = "Result"]
diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md
index e912ca0fe5b..718d2201c3a 100644
--- a/src/doc/rustdoc/src/read-documentation/search.md
+++ b/src/doc/rustdoc/src/read-documentation/search.md
@@ -130,29 +130,31 @@ pub trait MyTrait {
 /// This function can be found using the following search queries:
 ///
 ///     MyTrait<First=u8, Second=u32> -> bool
-///     MyTrait<u32, First=u8> -> bool
 ///     MyTrait<Second=u32> -> bool
-///     MyTrait<u32, u8> -> bool
 ///
 /// The following queries, however, will *not* match it:
 ///
 ///     MyTrait<First=u32> -> bool
 ///     MyTrait<u32, u32> -> bool
+///     MyTrait<u32, First=u8> -> bool
+///     MyTrait<u32, u8> -> bool
 pub fn my_fn(x: impl MyTrait<First=u8, Second=u32>) -> bool { true }
 ```
 
-Generics and function parameters are order-agnostic, but sensitive to nesting
+Function parameters are order-agnostic, but sensitive to nesting
 and number of matches. For example, a function with the signature
 `fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
 will match these queries:
 
 * `&mut Read -> Result<Vec<u8>, Error>`
 * `Read -> Result<Vec<u8>, Error>`
-* `Read -> Result<Error, Vec>`
 * `Read -> Result<Vec<u8>>`
 * `Read -> u8`
 
-But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
+But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`,
+because those are nested incorrectly, and it does not match
+`Result<Error, Vec<u8>>` or `Result<Error>`, because those are
+in the wrong order.
 
 To search for a function that accepts a function as a parameter,
 like `Iterator::all`, wrap the nested signature in parenthesis,
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index ee2fc1f44b3..f91fdfa1fb5 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -13,8 +13,8 @@ use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
 use thin_vec::ThinVec;
 use tracing::instrument;
 
-use crate::clean;
 use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
+use crate::clean::{self, utils};
 use crate::formats::cache::{Cache, OrphanImplItem};
 use crate::formats::item_type::ItemType;
 use crate::html::format::join_with_double_colon;
@@ -66,7 +66,7 @@ pub(crate) fn build_index<'tcx>(
     let mut associated_types = FxHashMap::default();
 
     // item type, display path, re-exported internal path
-    let mut crate_paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)> = vec![];
+    let mut crate_paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)> = vec![];
 
     // Attach all orphan items to the type's definition if the type
     // has since been learned.
@@ -132,10 +132,11 @@ pub(crate) fn build_index<'tcx>(
             map: &mut FxHashMap<F, isize>,
             itemid: F,
             lastpathid: &mut isize,
-            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>,
             item_type: ItemType,
             path: &[Symbol],
             exact_path: Option<&[Symbol]>,
+            search_unbox: bool,
         ) -> RenderTypeId {
             match map.entry(itemid) {
                 Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()),
@@ -147,6 +148,7 @@ pub(crate) fn build_index<'tcx>(
                         item_type,
                         path.to_vec(),
                         exact_path.map(|path| path.to_vec()),
+                        search_unbox,
                     ));
                     RenderTypeId::Index(pathid)
                 }
@@ -160,9 +162,21 @@ pub(crate) fn build_index<'tcx>(
             primitives: &mut FxHashMap<Symbol, isize>,
             associated_types: &mut FxHashMap<Symbol, isize>,
             lastpathid: &mut isize,
-            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>,
+            tcx: TyCtxt<'_>,
         ) -> Option<RenderTypeId> {
+            use crate::clean::PrimitiveType;
             let Cache { ref paths, ref external_paths, ref exact_paths, .. } = *cache;
+            let search_unbox = match id {
+                RenderTypeId::Mut => false,
+                RenderTypeId::DefId(defid) => utils::has_doc_flag(tcx, defid, sym::search_unbox),
+                RenderTypeId::Primitive(PrimitiveType::Reference | PrimitiveType::Tuple) => true,
+                RenderTypeId::Primitive(..) => false,
+                RenderTypeId::AssociatedType(..) => false,
+                // this bool is only used by `insert_into_map`, so it doesn't matter what we set here
+                // because Index means we've already inserted into the map
+                RenderTypeId::Index(_) => false,
+            };
             match id {
                 RenderTypeId::Mut => Some(insert_into_map(
                     primitives,
@@ -172,6 +186,7 @@ pub(crate) fn build_index<'tcx>(
                     ItemType::Keyword,
                     &[kw::Mut],
                     None,
+                    search_unbox,
                 )),
                 RenderTypeId::DefId(defid) => {
                     if let Some(&(ref fqp, item_type)) =
@@ -195,6 +210,7 @@ pub(crate) fn build_index<'tcx>(
                             item_type,
                             fqp,
                             exact_fqp.map(|x| &x[..]).filter(|exact_fqp| exact_fqp != fqp),
+                            search_unbox,
                         ))
                     } else {
                         None
@@ -210,6 +226,7 @@ pub(crate) fn build_index<'tcx>(
                         ItemType::Primitive,
                         &[sym],
                         None,
+                        search_unbox,
                     ))
                 }
                 RenderTypeId::Index(_) => Some(id),
@@ -221,6 +238,7 @@ pub(crate) fn build_index<'tcx>(
                     ItemType::AssocType,
                     &[sym],
                     None,
+                    search_unbox,
                 )),
             }
         }
@@ -232,7 +250,8 @@ pub(crate) fn build_index<'tcx>(
             primitives: &mut FxHashMap<Symbol, isize>,
             associated_types: &mut FxHashMap<Symbol, isize>,
             lastpathid: &mut isize,
-            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>,
+            tcx: TyCtxt<'_>,
         ) {
             if let Some(generics) = &mut ty.generics {
                 for item in generics {
@@ -244,6 +263,7 @@ pub(crate) fn build_index<'tcx>(
                         associated_types,
                         lastpathid,
                         crate_paths,
+                        tcx,
                     );
                 }
             }
@@ -257,6 +277,7 @@ pub(crate) fn build_index<'tcx>(
                         associated_types,
                         lastpathid,
                         crate_paths,
+                        tcx,
                     );
                     let Some(converted_associated_type) = converted_associated_type else {
                         return false;
@@ -271,6 +292,7 @@ pub(crate) fn build_index<'tcx>(
                             associated_types,
                             lastpathid,
                             crate_paths,
+                            tcx,
                         );
                     }
                     true
@@ -288,6 +310,7 @@ pub(crate) fn build_index<'tcx>(
                 associated_types,
                 lastpathid,
                 crate_paths,
+                tcx,
             );
         }
         if let Some(search_type) = &mut item.search_type {
@@ -300,6 +323,7 @@ pub(crate) fn build_index<'tcx>(
                     &mut associated_types,
                     &mut lastpathid,
                     &mut crate_paths,
+                    tcx,
                 );
             }
             for item in &mut search_type.output {
@@ -311,6 +335,7 @@ pub(crate) fn build_index<'tcx>(
                     &mut associated_types,
                     &mut lastpathid,
                     &mut crate_paths,
+                    tcx,
                 );
             }
             for constraint in &mut search_type.where_clause {
@@ -323,6 +348,7 @@ pub(crate) fn build_index<'tcx>(
                         &mut associated_types,
                         &mut lastpathid,
                         &mut crate_paths,
+                        tcx,
                     );
                 }
             }
@@ -350,7 +376,12 @@ pub(crate) fn build_index<'tcx>(
                                 .filter(|exact_fqp| {
                                     exact_fqp.last() == Some(&item.name) && *exact_fqp != fqp
                                 });
-                            crate_paths.push((short, fqp.clone(), exact_fqp.cloned()));
+                            crate_paths.push((
+                                short,
+                                fqp.clone(),
+                                exact_fqp.cloned(),
+                                utils::has_doc_flag(tcx, defid, sym::search_unbox),
+                            ));
                             Some(pathid)
                         } else {
                             None
@@ -431,7 +462,7 @@ pub(crate) fn build_index<'tcx>(
 
     struct CrateData<'a> {
         items: Vec<&'a IndexItem>,
-        paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
+        paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>,
         // The String is alias name and the vec is the list of the elements with this alias.
         //
         // To be noted: the `usize` elements are indexes to `items`.
@@ -450,6 +481,7 @@ pub(crate) fn build_index<'tcx>(
         name: Symbol,
         path: Option<usize>,
         exact_path: Option<usize>,
+        search_unbox: bool,
     }
 
     impl Serialize for Paths {
@@ -467,6 +499,15 @@ pub(crate) fn build_index<'tcx>(
                 assert!(self.path.is_some());
                 seq.serialize_element(path)?;
             }
+            if self.search_unbox {
+                if self.path.is_none() {
+                    seq.serialize_element(&None::<u8>)?;
+                }
+                if self.exact_path.is_none() {
+                    seq.serialize_element(&None::<u8>)?;
+                }
+                seq.serialize_element(&1)?;
+            }
             seq.end()
         }
     }
@@ -489,9 +530,15 @@ pub(crate) fn build_index<'tcx>(
                 mod_paths.insert(&item.path, index);
             }
             let mut paths = Vec::with_capacity(self.paths.len());
-            for (ty, path, exact) in &self.paths {
+            for &(ty, ref path, ref exact, search_unbox) in &self.paths {
                 if path.len() < 2 {
-                    paths.push(Paths { ty: *ty, name: path[0], path: None, exact_path: None });
+                    paths.push(Paths {
+                        ty,
+                        name: path[0],
+                        path: None,
+                        exact_path: None,
+                        search_unbox,
+                    });
                     continue;
                 }
                 let full_path = join_with_double_colon(&path[..path.len() - 1]);
@@ -517,10 +564,11 @@ pub(crate) fn build_index<'tcx>(
                 });
                 if let Some(index) = mod_paths.get(&full_path) {
                     paths.push(Paths {
-                        ty: *ty,
+                        ty,
                         name: *path.last().unwrap(),
                         path: Some(*index),
                         exact_path,
+                        search_unbox,
                     });
                     continue;
                 }
@@ -532,10 +580,11 @@ pub(crate) fn build_index<'tcx>(
                 match extra_paths.entry(full_path.clone()) {
                     Entry::Occupied(entry) => {
                         paths.push(Paths {
-                            ty: *ty,
+                            ty,
                             name: *path.last().unwrap(),
                             path: Some(*entry.get()),
                             exact_path,
+                            search_unbox,
                         });
                     }
                     Entry::Vacant(entry) => {
@@ -544,10 +593,11 @@ pub(crate) fn build_index<'tcx>(
                             revert_extra_paths.insert(index, full_path);
                         }
                         paths.push(Paths {
-                            ty: *ty,
+                            ty,
                             name: *path.last().unwrap(),
                             path: Some(index),
                             exact_path,
+                            search_unbox,
                         });
                     }
                 }
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 0458b81d352..d269c9322a3 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1327,6 +1327,7 @@ class DocSearch {
                 exactPath: null,
                 generics,
                 bindings,
+                unboxFlag: true,
             };
         } else if (pathIndex === 0) {
             // `0` is used as a sentinel because it's fewer bytes than `null`
@@ -1338,6 +1339,7 @@ class DocSearch {
                 exactPath: null,
                 generics,
                 bindings,
+                unboxFlag: true,
             };
         } else {
             const item = lowercasePaths[pathIndex - 1];
@@ -1353,6 +1355,7 @@ class DocSearch {
                 exactPath: item.exactPath,
                 generics,
                 bindings,
+                unboxFlag: item.unboxFlag,
             };
         }
         const cr = this.TYPES_POOL.get(result.id);
@@ -1390,6 +1393,7 @@ class DocSearch {
             if (cr.ty === result.ty && cr.path === result.path
                 && cr.bindings === result.bindings && cr.generics === result.generics
                 && cr.ty === result.ty && cr.name === result.name
+                && cr.unboxFlag === result.unboxFlag
             ) {
                 return cr;
             }
@@ -1681,14 +1685,17 @@ class DocSearch {
                 const ty = elem[0];
                 const name = elem[1];
                 let path = null;
-                if (elem.length > 2) {
+                if (elem.length > 2 && elem[2] !== null) {
                     path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
                     lastPath = path;
                 }
-                const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path;
+                const exactPath = elem.length > 3 && elem[3] !== null ?
+                    itemPaths.get(elem[3]) :
+                    path;
+                const unboxFlag = elem.length > 4 && !!elem[4];
 
-                lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath });
-                paths[i] = { ty, name, path, exactPath };
+                lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag });
+                paths[i] = { ty, name, path, exactPath, unboxFlag };
             }
 
             // convert `item*` into an object form, and construct word indices.
@@ -2376,17 +2383,20 @@ class DocSearch {
              */
             const writeFn = (fnType, result) => {
                 if (fnType.id < 0) {
-                    const queryId =  mgens && mgens.has(fnType.id) ? mgens.get(fnType.id) : null;
                     if (fnParamNames[-1 - fnType.id] === "") {
                         for (const nested of fnType.generics) {
                             writeFn(nested, result);
                         }
                         return;
-                    } else if (queryId < 0) {
-                        mappedNames.set(
-                            fnParamNames[-1 - fnType.id],
-                            queryParamNames[-1 - queryId],
-                        );
+                    } else if (mgens) {
+                        for (const [queryId, fnId] of mgens) {
+                            if (fnId === fnType.id) {
+                                mappedNames.set(
+                                    queryParamNames[-1 - queryId],
+                                    fnParamNames[-1 - fnType.id],
+                                );
+                            }
+                        }
                     }
                     pushText({
                         name: fnParamNames[-1 - fnType.id],
@@ -2436,15 +2446,15 @@ class DocSearch {
                                 }
                                 pushText({ name, highlighted: false }, result);
                                 pushText({
-                                    name: values.length > 1 ? "=(" : "=",
+                                    name: values.length !== 1 ? "=(" : "=",
                                     highlighted: false,
                                 }, result);
                                 onEachBtwn(
-                                    values,
+                                    values || [],
                                     value => writeFn(value, result),
                                     () => pushText({ name: " + ",  highlighted: false }, result),
                                 );
-                                if (values.length > 1) {
+                                if (values.length !== 1) {
                                     pushText({ name: ")", highlighted: false }, result);
                                 }
                             },
@@ -2616,8 +2626,8 @@ class DocSearch {
          * @param {Array<QueryElement>} queryElems - The elements from the parsed query.
          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
          * @param {Map<number,number>|null} mgensIn
-         *     - Map functions generics to query generics (never modified).
-         * @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
+         *     - Map query generics to function generics (never modified).
+         * @param {Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
          * @param {number} unboxingDepth
          *     - Limit checks that Ty matches Vec<Ty>,
          *       but not Vec<ParamEnvAnd<WithInfcx<ConstTy<Interner<Ty=Ty>>>>>
@@ -2640,7 +2650,7 @@ class DocSearch {
              */
             const mgens = mgensIn === null ? null : new Map(mgensIn);
             if (queryElems.length === 0) {
-                return (!solutionCb || solutionCb(mgens)) ? fnTypesIn : null;
+                return solutionCb(mgens) ? fnTypesIn : null;
             }
             if (!fnTypesIn || fnTypesIn.length === 0) {
                 return null;
@@ -2657,12 +2667,12 @@ class DocSearch {
                         continue;
                     }
                     if (fnType.id < 0 && queryElem.id < 0) {
-                        if (mgens && mgens.has(fnType.id) &&
-                            mgens.get(fnType.id) !== queryElem.id) {
+                        if (mgens && mgens.has(queryElem.id) &&
+                            mgens.get(queryElem.id) !== fnType.id) {
                             continue;
                         }
                         const mgensScratch = new Map(mgens);
-                        mgensScratch.set(fnType.id, queryElem.id);
+                        mgensScratch.set(queryElem.id, fnType.id);
                         if (!solutionCb || solutionCb(mgensScratch)) {
                             const highlighted = [...fnTypesIn];
                             highlighted[i] = Object.assign({
@@ -2672,13 +2682,13 @@ class DocSearch {
                             });
                             return highlighted;
                         }
-                    } else if (!solutionCb || solutionCb(mgens ? new Map(mgens) : null)) {
+                    } else if (solutionCb(mgens ? new Map(mgens) : null)) {
                         // unifyFunctionTypeIsMatchCandidate already checks that ids match
                         const highlighted = [...fnTypesIn];
                         highlighted[i] = Object.assign({
                             highlighted: true,
                         }, fnType, {
-                            generics: unifyFunctionTypes(
+                            generics: unifyGenericTypes(
                                 fnType.generics,
                                 queryElem.generics,
                                 whereClause,
@@ -2701,17 +2711,11 @@ class DocSearch {
                         continue;
                     }
                     if (fnType.id < 0) {
-                        if (mgens && mgens.has(fnType.id) &&
-                            mgens.get(fnType.id) !== 0) {
-                            continue;
-                        }
-                        const mgensScratch = new Map(mgens);
-                        mgensScratch.set(fnType.id, 0);
                         const highlightedGenerics = unifyFunctionTypes(
                             whereClause[(-fnType.id) - 1],
                             queryElems,
                             whereClause,
-                            mgensScratch,
+                            mgens,
                             solutionCb,
                             unboxingDepth + 1,
                         );
@@ -2782,11 +2786,11 @@ class DocSearch {
                 let mgensScratch;
                 if (fnType.id < 0) {
                     mgensScratch = new Map(mgens);
-                    if (mgensScratch.has(fnType.id)
-                        && mgensScratch.get(fnType.id) !== queryElem.id) {
+                    if (mgensScratch.has(queryElem.id)
+                        && mgensScratch.get(queryElem.id) !== fnType.id) {
                         continue;
                     }
-                    mgensScratch.set(fnType.id, queryElem.id);
+                    mgensScratch.set(queryElem.id, fnType.id);
                 } else {
                     mgensScratch = mgens;
                 }
@@ -2809,7 +2813,7 @@ class DocSearch {
                     mgensScratch => {
                         if (fnType.generics.length === 0 && queryElem.generics.length === 0
                             && fnType.bindings.size === 0 && queryElem.bindings.size === 0) {
-                            return !solutionCb || solutionCb(mgensScratch);
+                            return solutionCb(mgensScratch);
                         }
                         const solution = unifyFunctionTypeCheckBindings(
                             fnType,
@@ -2823,7 +2827,7 @@ class DocSearch {
                         }
                         const simplifiedGenerics = solution.simplifiedGenerics;
                         for (const simplifiedMgens of solution.mgens) {
-                            unifiedGenerics = unifyFunctionTypes(
+                            unifiedGenerics = unifyGenericTypes(
                                 simplifiedGenerics,
                                 queryElem.generics,
                                 whereClause,
@@ -2831,7 +2835,7 @@ class DocSearch {
                                 solutionCb,
                                 unboxingDepth,
                             );
-                            if (unifiedGenerics) {
+                            if (unifiedGenerics !== null) {
                                 unifiedGenericsMgens = simplifiedMgens;
                                 return true;
                             }
@@ -2875,16 +2879,6 @@ class DocSearch {
                 )) {
                     continue;
                 }
-                let mgensScratch;
-                if (fnType.id < 0) {
-                    mgensScratch = new Map(mgens);
-                    if (mgensScratch.has(fnType.id) && mgensScratch.get(fnType.id) !== 0) {
-                        continue;
-                    }
-                    mgensScratch.set(fnType.id, 0);
-                } else {
-                    mgensScratch = mgens;
-                }
                 const generics = fnType.id < 0 ?
                     whereClause[(-fnType.id) - 1] :
                     fnType.generics;
@@ -2895,7 +2889,7 @@ class DocSearch {
                     fnTypes.toSpliced(i, 1, ...bindings, ...generics),
                     queryElems,
                     whereClause,
-                    mgensScratch,
+                    mgens,
                     solutionCb,
                     unboxingDepth + 1,
                 );
@@ -2920,6 +2914,199 @@ class DocSearch {
             return null;
         }
         /**
+         * This function compares two lists of generics.
+         *
+         * This function behaves very similarly to `unifyFunctionTypes`, except that it
+         * doesn't skip or reorder anything. This is intended to match the behavior of
+         * the ordinary Rust type system, so that `Vec<Allocator>` only matches an actual
+         * `Vec` of `Allocators` and not the implicit `Allocator` parameter that every
+         * `Vec` has.
+         *
+         * @param {Array<FunctionType>} fnTypesIn - The objects to check.
+         * @param {Array<QueryElement>} queryElems - The elements from the parsed query.
+         * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+         * @param {Map<number,number>|null} mgensIn
+         *     - Map functions generics to query generics (never modified).
+         * @param {Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
+         * @param {number} unboxingDepth
+         *     - Limit checks that Ty matches Vec<Ty>,
+         *       but not Vec<ParamEnvAnd<WithInfcx<ConstTy<Interner<Ty=Ty>>>>>
+         *
+         * @return {[FunctionType]|null} - Returns highlighed results if a match, null otherwise.
+         */
+        function unifyGenericTypes(
+            fnTypesIn,
+            queryElems,
+            whereClause,
+            mgensIn,
+            solutionCb,
+            unboxingDepth,
+        ) {
+            if (unboxingDepth >= UNBOXING_LIMIT) {
+                return null;
+            }
+            /**
+             * @type Map<integer, integer>|null
+             */
+            const mgens = mgensIn === null ? null : new Map(mgensIn);
+            if (queryElems.length === 0) {
+                return solutionCb(mgens) ? fnTypesIn : null;
+            }
+            if (!fnTypesIn || fnTypesIn.length === 0) {
+                return null;
+            }
+            const fnType = fnTypesIn[0];
+            const queryElem = queryElems[0];
+            if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) {
+                if (fnType.id < 0 && queryElem.id < 0) {
+                    if (!mgens || !mgens.has(queryElem.id) ||
+                        mgens.get(queryElem.id) === fnType.id
+                    ) {
+                        const mgensScratch = new Map(mgens);
+                        mgensScratch.set(queryElem.id, fnType.id);
+                        const fnTypesRemaining = unifyGenericTypes(
+                            fnTypesIn.slice(1),
+                            queryElems.slice(1),
+                            whereClause,
+                            mgensScratch,
+                            solutionCb,
+                            unboxingDepth,
+                        );
+                        if (fnTypesRemaining) {
+                            const highlighted = [fnType, ...fnTypesRemaining];
+                            highlighted[0] = Object.assign({
+                                highlighted: true,
+                            }, fnType, {
+                                generics: whereClause[-1 - fnType.id],
+                            });
+                            return highlighted;
+                        }
+                    }
+                } else {
+                    let unifiedGenerics;
+                    const fnTypesRemaining = unifyGenericTypes(
+                        fnTypesIn.slice(1),
+                        queryElems.slice(1),
+                        whereClause,
+                        mgens,
+                        mgensScratch => {
+                            const solution = unifyFunctionTypeCheckBindings(
+                                fnType,
+                                queryElem,
+                                whereClause,
+                                mgensScratch,
+                                unboxingDepth,
+                            );
+                            if (!solution) {
+                                return false;
+                            }
+                            const simplifiedGenerics = solution.simplifiedGenerics;
+                            for (const simplifiedMgens of solution.mgens) {
+                                unifiedGenerics = unifyGenericTypes(
+                                    simplifiedGenerics,
+                                    queryElem.generics,
+                                    whereClause,
+                                    simplifiedMgens,
+                                    solutionCb,
+                                    unboxingDepth,
+                                );
+                                if (unifiedGenerics !== null) {
+                                    return true;
+                                }
+                            }
+                        },
+                        unboxingDepth,
+                    );
+                    if (fnTypesRemaining) {
+                        const highlighted = [fnType, ...fnTypesRemaining];
+                        highlighted[0] = Object.assign({
+                            highlighted: true,
+                        }, fnType, {
+                            generics: unifiedGenerics || fnType.generics,
+                        });
+                        return highlighted;
+                    }
+                }
+            }
+            if (unifyFunctionTypeIsUnboxCandidate(
+                fnType,
+                queryElem,
+                whereClause,
+                mgens,
+                unboxingDepth + 1,
+            )) {
+                let highlightedRemaining;
+                if (fnType.id < 0) {
+                    // Where clause corresponds to `F: A + B`
+                    //                                 ^^^^^
+                    // The order of the constraints doesn't matter, so
+                    // use order-agnostic matching for it.
+                    const highlightedGenerics = unifyFunctionTypes(
+                        whereClause[(-fnType.id) - 1],
+                        [queryElem],
+                        whereClause,
+                        mgens,
+                        mgensScratch => {
+                            const hl = unifyGenericTypes(
+                                fnTypesIn.slice(1),
+                                queryElems.slice(1),
+                                whereClause,
+                                mgensScratch,
+                                solutionCb,
+                                unboxingDepth,
+                            );
+                            if (hl) {
+                                highlightedRemaining = hl;
+                            }
+                            return hl;
+                        },
+                        unboxingDepth + 1,
+                    );
+                    if (highlightedGenerics) {
+                        return [Object.assign({
+                            highlighted: true,
+                        }, fnType, {
+                            generics: highlightedGenerics,
+                        }), ...highlightedRemaining];
+                    }
+                } else {
+                    const highlightedGenerics = unifyGenericTypes(
+                        [
+                            ...Array.from(fnType.bindings.values()).flat(),
+                            ...fnType.generics,
+                        ],
+                        [queryElem],
+                        whereClause,
+                        mgens,
+                        mgensScratch => {
+                            const hl = unifyGenericTypes(
+                                fnTypesIn.slice(1),
+                                queryElems.slice(1),
+                                whereClause,
+                                mgensScratch,
+                                solutionCb,
+                                unboxingDepth,
+                            );
+                            if (hl) {
+                                highlightedRemaining = hl;
+                            }
+                            return hl;
+                        },
+                        unboxingDepth + 1,
+                    );
+                    if (highlightedGenerics) {
+                        return [Object.assign({}, fnType, {
+                            generics: highlightedGenerics,
+                            bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => {
+                                return [k, highlightedGenerics.splice(0, v.length)];
+                            })),
+                        }), ...highlightedRemaining];
+                    }
+                }
+            }
+            return null;
+        }
+        /**
          * Check if this function is a match candidate.
          *
          * This function is all the fast checks that don't require backtracking.
@@ -2930,7 +3117,7 @@ class DocSearch {
          *
          * @param {FunctionType} fnType
          * @param {QueryElement} queryElem
-         * @param {Map<number,number>|null} mgensIn - Map functions generics to query generics.
+         * @param {Map<number,number>|null} mgensIn - Map query generics to function generics.
          * @returns {boolean}
          */
         const unifyFunctionTypeIsMatchCandidate = (fnType, queryElem, mgensIn) => {
@@ -2940,22 +3127,13 @@ class DocSearch {
             }
             // fnType.id < 0 means generic
             // queryElem.id < 0 does too
-            // mgensIn[fnType.id] = queryElem.id
-            // or, if mgensIn[fnType.id] = 0, then we've matched this generic with a bare trait
-            // and should make that same decision everywhere it appears
+            // mgensIn[queryElem.id] = fnType.id
             if (fnType.id < 0 && queryElem.id < 0) {
-                if (mgensIn) {
-                    if (mgensIn.has(fnType.id) && mgensIn.get(fnType.id) !== queryElem.id) {
-                        return false;
-                    }
-                    for (const [fid, qid] of mgensIn.entries()) {
-                        if (fnType.id !== fid && queryElem.id === qid) {
-                            return false;
-                        }
-                        if (fnType.id === fid && queryElem.id !== qid) {
-                            return false;
-                        }
-                    }
+                if (
+                    mgensIn && mgensIn.has(queryElem.id) &&
+                    mgensIn.get(queryElem.id) !== fnType.id
+                ) {
+                    return false;
                 }
                 return true;
             } else {
@@ -3032,7 +3210,7 @@ class DocSearch {
          * @param {FunctionType} fnType
          * @param {QueryElement} queryElem
          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
-         * @param {Map<number,number>} mgensIn - Map functions generics to query generics.
+         * @param {Map<number,number>} mgensIn - Map query generics to function generics.
          *                                            Never modified.
          * @param {number} unboxingDepth
          * @returns {false|{mgens: [Map<number,number>], simplifiedGenerics: [FunctionType]}}
@@ -3100,7 +3278,7 @@ class DocSearch {
          * @param {FunctionType} fnType
          * @param {QueryElement} queryElem
          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
-         * @param {Map<number,number>|null} mgens - Map functions generics to query generics.
+         * @param {Map<number,number>|null} mgens - Map query generics to function generics.
          * @param {number} unboxingDepth
          * @returns {boolean}
          */
@@ -3114,19 +3292,10 @@ class DocSearch {
             if (unboxingDepth >= UNBOXING_LIMIT) {
                 return false;
             }
-            if (fnType.id < 0 && queryElem.id >= 0) {
+            if (fnType.id < 0) {
                 if (!whereClause) {
                     return false;
                 }
-                // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic
-                // mgens[fnType.id] === null indicates that we haven't decided yet
-                if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
-                    return false;
-                }
-                // Where clauses can represent cyclical data.
-                // `null` prevents it from trying to unbox in an infinite loop
-                const mgensTmp = new Map(mgens);
-                mgensTmp.set(fnType.id, null);
                 // This is only a potential unbox if the search query appears in the where clause
                 // for example, searching `Read -> usize` should find
                 // `fn read_all<R: Read>(R) -> Result<usize>`
@@ -3135,10 +3304,11 @@ class DocSearch {
                     whereClause[(-fnType.id) - 1],
                     queryElem,
                     whereClause,
-                    mgensTmp,
+                    mgens,
                     unboxingDepth,
                 );
-            } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) {
+            } else if (fnType.unboxFlag &&
+                (fnType.generics.length > 0 || fnType.bindings.size > 0)) {
                 const simplifiedGenerics = [
                     ...fnType.generics,
                     ...Array.from(fnType.bindings.values()).flat(),
@@ -3182,7 +3352,7 @@ class DocSearch {
          * @param {Row} row
          * @param {QueryElement} elem          - The element from the parsed query.
          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
-         * @param {Map<number,number>|null} mgens - Map functions generics to query generics.
+         * @param {Map<number,number>|null} mgens - Map query generics to function generics.
          *
          * @return {boolean} - Returns true if the type matches, false otherwise.
          */
@@ -3200,8 +3370,32 @@ class DocSearch {
             ) {
                 return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty);
             } else {
-                return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth);
+                return unifyFunctionTypes(
+                    [row],
+                    [elem],
+                    whereClause,
+                    mgens,
+                    () => true,
+                    unboxingDepth,
+                );
+            }
+        };
+
+        /**
+         * Check a query solution for conflicting generics.
+         */
+        const checkTypeMgensForConflict = mgens => {
+            if (!mgens) {
+                return true;
             }
+            const fnTypes = new Set();
+            for (const [_qid, fid] of mgens) {
+                if (fnTypes.has(fid)) {
+                    return false;
+                }
+                fnTypes.add(fid);
+            }
+            return true;
         };
 
         /**
@@ -3523,7 +3717,7 @@ class DocSearch {
                         parsedQuery.returned,
                         row.type.where_clause,
                         mgens,
-                        null,
+                        checkTypeMgensForConflict,
                         0, // unboxing depth
                     );
                 },
@@ -3973,7 +4167,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                         displayType.appendChild(line);
                         addWhereLineFn = () => {};
                     };
-                    for (const [name, qname] of mappedNames) {
+                    for (const [qname, name] of mappedNames) {
                         // don't care unless the generic name is different
                         if (name === qname) {
                             continue;
diff --git a/tests/rustdoc-gui/search-about-this-result.goml b/tests/rustdoc-gui/search-about-this-result.goml
index 62780d01ed7..1d45c06dc43 100644
--- a/tests/rustdoc-gui/search-about-this-result.goml
+++ b/tests/rustdoc-gui/search-about-this-result.goml
@@ -11,8 +11,8 @@ assert-count: ("#search-tabs button", 1)
 assert-count: (".search-results > a", 1)
 
 assert: "//div[@class='type-signature']/strong[text()='Iterator']"
-assert: "//div[@class='type-signature']/strong[text()='(']"
-assert: "//div[@class='type-signature']/strong[text()=')']"
+assert: "//div[@class='type-signature']/strong[text()='(B']"
+assert: "//div[@class='type-signature']/strong[text()='B)']"
 
 assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='FnMut']"
 assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='Iterator::Item']"
diff --git a/tests/rustdoc-js-std/bufread-fill-buf.js b/tests/rustdoc-js-std/bufread-fill-buf.js
index 3828cf76026..6b9309f6864 100644
--- a/tests/rustdoc-js-std/bufread-fill-buf.js
+++ b/tests/rustdoc-js-std/bufread-fill-buf.js
@@ -2,12 +2,15 @@
 
 const EXPECTED = [
     {
-        'query': 'bufread -> result<u8>',
+        'query': 'bufread -> result<[u8]>',
         'others': [
-            { 'path': 'std::io::Split', 'name': 'next' },
             { 'path': 'std::boxed::Box', 'name': 'fill_buf' },
-            { 'path': 'std::io::Chain', 'name': 'fill_buf' },
-            { 'path': 'std::io::Take', 'name': 'fill_buf' },
+        ],
+    },
+    {
+        'query': 'split<bufread> -> option<result<vec<u8>>>',
+        'others': [
+            { 'path': 'std::io::Split', 'name': 'next' },
         ],
     },
 ];
diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js
index 1690d5dc8b5..3be18e6adf3 100644
--- a/tests/rustdoc-js-std/option-type-signatures.js
+++ b/tests/rustdoc-js-std/option-type-signatures.js
@@ -80,11 +80,6 @@ const EXPECTED = [
                 'name': 'and',
                 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>',
             },
-            {
-                'path': 'std::option::Option',
-                'name': 'zip',
-                'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(T, `U`)>',
-            },
         ],
     },
     {
@@ -103,12 +98,12 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'option<t>, option<u> -> option<t, u>',
+        'query': 'option<t>, option<u> -> option<(t, u)>',
         'others': [
             {
                 'path': 'std::option::Option',
                 'name': 'zip',
-                'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, `U`)>',
+                'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`(T`, `U)`>',
             },
         ],
     },
@@ -174,37 +169,23 @@ const EXPECTED = [
                 'path': 'std::option::Option',
                 'name': 'map',
                 'displayType': '`Option`<`T`>, F -> `Option`<U>',
-                'displayMappedNames': `T = t, U = u`,
+                'displayMappedNames': `t = T, u = U`,
                 'displayWhereClause': "F: `FnOnce` (T) -> `U`",
             },
             {
                 'path': 'std::option::Option',
                 'name': 'and_then',
                 'displayType': '`Option`<`T`>, F -> `Option`<U>',
-                'displayMappedNames': `T = t, U = u`,
+                'displayMappedNames': `t = T, u = U`,
                 'displayWhereClause': "F: `FnOnce` (T) -> Option<`U`>",
             },
             {
                 'path': 'std::option::Option',
                 'name': 'zip_with',
                 'displayType': 'Option<T>, `Option`<`U`>, F -> `Option`<R>',
-                'displayMappedNames': `U = t, R = u`,
+                'displayMappedNames': `t = U, u = R`,
                 'displayWhereClause': "F: `FnOnce` (T, U) -> `R`",
             },
-            {
-                'path': 'std::task::Poll',
-                'name': 'map_ok',
-                'displayType': 'Poll<`Option`<Result<`T`, E>>>, F -> Poll<`Option`<Result<U, E>>>',
-                'displayMappedNames': `T = t, U = u`,
-                'displayWhereClause': "F: `FnOnce` (T) -> `U`",
-            },
-            {
-                'path': 'std::task::Poll',
-                'name': 'map_err',
-                'displayType': 'Poll<`Option`<Result<`T`, E>>>, F -> Poll<`Option`<Result<T, U>>>',
-                'displayMappedNames': `T = t, U = u`,
-                'displayWhereClause': "F: `FnOnce` (E) -> `U`",
-            },
         ],
     },
     {
@@ -214,7 +195,7 @@ const EXPECTED = [
                 'path': 'std::option::Option',
                 'name': 'and_then',
                 'displayType': '`Option`<`T`>, F -> `Option`<U>',
-                'displayMappedNames': `T = t, U = u`,
+                'displayMappedNames': `t = T, u = U`,
                 'displayWhereClause': "F: `FnOnce` (T) -> `Option`<`U`>",
             },
         ],
diff --git a/tests/rustdoc-js-std/vec-type-signatures.js b/tests/rustdoc-js-std/vec-type-signatures.js
index 18cf9d6efd0..3c2ff30a833 100644
--- a/tests/rustdoc-js-std/vec-type-signatures.js
+++ b/tests/rustdoc-js-std/vec-type-signatures.js
@@ -19,4 +19,16 @@ const EXPECTED = [
             { 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
         ],
     },
+    {
+        'query': 'vec<T, Allocator> -> Box<[T]>',
+        'others': [
+            {
+                'path': 'std::boxed::Box',
+                'name': 'from',
+                'displayType': '`Vec`<`T`, `A`> -> `Box`<`[T]`, A>',
+                'displayMappedNames': `T = T`,
+                'displayWhereClause': 'A: `Allocator`',
+            },
+        ],
+    },
 ];
diff --git a/tests/rustdoc-js/assoc-type-backtrack.js b/tests/rustdoc-js/assoc-type-backtrack.js
index 493e1a9910d..ccf5c063c8c 100644
--- a/tests/rustdoc-js/assoc-type-backtrack.js
+++ b/tests/rustdoc-js/assoc-type-backtrack.js
@@ -6,7 +6,6 @@ const EXPECTED = [
         'correction': null,
         'others': [
             { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' },
-            { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
         ],
     },
     {
@@ -14,6 +13,19 @@ const EXPECTED = [
         'correction': null,
         'others': [
             { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' },
+        ],
+    },
+    {
+        'query': 'cloned<mytrait>, mytrait2 -> T',
+        'correction': null,
+        'others': [
+            { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
+        ],
+    },
+    {
+        'query': 'cloned<mytrait<U>>, mytrait2 -> T',
+        'correction': null,
+        'others': [
             { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
         ],
     },
@@ -22,7 +34,6 @@ const EXPECTED = [
         'correction': null,
         'others': [
             { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' },
-            { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
         ],
     },
     {
@@ -50,14 +61,14 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'mytrait<U> -> Option<T>',
+        'query': 'cloned<mytrait<U>> -> Option<T>',
         'correction': null,
         'others': [
             { 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' },
         ],
     },
     {
-        'query': 'mytrait<Item=U> -> Option<T>',
+        'query': 'cloned<mytrait<Item=U>> -> Option<T>',
         'correction': null,
         'others': [
             { 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' },
@@ -89,19 +100,21 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'myintofuture<myfuture<t>> -> myfuture<t>',
+        'query': 'myintofuture<t, myfuture<t>> -> myfuture<t>',
         'correction': null,
         'others': [
             { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' },
             { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
         ],
     },
-    // Invalid unboxing of the one-argument case.
-    // If you unbox one of the myfutures, you need to unbox both of them.
+    // Unboxings of the one-argument case.
     {
         'query': 'myintofuture<fut=t> -> myfuture<t>',
         'correction': null,
-        'others': [],
+        'others': [
+            { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' },
+            { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+        ],
     },
     // Unboxings of the two-argument case.
     {
@@ -119,7 +132,7 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'myintofuture<myfuture>, myintofuture<myfuture> -> myfuture',
+        'query': 'myintofuture<t, myfuture>, myintofuture<t, myfuture> -> myfuture',
         'correction': null,
         'others': [
             { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
@@ -132,24 +145,29 @@ const EXPECTED = [
             { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
         ],
     },
-    // Invalid unboxings of the two-argument case.
-    // If you unbox one of the myfutures, you need to unbox all of them.
+    // If you unbox one of the myfutures, you don't need to unbox all of them.
     {
         'query': 'myintofuture<fut=t>, myintofuture<fut=myfuture<t>> -> myfuture<t>',
         'correction': null,
-        'others': [],
+        'others': [
+            { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+        ],
     },
     {
         'query': 'myintofuture<fut=myfuture<t>>, myintofuture<fut=t> -> myfuture<t>',
         'correction': null,
-        'others': [],
+        'others': [
+            { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+        ],
     },
     {
         'query': 'myintofuture<fut=myfuture<t>>, myintofuture<fut=myfuture<t>> -> t',
         'correction': null,
-        'others': [],
+        'others': [
+            { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
+        ],
     },
-    // different generics don't match up either
+    // different generics must match up
     {
         'query': 'myintofuture<fut=myfuture<u>>, myintofuture<fut=myfuture<t>> -> myfuture<t>',
         'correction': null,
diff --git a/tests/rustdoc-js/assoc-type-backtrack.rs b/tests/rustdoc-js/assoc-type-backtrack.rs
index 2dfede9dc38..8a74685b30b 100644
--- a/tests/rustdoc-js/assoc-type-backtrack.rs
+++ b/tests/rustdoc-js/assoc-type-backtrack.rs
@@ -1,3 +1,5 @@
+#![feature(rustdoc_internals)]
+
 pub trait MyTrait2<X> {
     type Output;
 }
@@ -31,10 +33,12 @@ where
     }
 }
 
+#[doc(search_unbox)]
 pub trait MyFuture {
     type Output;
 }
 
+#[doc(search_unbox)]
 pub trait MyIntoFuture {
     type Output;
     type Fut: MyFuture<Output = Self::Output>;
diff --git a/tests/rustdoc-js/assoc-type-unbound.js b/tests/rustdoc-js/assoc-type-unbound.js
index 611b8bd1501..9fd14f9d9fb 100644
--- a/tests/rustdoc-js/assoc-type-unbound.js
+++ b/tests/rustdoc-js/assoc-type-unbound.js
@@ -13,7 +13,7 @@ const EXPECTED = [
                 'path': 'assoc_type_unbound::MyIter',
                 'name': 'next',
                 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
-                'displayMappedNames': 'MyIter::Item = T',
+                'displayMappedNames': 'T = MyIter::Item',
                 'displayWhereClause': '',
             },
         ],
@@ -26,7 +26,7 @@ const EXPECTED = [
                 'path': 'assoc_type_unbound::MyIter',
                 'name': 'next',
                 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
-                'displayMappedNames': 'MyIter::Item = T',
+                'displayMappedNames': 'T = MyIter::Item',
                 'displayWhereClause': '',
             },
         ],
diff --git a/tests/rustdoc-js/assoc-type.rs b/tests/rustdoc-js/assoc-type.rs
index e12e73cb546..aee8f4b1c3f 100644
--- a/tests/rustdoc-js/assoc-type.rs
+++ b/tests/rustdoc-js/assoc-type.rs
@@ -1,12 +1,22 @@
-pub fn my_fn<X: Iterator<Item = Something>>(_x: X) -> u32 {
+#![feature(rustdoc_internals)]
+
+pub fn my_fn<X: other::Iterator<Item = Something>>(_x: X) -> u32 {
     3
 }
 
 pub struct Something;
 
 pub mod my {
+    #[doc(search_unbox)]
     pub trait Iterator<T> {}
     pub fn other_fn<X: Iterator<crate::Something>>(_: X) -> u32 {
         3
     }
 }
+
+pub mod other {
+    #[doc(search_unbox)]
+    pub trait Iterator {
+        type Item;
+    }
+}
diff --git a/tests/rustdoc-js/generics-impl.js b/tests/rustdoc-js/generics-impl.js
index 5e33e224876..c104730dcbd 100644
--- a/tests/rustdoc-js/generics-impl.js
+++ b/tests/rustdoc-js/generics-impl.js
@@ -14,7 +14,7 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'Aaaaaaa -> usize',
+        'query': 'Aaaaaaa -> Result<usize>',
         'others': [
             { 'path': 'generics_impl::Aaaaaaa', 'name': 'read' },
         ],
@@ -23,6 +23,11 @@ const EXPECTED = [
         'query': 'Read -> u64',
         'others': [
             { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' },
+        ],
+    },
+    {
+        'query': 'Ddddddd<Read> -> u64',
+        'others': [
             { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
         ],
     },
@@ -30,7 +35,6 @@ const EXPECTED = [
         'query': 'trait:Read -> u64',
         'others': [
             { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' },
-            { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
         ],
     },
     {
diff --git a/tests/rustdoc-js/generics-impl.rs b/tests/rustdoc-js/generics-impl.rs
index 27d44fdd7e9..f9fe7f390f3 100644
--- a/tests/rustdoc-js/generics-impl.rs
+++ b/tests/rustdoc-js/generics-impl.rs
@@ -1,4 +1,4 @@
-use std::io::{Read, Result as IoResult};
+use std::io::{self, Read};
 
 pub struct Aaaaaaa;
 
@@ -12,7 +12,7 @@ impl Aaaaaaa {
 }
 
 impl Read for Aaaaaaa {
-    fn read(&mut self, out: &mut [u8]) -> IoResult<usize> {
+    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
         Ok(out.len())
     }
 }
diff --git a/tests/rustdoc-js/generics-match-ambiguity-no-unbox.js b/tests/rustdoc-js/generics-match-ambiguity-no-unbox.js
new file mode 100644
index 00000000000..ea4c26d311c
--- /dev/null
+++ b/tests/rustdoc-js/generics-match-ambiguity-no-unbox.js
@@ -0,0 +1,68 @@
+// ignore-order
+// exact-check
+
+// Make sure that results are order-agnostic, even when there's search items that only differ
+// by generics.
+
+const EXPECTED = [
+    {
+        'query': 'Wrap',
+        'in_args': [
+            { 'path': 'generics_match_ambiguity', 'name': 'bar' },
+            { 'path': 'generics_match_ambiguity', 'name': 'foo' },
+        ],
+    },
+    {
+        'query': 'Wrap<i32>',
+        'in_args': [
+            { 'path': 'generics_match_ambiguity', 'name': 'bar' },
+            { 'path': 'generics_match_ambiguity', 'name': 'foo' },
+        ],
+    },
+    {
+        'query': 'Wrap<i32>, Wrap<i32, u32>',
+        'others': [
+            { 'path': 'generics_match_ambiguity', 'name': 'bar' },
+            { 'path': 'generics_match_ambiguity', 'name': 'foo' },
+        ],
+    },
+    {
+        'query': 'Wrap<i32, u32>, Wrap<i32>',
+        'others': [
+            { 'path': 'generics_match_ambiguity', 'name': 'bar' },
+            { 'path': 'generics_match_ambiguity', 'name': 'foo' },
+        ],
+    },
+    {
+        'query': 'W3<i32>, W3<i32, u32>',
+        'others': [
+            { 'path': 'generics_match_ambiguity', 'name': 'baaa' },
+            { 'path': 'generics_match_ambiguity', 'name': 'baab' },
+        ],
+    },
+    {
+        'query': 'W3<i32, u32>, W3<i32>',
+        'others': [
+            { 'path': 'generics_match_ambiguity', 'name': 'baaa' },
+            { 'path': 'generics_match_ambiguity', 'name': 'baab' },
+        ],
+    },
+    {
+        // strict generics matching; W2<i32, u32> doesn't match W2<W3<i32, u32>>,
+        // even though W2<i32> works just fine (ignoring the W3)
+        'query': 'W2<i32>, W2<i32, u32>',
+        'others': [],
+    },
+    {
+        'query': 'W2<i32, u32>, W2<i32>',
+        'others': [],
+    },
+    {
+        'query': 'W2<i32>, W3<i32, u32>',
+        'others': [],
+    },
+    {
+        'query': 'W2<i32>, W2<i32>',
+        'others': [],
+    },
+];
diff --git a/tests/rustdoc-js/generics-match-ambiguity-no-unbox.rs b/tests/rustdoc-js/generics-match-ambiguity-no-unbox.rs
new file mode 100644
index 00000000000..43c2896fa2c
--- /dev/null
+++ b/tests/rustdoc-js/generics-match-ambiguity-no-unbox.rs
@@ -0,0 +1,18 @@
+#![crate_name = "generics_match_ambiguity"]
+
+pub struct Wrap<T, U = ()>(pub T, pub U);
+
+pub fn foo(a: Wrap<i32>, b: Wrap<i32, u32>) {}
+pub fn bar(a: Wrap<i32, u32>, b: Wrap<i32>) {}
+
+pub struct W2<T>(pub T);
+pub struct W3<T, U = ()>(pub T, pub U);
+
+pub fn baaa(a: W3<i32>, b: W3<i32, u32>) {}
+pub fn baab(a: W3<i32, u32>, b: W3<i32>) {}
+pub fn baac(a: W2<W3<i32>>, b: W3<i32, u32>) {}
+pub fn baad(a: W2<W3<i32, u32>>, b: W3<i32>) {}
+pub fn baae(a: W3<i32>, b: W2<W3<i32, u32>>) {}
+pub fn baaf(a: W3<i32, u32>, b: W2<W3<i32>>) {}
+pub fn baag(a: W2<W3<i32>>, b: W2<W3<i32, u32>>) {}
+pub fn baah(a: W2<W3<i32, u32>>, b: W2<W3<i32>>) {}
diff --git a/tests/rustdoc-js/generics-match-ambiguity.js b/tests/rustdoc-js/generics-match-ambiguity.js
index edce4268c5a..aadb179321c 100644
--- a/tests/rustdoc-js/generics-match-ambiguity.js
+++ b/tests/rustdoc-js/generics-match-ambiguity.js
@@ -60,18 +60,14 @@ const EXPECTED = [
         ],
     },
     {
+        // strict generics matching; W2<i32, u32> doesn't match W2<W3<i32, u32>>,
+        // even though W2<i32> works just fine (ignoring the W3)
         'query': 'W2<i32>, W2<i32, u32>',
-        'others': [
-            { 'path': 'generics_match_ambiguity', 'name': 'baag' },
-            { 'path': 'generics_match_ambiguity', 'name': 'baah' },
-        ],
+        'others': [],
     },
     {
         'query': 'W2<i32, u32>, W2<i32>',
-        'others': [
-            { 'path': 'generics_match_ambiguity', 'name': 'baag' },
-            { 'path': 'generics_match_ambiguity', 'name': 'baah' },
-        ],
+        'others': [],
     },
     {
         'query': 'W2<i32>, W3<i32, u32>',
diff --git a/tests/rustdoc-js/generics-match-ambiguity.rs b/tests/rustdoc-js/generics-match-ambiguity.rs
index 79c493856eb..7aadbbd609c 100644
--- a/tests/rustdoc-js/generics-match-ambiguity.rs
+++ b/tests/rustdoc-js/generics-match-ambiguity.rs
@@ -1,9 +1,14 @@
+#![feature(rustdoc_internals)]
+
+#[doc(search_unbox)]
 pub struct Wrap<T, U = ()>(pub T, pub U);
 
 pub fn foo(a: Wrap<i32>, b: Wrap<i32, u32>) {}
 pub fn bar(a: Wrap<i32, u32>, b: Wrap<i32>) {}
 
+#[doc(search_unbox)]
 pub struct W2<T>(pub T);
+#[doc(search_unbox)]
 pub struct W3<T, U = ()>(pub T, pub U);
 
 pub fn baaa(a: W3<i32>, b: W3<i32, u32>) {}
@@ -14,4 +19,3 @@ pub fn baae(a: W3<i32>, b: W2<W3<i32, u32>>) {}
 pub fn baaf(a: W3<i32, u32>, b: W2<W3<i32>>) {}
 pub fn baag(a: W2<W3<i32>>, b: W2<W3<i32, u32>>) {}
 pub fn baah(a: W2<W3<i32, u32>>, b: W2<W3<i32>>) {}
-//
diff --git a/tests/rustdoc-js/generics-nested.js b/tests/rustdoc-js/generics-nested.js
index 294c1949074..b3184dde0d0 100644
--- a/tests/rustdoc-js/generics-nested.js
+++ b/tests/rustdoc-js/generics-nested.js
@@ -18,9 +18,8 @@ const EXPECTED = [
         ],
     },
     {
+        // can't put generics out of order
         'query': '-> Out<Second, First>',
-        'others': [
-            { 'path': 'generics_nested', 'name': 'bet' },
-        ],
+        'others': [],
     },
 ];
diff --git a/tests/rustdoc-js/generics-unbox.js b/tests/rustdoc-js/generics-unbox.js
index 9cdfc7ac8b6..6baf00c814b 100644
--- a/tests/rustdoc-js/generics-unbox.js
+++ b/tests/rustdoc-js/generics-unbox.js
@@ -11,20 +11,17 @@ const EXPECTED = [
         'query': 'Inside<T> -> Out3<T>',
         'others': [
             { 'path': 'generics_unbox', 'name': 'beta' },
-            { 'path': 'generics_unbox', 'name': 'gamma' },
         ],
     },
     {
         'query': 'Inside<T> -> Out4<T>',
         'others': [
-            { 'path': 'generics_unbox', 'name': 'beta' },
             { 'path': 'generics_unbox', 'name': 'gamma' },
         ],
     },
     {
         'query': 'Inside<T> -> Out3<U, T>',
         'others': [
-            { 'path': 'generics_unbox', 'name': 'beta' },
             { 'path': 'generics_unbox', 'name': 'gamma' },
         ],
     },
@@ -32,7 +29,6 @@ const EXPECTED = [
         'query': 'Inside<T> -> Out4<U, T>',
         'others': [
             { 'path': 'generics_unbox', 'name': 'beta' },
-            { 'path': 'generics_unbox', 'name': 'gamma' },
         ],
     },
 ];
diff --git a/tests/rustdoc-js/generics-unbox.rs b/tests/rustdoc-js/generics-unbox.rs
index bef34f891e9..c2578575997 100644
--- a/tests/rustdoc-js/generics-unbox.rs
+++ b/tests/rustdoc-js/generics-unbox.rs
@@ -1,26 +1,34 @@
+#![feature(rustdoc_internals)]
+
+#[doc(search_unbox)]
 pub struct Out<A, B = ()> {
     a: A,
     b: B,
 }
 
+#[doc(search_unbox)]
 pub struct Out1<A, const N: usize> {
     a: [A; N],
 }
 
+#[doc(search_unbox)]
 pub struct Out2<A, const N: usize> {
     a: [A; N],
 }
 
+#[doc(search_unbox)]
 pub struct Out3<A, B> {
     a: A,
     b: B,
 }
 
+#[doc(search_unbox)]
 pub struct Out4<A, B> {
     a: A,
     b: B,
 }
 
+#[doc(search_unbox)]
 pub struct Inside<T>(T);
 
 pub fn alpha<const N: usize, T>(_: Inside<T>) -> Out<Out1<T, N>, Out2<T, N>> {
diff --git a/tests/rustdoc-js/generics.js b/tests/rustdoc-js/generics.js
index b3ca0af3056..a6d20538efe 100644
--- a/tests/rustdoc-js/generics.js
+++ b/tests/rustdoc-js/generics.js
@@ -30,21 +30,13 @@ const EXPECTED = [
         'others': [
             { 'path': 'generics', 'name': 'P' },
         ],
-        'returned': [
-            { 'path': 'generics', 'name': 'alef' },
-        ],
-        'in_args': [
-            { 'path': 'generics', 'name': 'alpha' },
-        ],
+        'returned': [],
+        'in_args': [],
     },
     {
         'query': 'P',
-        'returned': [
-            { 'path': 'generics', 'name': 'alef' },
-        ],
-        'in_args': [
-            { 'path': 'generics', 'name': 'alpha' },
-        ],
+        'returned': [],
+        'in_args': [],
     },
     {
         'query': '"ExtraCreditStructMulti"<ExtraCreditInnerMulti, ExtraCreditInnerMulti>',
diff --git a/tests/rustdoc-js/hof.js b/tests/rustdoc-js/hof.js
index 5e6c9d83c7c..c1142f10668 100644
--- a/tests/rustdoc-js/hof.js
+++ b/tests/rustdoc-js/hof.js
@@ -9,19 +9,19 @@ const EXPECTED = [
 
     // ML-style higher-order function notation
     {
-        'query': 'bool, (u32 -> !) -> ()',
+        'query': 'bool, (first<u32> -> !) -> ()',
         'others': [
             {"path": "hof", "name": "fn_ptr"},
         ],
     },
     {
-        'query': 'u8, (u32 -> !) -> ()',
+        'query': 'u8, (second<u32> -> !) -> ()',
         'others': [
             {"path": "hof", "name": "fn_once"},
         ],
     },
     {
-        'query': 'i8, (u32 -> !) -> ()',
+        'query': 'i8, (third<u32> -> !) -> ()',
         'others': [
             {"path": "hof", "name": "fn_mut"},
         ],
@@ -54,9 +54,6 @@ const EXPECTED = [
         'query': '(u32 -> !) -> ()',
         'others': [
             {"path": "hof", "name": "fn_"},
-            {"path": "hof", "name": "fn_ptr"},
-            {"path": "hof", "name": "fn_mut"},
-            {"path": "hof", "name": "fn_once"},
         ],
     },
     {
@@ -95,30 +92,30 @@ const EXPECTED = [
 
     // Rust-style higher-order function notation
     {
-        'query': 'bool, fn(u32) -> ! -> ()',
+        'query': 'bool, fn(first<u32>) -> ! -> ()',
         'others': [
             {"path": "hof", "name": "fn_ptr"},
         ],
     },
     {
-        'query': 'u8, fnonce(u32) -> ! -> ()',
+        'query': 'u8, fnonce(second<u32>) -> ! -> ()',
         'others': [
             {"path": "hof", "name": "fn_once"},
         ],
     },
     {
-        'query': 'u8, fn(u32) -> ! -> ()',
+        'query': 'u8, fn(second<u32>) -> ! -> ()',
         // fnonce != fn
         'others': [],
     },
     {
-        'query': 'i8, fnmut(u32) -> ! -> ()',
+        'query': 'i8, fnmut(third<u32>) -> ! -> ()',
         'others': [
             {"path": "hof", "name": "fn_mut"},
         ],
     },
     {
-        'query': 'i8, fn(u32) -> ! -> ()',
+        'query': 'i8, fn(third<u32>) -> ! -> ()',
         // fnmut != fn
         'others': [],
     },
@@ -152,7 +149,7 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'fn(u32) -> ! -> ()',
+        'query': 'fn() -> ! -> ()',
         'others': [
             // fn matches primitive:fn and trait:Fn
             {"path": "hof", "name": "fn_"},
@@ -160,14 +157,14 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'trait:fn(u32) -> ! -> ()',
+        'query': 'trait:fn() -> ! -> ()',
         'others': [
             // fn matches primitive:fn and trait:Fn
             {"path": "hof", "name": "fn_"},
         ],
     },
     {
-        'query': 'primitive:fn(u32) -> ! -> ()',
+        'query': 'primitive:fn() -> ! -> ()',
         'others': [
             // fn matches primitive:fn and trait:Fn
             {"path": "hof", "name": "fn_ptr"},
diff --git a/tests/rustdoc-js/looks-like-rustc-interner.js b/tests/rustdoc-js/looks-like-rustc-interner.js
index a4806d23499..d6d2764c3ae 100644
--- a/tests/rustdoc-js/looks-like-rustc-interner.js
+++ b/tests/rustdoc-js/looks-like-rustc-interner.js
@@ -1,9 +1,15 @@
 // https://github.com/rust-lang/rust/pull/122247
 // exact-check
 
-const EXPECTED = {
-    'query': 'canonicalvarinfo, intoiterator -> intoiterator',
-    'others': [
-        { 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' },
-    ],
-};
+const EXPECTED = [
+    {
+        'query': 'canonicalvarinfo, intoiterator -> intoiterator',
+        'others': [],
+    },
+    {
+        'query': '[canonicalvarinfo], interner<tys=intoiterator> -> intoiterator',
+        'others': [
+            { 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/nested-unboxed.js b/tests/rustdoc-js/nested-unboxed.js
index 44f784eb1f6..5f9eabc12f6 100644
--- a/tests/rustdoc-js/nested-unboxed.js
+++ b/tests/rustdoc-js/nested-unboxed.js
@@ -33,9 +33,8 @@ const EXPECTED = [
     },
     {
         'query': '-> Result<i32, u32, bool>',
-        'others': [
-            { 'path': 'nested_unboxed', 'name': 'something' },
-        ],
+        // can't put nested generics out of order
+        'others': [],
     },
     {
         'query': '-> Result<Object<i32>, bool>',
@@ -45,9 +44,7 @@ const EXPECTED = [
     },
     {
         'query': '-> Result<Object<u32>, bool>',
-        'others': [
-            { 'path': 'nested_unboxed', 'name': 'something' },
-        ],
+        'others': [],
     },
     {
         'query': '-> Result<Object<i32>, u32, bool>',
diff --git a/tests/rustdoc-js/nested-unboxed.rs b/tests/rustdoc-js/nested-unboxed.rs
index 57f9592b791..6c8b1bd6aa1 100644
--- a/tests/rustdoc-js/nested-unboxed.rs
+++ b/tests/rustdoc-js/nested-unboxed.rs
@@ -1,3 +1,6 @@
+#![feature(rustdoc_internals)]
+
+#[doc(search_unbox)]
 pub struct Object<T, U>(T, U);
 
 pub fn something() -> Result<Object<i32, u32>, bool> {
diff --git a/tests/rustdoc-js/reference.js b/tests/rustdoc-js/reference.js
index b4a1fb15d36..378fc03475b 100644
--- a/tests/rustdoc-js/reference.js
+++ b/tests/rustdoc-js/reference.js
@@ -79,9 +79,8 @@ const EXPECTED = [
     },
     {
         'query': 'reference<ring>, reference<ring> -> ()',
-        'others': [
-            { 'path': 'reference::Ring', 'name': 'wear' },
-        ],
+        // can't leave out the `mut`, because can't reorder like that
+        'others': [],
     },
     {
         'query': 'reference<mut, ring>, reference<ring> -> ()',
@@ -102,9 +101,8 @@ const EXPECTED = [
     },
     {
         'query': 'reference<middle>, reference<middle> -> ()',
-        'others': [
-            { 'path': 'reference', 'name': 'show' },
-        ],
+        // can't leave out the mut
+        'others': [],
     },
     {
         'query': 'reference<mut, middle>, reference<mut, middle> -> ()',
@@ -203,9 +201,8 @@ const EXPECTED = [
     // middle with shorthand
     {
         'query': '&middle, &middle -> ()',
-        'others': [
-            { 'path': 'reference', 'name': 'show' },
-        ],
+        // can't leave out the mut
+        'others': [],
     },
     {
         'query': '&mut middle, &mut middle -> ()',
diff --git a/tests/rustdoc-js/tuple-unit.js b/tests/rustdoc-js/tuple-unit.js
index d24a3da328c..6a9b861cf94 100644
--- a/tests/rustdoc-js/tuple-unit.js
+++ b/tests/rustdoc-js/tuple-unit.js
@@ -57,7 +57,7 @@ const EXPECTED = [
         'in_args': [],
     },
     {
-        'query': '(Q, ())',
+        'query': '(Q, R<()>)',
         'returned': [
             { 'path': 'tuple_unit', 'name': 'nest' },
         ],
@@ -71,7 +71,7 @@ const EXPECTED = [
         'in_args': [],
     },
     {
-        'query': '(u32)',
+        'query': 'R<(u32)>',
         'returned': [
             { 'path': 'tuple_unit', 'name': 'nest' },
         ],
diff --git a/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs b/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs
index 58306a4cfc9..57d6b591287 100644
--- a/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs
+++ b/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs
@@ -7,4 +7,7 @@ trait Mine {}
 #[doc(fake_variadic)]  //~ ERROR: `#[doc(fake_variadic)]` is meant for internal use only
 impl<T> Mine for (T,) {}
 
+#[doc(search_unbox)]  //~ ERROR: `#[doc(search_unbox)]` is meant for internal use only
+struct Wrap<T> (T);
+
 fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr b/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr
index bbb9edd58f0..f3c00a2156b 100644
--- a/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr
+++ b/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr
@@ -18,6 +18,16 @@ LL | #[doc(fake_variadic)]
    = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: aborting due to 2 previous errors
+error[E0658]: `#[doc(search_unbox)]` is meant for internal use only
+  --> $DIR/feature-gate-rustdoc_internals.rs:10:1
+   |
+LL | #[doc(search_unbox)]
+   | ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #90418 <https://github.com/rust-lang/rust/issues/90418> for more information
+   = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0658`.