about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-11-11 12:26:00 +0000
committerbors <bors@rust-lang.org>2024-11-11 12:26:00 +0000
commitd4822c2d84c242cc7403118b50c571464f38ef8f (patch)
treebf58926e659f32c0de796e2730a90729910a6c55
parent71042b4b201a432b426304f511b3169850ed8923 (diff)
parent9900ea48b566656fb12b5fcbd0a1b20aaa96e5ca (diff)
Auto merge of #127589 - notriddle:notriddle/search-sem-3, r=GuillaumeGomez
rustdoc-search: simplify rules for generics and type params

**Heads up!**: This PR is a follow-up that depends on #124544. It adds 12dc24f46007f82b93ed85614347a42d47580afa, a change to the filtering behavior, and 9900ea48b566656fb12b5fcbd0a1b20aaa96e5ca, a minor ranking tweak.

Part of https://github.com/rust-lang/rust-project-goals/issues/112

This PR overturns https://github.com/rust-lang/rust/pull/109802

## Preview

* no results: [`Box<[A]> -> Vec<B>`](http://notriddle.com/rustdoc-html-demo-12/search-sem-3/std/index.html?search=Box%3C%5BA%5D%3E%20-%3E%20Vec%3CB%3E)
* results: [`Box<[A]> -> Vec<A>`](http://notriddle.com/rustdoc-html-demo-12/search-sem-3/std/index.html?search=Box%3C%5BA%5D%3E%20-%3E%20Vec%3CA%3E)
* [`T -> U`](http://notriddle.com/rustdoc-html-demo-12/search-sem-3/std/index.html?search=T%20-%3E%20U)
* [`Cx -> TyCtxt`](http://notriddle.com/rustdoc-html-demo-12/search-sem-3-compiler/rustdoc/index.html?search=Cx%20-%3E%20TyCtxt)

![image](https://github.com/user-attachments/assets/015ae28c-7469-4f7f-be03-157d28d7ec97)

## Description

This commit is a response to feedback on the displayed type signatures results, by making generics act stricter.

- Order within generics is significant. This means `Vec<Allocator>` now matches only with a true vector of allocators, instead of matching the second type param. It also makes unboxing within generics stricter, so `Result<A, B>` only matches if `B` is in the error type and `A` is in the success type. The top level of the function search is unaffected.
- Generics are only "unboxed" if a type is explicitly opted into it. References and tuples are hardcoded to allow unboxing, and Box, Rc, Arc, Option, Result, and Future are opted in with an unstable attribute. Search result unboxing is the process that allows you to search for `i32 -> str` and get back a function with the type signature `&Future<i32> -> Box<str>`.
- Instead of ranking by set overlap, it ranks by the number of items in the type signature. This makes it easier to find single type signatures like transmute.

## Find the discussion on

* <https://rust-lang.zulipchat.com/#narrow/stream/393423-t-rustdoc.2Fmeetings/topic/meeting.202024-07-08/near/449965149>
* <https://github.com/rust-lang/rust/pull/124544#issuecomment-2204272265>
* <https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/deciding.20on.20semantics.20of.20generics.20in.20rustdoc.20search>
-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/mod.rs1
-rw-r--r--src/librustdoc/html/render/search_index.rs119
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css21
-rw-r--r--src/librustdoc/html/static/js/externs.js7
-rw-r--r--src/librustdoc/html/static/js/search.js1116
-rw-r--r--src/tools/rustdoc-js/tester.js55
-rw-r--r--tests/rustdoc-gui/search-about-this-result.goml42
-rw-r--r--tests/rustdoc-gui/search-corrections.goml12
-rw-r--r--tests/rustdoc-js-std/bufread-fill-buf.js11
-rw-r--r--tests/rustdoc-js-std/option-type-signatures.js157
-rw-r--r--tests/rustdoc-js-std/parser-bindings.js52
-rw-r--r--tests/rustdoc-js-std/parser-errors.js159
-rw-r--r--tests/rustdoc-js-std/parser-filter.js27
-rw-r--r--tests/rustdoc-js-std/parser-generics.js18
-rw-r--r--tests/rustdoc-js-std/parser-hof.js86
-rw-r--r--tests/rustdoc-js-std/parser-ident.js37
-rw-r--r--tests/rustdoc-js-std/parser-literal.js7
-rw-r--r--tests/rustdoc-js-std/parser-paths.js31
-rw-r--r--tests/rustdoc-js-std/parser-quote.js21
-rw-r--r--tests/rustdoc-js-std/parser-reference.js55
-rw-r--r--tests/rustdoc-js-std/parser-returned.js33
-rw-r--r--tests/rustdoc-js-std/parser-separators.js24
-rw-r--r--tests/rustdoc-js-std/parser-slice-array.js56
-rw-r--r--tests/rustdoc-js-std/parser-tuple.js65
-rw-r--r--tests/rustdoc-js-std/parser-weird-queries.js18
-rw-r--r--tests/rustdoc-js-std/simd-type-signatures.js20
-rw-r--r--tests/rustdoc-js-std/transmute-fail.js13
-rw-r--r--tests/rustdoc-js-std/transmute.js12
-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.js39
-rw-r--r--tests/rustdoc-js/assoc-type-unbound.rs4
-rw-r--r--tests/rustdoc-js/assoc-type.js48
-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-trait.js48
-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/impl-trait.js6
-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/non-english-identifier.js34
-rw-r--r--tests/rustdoc-js/reference.js15
-rw-r--r--tests/rustdoc-js/tuple-unit.js4
-rw-r--r--tests/rustdoc-js/type-parameters.js6
-rw-r--r--tests/ui/feature-gates/feature-gate-rustdoc_internals.rs3
-rw-r--r--tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr12
69 files changed, 1980 insertions, 851 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 6f0bcf5c3f0..08098ae7f6c 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 64a527ef106..836511325f4 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;
@@ -937,6 +937,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
@@ -1149,6 +1166,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 70c92f0144c..2d1734c0314 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 c04c793ba46..f89fd0f7f2a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1761,6 +1761,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 64b0520e983..a68aaec5f70 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 f348fba6220..c78a4db95ff 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/mod.rs b/src/librustdoc/html/render/mod.rs
index 8446235fb18..a17c7626148 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -204,6 +204,7 @@ pub(crate) struct IndexItemFunctionType {
     inputs: Vec<RenderType>,
     output: Vec<RenderType>,
     where_clause: Vec<Vec<RenderType>>,
+    param_names: Vec<Symbol>,
 }
 
 impl IndexItemFunctionType {
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index d1939adc1a5..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,
                         });
                     }
                 }
@@ -646,9 +696,25 @@ pub(crate) fn build_index<'tcx>(
                 full_paths.push((*index, path));
             }
 
+            let param_names: Vec<(usize, String)> = {
+                let mut prev = Vec::new();
+                let mut result = Vec::new();
+                for (index, item) in self.items.iter().enumerate() {
+                    if let Some(ty) = &item.search_type
+                        && let my =
+                            ty.param_names.iter().map(|sym| sym.as_str()).collect::<Vec<_>>()
+                        && my != prev
+                    {
+                        result.push((index, my.join(",")));
+                        prev = my;
+                    }
+                }
+                result
+            };
+
             let has_aliases = !self.aliases.is_empty();
             let mut crate_data =
-                serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
+                serializer.serialize_struct("CrateData", if has_aliases { 13 } else { 12 })?;
             crate_data.serialize_field("t", &types)?;
             crate_data.serialize_field("n", &names)?;
             crate_data.serialize_field("q", &full_paths)?;
@@ -660,6 +726,7 @@ pub(crate) fn build_index<'tcx>(
             crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
             crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?;
             crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?;
+            crate_data.serialize_field("P", &param_names)?;
             if has_aliases {
                 crate_data.serialize_field("a", &self.aliases)?;
             }
@@ -758,7 +825,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
             None
         }
     });
-    let (mut inputs, mut output, where_clause) = match item.kind {
+    let (mut inputs, mut output, param_names, where_clause) = match item.kind {
         clean::ForeignFunctionItem(ref f, _)
         | clean::FunctionItem(ref f)
         | clean::MethodItem(ref f, _)
@@ -771,7 +838,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
     inputs.retain(|a| a.id.is_some() || a.generics.is_some());
     output.retain(|a| a.id.is_some() || a.generics.is_some());
 
-    Some(IndexItemFunctionType { inputs, output, where_clause })
+    Some(IndexItemFunctionType { inputs, output, where_clause, param_names })
 }
 
 fn get_index_type(
@@ -1285,7 +1352,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>,
     cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Symbol>, Vec<Vec<RenderType>>) {
     let decl = &func.decl;
 
     let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
@@ -1331,7 +1398,21 @@ fn get_fn_inputs_and_outputs<'tcx>(
     let mut ret_types = Vec::new();
     simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
 
-    let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
-    simplified_params.sort_by_key(|(idx, _)| -idx);
-    (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
+    let mut simplified_params = rgen.into_iter().collect::<Vec<_>>();
+    simplified_params.sort_by_key(|(_, (idx, _))| -idx);
+    (
+        arg_types,
+        ret_types,
+        simplified_params
+            .iter()
+            .map(|(name, (_idx, _traits))| match name {
+                SimplifiedParam::Symbol(name) => *name,
+                SimplifiedParam::Anonymous(_) => kw::Empty,
+                SimplifiedParam::AssociatedType(def_id, name) => {
+                    Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name))
+                }
+            })
+            .collect(),
+        simplified_params.into_iter().map(|(_name, (_idx, traits))| traits).collect(),
+    )
 }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 1042d254749..66a8a198928 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -264,6 +264,7 @@ a.anchor,
 .mobile-topbar h2 a,
 h1 a,
 .search-results a,
+.search-results li,
 .stab,
 .result-name i {
 	color: var(--main-color);
@@ -379,7 +380,7 @@ details:not(.toggle) summary {
 	margin-bottom: .6em;
 }
 
-code, pre, .code-header {
+code, pre, .code-header, .type-signature {
 	font-family: "Source Code Pro", monospace;
 }
 .docblock code, .docblock-short code {
@@ -1205,22 +1206,28 @@ so that we can apply CSS-filters to change the arrow color in themes */
 
 .search-results.active {
 	display: block;
+	margin: 0;
+	padding: 0;
 }
 
 .search-results > a {
-	display: flex;
+	display: grid;
+	grid-template-areas:
+		"search-result-name search-result-desc"
+		"search-result-type-signature search-result-type-signature";
+	grid-template-columns: .6fr .4fr;
 	/* A little margin ensures the browser's outlining of focused links has room to display. */
 	margin-left: 2px;
 	margin-right: 2px;
 	border-bottom: 1px solid var(--search-result-border-color);
-	gap: 1em;
+	column-gap: 1em;
 }
 
 .search-results > a > div.desc {
 	white-space: nowrap;
 	text-overflow: ellipsis;
 	overflow: hidden;
-	flex: 2;
+	grid-area: search-result-desc;
 }
 
 .search-results a:hover,
@@ -1232,7 +1239,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	display: flex;
 	align-items: center;
 	justify-content: start;
-	flex: 3;
+	grid-area: search-result-name;
 }
 .search-results .result-name .alias {
 	color: var(--search-results-alias-color);
@@ -1253,6 +1260,10 @@ so that we can apply CSS-filters to change the arrow color in themes */
 .search-results .result-name .path > * {
 	display: inline;
 }
+.search-results .type-signature {
+	grid-area: search-result-type-signature;
+	white-space: pre-wrap;
+}
 
 .popover {
 	position: absolute;
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index 3d3e0a8f838..c4faca1c0c3 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -9,12 +9,12 @@ function initSearch(searchIndex){}
 /**
  * @typedef {{
  *     name: string,
- *     id: integer|null,
+ *     id: number|null,
  *     fullPath: Array<string>,
  *     pathWithoutLast: Array<string>,
  *     pathLast: string,
  *     generics: Array<QueryElement>,
- *     bindings: Map<integer, Array<QueryElement>>,
+ *     bindings: Map<number, Array<QueryElement>>,
  * }}
  */
 let QueryElement;
@@ -92,6 +92,9 @@ let Results;
  *     parent: (Object|undefined),
  *     path: string,
  *     ty: number,
+ *     type: FunctionSearchType?,
+ *     displayType: Promise<Array<Array<string>>>|null,
+ *     displayTypeMappedNames: Promise<Array<[string, Array<string>]>>|null,
  * }}
  */
 let ResultObject;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index eed64d024c0..4e1bbbbf59d 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -15,7 +15,16 @@ if (!Array.prototype.toSpliced) {
     };
 }
 
-(function() {
+function onEachBtwn(arr, func, funcBtwn) {
+    let skipped = true;
+    for (const value of arr) {
+        if (!skipped) {
+            funcBtwn(value);
+        }
+        skipped = func(value);
+    }
+}
+
 // ==================== Core search logic begin ====================
 // This mapping table should match the discriminants of
 // `rustdoc::formats::item_type::ItemType` type in Rust.
@@ -50,8 +59,10 @@ const itemTypes = [
 ];
 
 // used for special search precedence
+const TY_PRIMITIVE = itemTypes.indexOf("primitive");
 const TY_GENERIC = itemTypes.indexOf("generic");
 const TY_IMPORT = itemTypes.indexOf("import");
+const TY_TRAIT = itemTypes.indexOf("trait");
 const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
 
 // Hard limit on how deep to recurse into generics when doing type-driven search.
@@ -276,7 +287,7 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) {
         // The type filter doesn't count as an element since it's a modifier.
         const typeFilterElem = elems.pop();
         checkExtraTypeFilterCharacters(start, parserState);
-        parserState.typeFilter = typeFilterElem.name;
+        parserState.typeFilter = typeFilterElem.normalizedPathLast;
         parserState.pos += 1;
         parserState.totalElems -= 1;
         query.literalSearch = false;
@@ -686,7 +697,7 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) {
     } else if (quadcolon !== null) {
         throw ["Unexpected ", quadcolon[0]];
     }
-    const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);
+    const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/).map(x => x.toLowerCase());
     // In case we only have something like `<p>`, there is no name.
     if (pathSegments.length === 0
         || (pathSegments.length === 1 && pathSegments[0] === "")) {
@@ -726,7 +737,10 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) {
                 if (gen.name !== null) {
                     gen.bindingName.generics.unshift(gen);
                 }
-                bindings.set(gen.bindingName.name, gen.bindingName.generics);
+                bindings.set(
+                    gen.bindingName.name.toLowerCase().replace(/_/g, ""),
+                    gen.bindingName.generics,
+                );
                 return false;
             }
             return true;
@@ -1114,6 +1128,13 @@ class DocSearch {
          * @type {Map<string, {id: integer, assocOnly: boolean}>}
          */
         this.typeNameIdMap = new Map();
+        /**
+         * Map from type ID to associated type name. Used for display,
+         * not for search.
+         *
+         * @type {Map<integer, string>}
+         */
+        this.assocTypeIdNameMap = new Map();
         this.ALIASES = new Map();
         this.rootPath = rootPath;
         this.searchState = searchState;
@@ -1158,6 +1179,14 @@ class DocSearch {
          * Special type name IDs for searching higher order functions (`->` syntax).
          */
         this.typeNameIdOfHof = this.buildTypeMapIndex("->");
+        /**
+         * Special type name IDs the output assoc type.
+         */
+        this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true);
+        /**
+         * Special type name IDs for searching by reference.
+         */
+        this.typeNameIdOfReference = this.buildTypeMapIndex("reference");
 
         /**
          * Empty, immutable map used in item search types with no bindings.
@@ -1234,9 +1263,9 @@ class DocSearch {
      *
      * @return {Array<FunctionSearchType>}
      */
-    buildItemSearchTypeAll(types, lowercasePaths) {
+    buildItemSearchTypeAll(types, paths, lowercasePaths) {
         return types.length > 0 ?
-            types.map(type => this.buildItemSearchType(type, lowercasePaths)) :
+            types.map(type => this.buildItemSearchType(type, paths, lowercasePaths)) :
             this.EMPTY_GENERICS_ARRAY;
     }
 
@@ -1245,7 +1274,7 @@ class DocSearch {
      *
      * @param {RawFunctionType} type
      */
-    buildItemSearchType(type, lowercasePaths, isAssocType) {
+    buildItemSearchType(type, paths, lowercasePaths, isAssocType) {
         const PATH_INDEX_DATA = 0;
         const GENERICS_DATA = 1;
         const BINDINGS_DATA = 2;
@@ -1258,6 +1287,7 @@ class DocSearch {
             pathIndex = type[PATH_INDEX_DATA];
             generics = this.buildItemSearchTypeAll(
                 type[GENERICS_DATA],
+                paths,
                 lowercasePaths,
             );
             if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) {
@@ -1274,8 +1304,8 @@ class DocSearch {
                     //
                     // As a result, the key should never have generics on it.
                     return [
-                        this.buildItemSearchType(assocType, lowercasePaths, true).id,
-                        this.buildItemSearchTypeAll(constraints, lowercasePaths),
+                        this.buildItemSearchType(assocType, paths, lowercasePaths, true).id,
+                        this.buildItemSearchTypeAll(constraints, paths, lowercasePaths),
                     ];
                 }));
             } else {
@@ -1291,31 +1321,41 @@ class DocSearch {
             // the actual names of generic parameters aren't stored, since they aren't API
             result = {
                 id: pathIndex,
+                name: "",
                 ty: TY_GENERIC,
                 path: null,
                 exactPath: null,
                 generics,
                 bindings,
+                unboxFlag: true,
             };
         } else if (pathIndex === 0) {
             // `0` is used as a sentinel because it's fewer bytes than `null`
             result = {
                 id: null,
+                name: "",
                 ty: null,
                 path: null,
                 exactPath: null,
                 generics,
                 bindings,
+                unboxFlag: true,
             };
         } else {
             const item = lowercasePaths[pathIndex - 1];
+            const id = this.buildTypeMapIndex(item.name, isAssocType);
+            if (isAssocType) {
+                this.assocTypeIdNameMap.set(id, paths[pathIndex - 1].name);
+            }
             result = {
-                id: this.buildTypeMapIndex(item.name, isAssocType),
+                id,
+                name: paths[pathIndex - 1].name,
                 ty: item.ty,
                 path: item.path,
                 exactPath: item.exactPath,
                 generics,
                 bindings,
+                unboxFlag: item.unboxFlag,
             };
         }
         const cr = this.TYPES_POOL.get(result.id);
@@ -1352,7 +1392,8 @@ class DocSearch {
             }
             if (cr.ty === result.ty && cr.path === result.path
                 && cr.bindings === result.bindings && cr.generics === result.generics
-                && cr.ty === result.ty
+                && cr.ty === result.ty && cr.name === result.name
+                && cr.unboxFlag === result.unboxFlag
             ) {
                 return cr;
             }
@@ -1371,7 +1412,7 @@ class DocSearch {
      *   query fingerprint. If any bits are set in the query but not in the function, it can't
      *   match.
      *
-     * - The fourth section has the number of distinct items in the set.
+     * - The fourth section has the number of items in the set.
      *   This is the distance function, used for filtering and for sorting.
      *
      * [^1]: Distance is the relatively naive metric of counting the number of distinct items in
@@ -1379,9 +1420,8 @@ class DocSearch {
      *
      * @param {FunctionType|QueryElement} type - a single type
      * @param {Uint32Array} output - write the fingerprint to this data structure: uses 128 bits
-     * @param {Set<number>} fps - Set of distinct items
      */
-    buildFunctionTypeFingerprint(type, output, fps) {
+    buildFunctionTypeFingerprint(type, output) {
         let input = type.id;
         // All forms of `[]`/`()`/`->` get collapsed down to one thing in the bloom filter.
         // Differentiating between arrays and slices, if the user asks for it, is
@@ -1427,10 +1467,11 @@ class DocSearch {
             output[0] |= (1 << (h0a % 32)) | (1 << (h1b % 32));
             output[1] |= (1 << (h1a % 32)) | (1 << (h2b % 32));
             output[2] |= (1 << (h2a % 32)) | (1 << (h0b % 32));
-            fps.add(input);
+            // output[3] is the total number of items in the type signature
+            output[3] += 1;
         }
         for (const g of type.generics) {
-            this.buildFunctionTypeFingerprint(g, output, fps);
+            this.buildFunctionTypeFingerprint(g, output);
         }
         const fb = {
             id: null,
@@ -1441,9 +1482,8 @@ class DocSearch {
         for (const [k, v] of type.bindings.entries()) {
             fb.id = k;
             fb.generics = v;
-            this.buildFunctionTypeFingerprint(fb, output, fps);
+            this.buildFunctionTypeFingerprint(fb, output);
         }
-        output[3] = fps.size;
     }
 
     /**
@@ -1463,11 +1503,12 @@ class DocSearch {
          * The raw function search type format is generated using serde in
          * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string
          *
+         * @param {Array<{name: string, ty: number}>} paths
          * @param {Array<{name: string, ty: number}>} lowercasePaths
          *
          * @return {null|FunctionSearchType}
          */
-        const buildFunctionSearchTypeCallback = lowercasePaths => {
+        const buildFunctionSearchTypeCallback = (paths, lowercasePaths) => {
             return functionSearchType => {
                 if (functionSearchType === 0) {
                     return null;
@@ -1477,11 +1518,16 @@ class DocSearch {
                 let inputs, output;
                 if (typeof functionSearchType[INPUTS_DATA] === "number") {
                     inputs = [
-                        this.buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths),
+                        this.buildItemSearchType(
+                            functionSearchType[INPUTS_DATA],
+                            paths,
+                            lowercasePaths,
+                        ),
                     ];
                 } else {
                     inputs = this.buildItemSearchTypeAll(
                         functionSearchType[INPUTS_DATA],
+                        paths,
                         lowercasePaths,
                     );
                 }
@@ -1490,12 +1536,14 @@ class DocSearch {
                         output = [
                             this.buildItemSearchType(
                                 functionSearchType[OUTPUT_DATA],
+                                paths,
                                 lowercasePaths,
                             ),
                         ];
                     } else {
                         output = this.buildItemSearchTypeAll(
                             functionSearchType[OUTPUT_DATA],
+                            paths,
                             lowercasePaths,
                         );
                     }
@@ -1506,8 +1554,12 @@ class DocSearch {
                 const l = functionSearchType.length;
                 for (let i = 2; i < l; ++i) {
                     where_clause.push(typeof functionSearchType[i] === "number"
-                        ? [this.buildItemSearchType(functionSearchType[i], lowercasePaths)]
-                        : this.buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
+                        ? [this.buildItemSearchType(functionSearchType[i], paths, lowercasePaths)]
+                        : this.buildItemSearchTypeAll(
+                            functionSearchType[i],
+                            paths,
+                            lowercasePaths,
+                        ));
                 }
                 return {
                     inputs, output, where_clause,
@@ -1551,6 +1603,13 @@ class DocSearch {
             this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e));
             let descIndex = 0;
 
+            /**
+             * List of generic function type parameter names.
+             * Used for display, not for searching.
+             * @type {[string]}
+             */
+            let lastParamNames = [];
+
             // This object should have exactly the same set of fields as the "row"
             // object defined below. Your JavaScript runtime will thank you.
             // https://mathiasbynens.be/notes/shapes-ics
@@ -1565,6 +1624,7 @@ class DocSearch {
                 desc: crateCorpus.doc,
                 parent: undefined,
                 type: null,
+                paramNames: lastParamNames,
                 id,
                 word: crate,
                 normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
@@ -1601,6 +1661,10 @@ class DocSearch {
             // an array of [(String) alias name
             //             [Number] index to items]
             const aliases = crateCorpus.a;
+            // an array of [(Number) item index,
+            //              (String) comma-separated list of function generic param names]
+            // an item whose index is not present will fall back to the previous present path
+            const itemParamNames = new Map(crateCorpus.P);
 
             // an array of [{name: String, ty: Number}]
             const lowercasePaths = [];
@@ -1608,7 +1672,7 @@ class DocSearch {
             // a string representing the list of function types
             const itemFunctionDecoder = new VlqHexDecoder(
                 crateCorpus.f,
-                buildFunctionSearchTypeCallback(lowercasePaths),
+                buildFunctionSearchTypeCallback(paths, lowercasePaths),
             );
 
             // convert `rawPaths` entries into object form
@@ -1620,14 +1684,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.
@@ -1659,20 +1726,22 @@ class DocSearch {
                 const name = itemNames[i] === "" ? lastName : itemNames[i];
                 const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase();
                 const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
+                const paramNames = itemParamNames.has(i) ?
+                    itemParamNames.get(i).split(",") :
+                    lastParamNames;
                 const type = itemFunctionDecoder.next();
                 if (type !== null) {
                     if (type) {
                         const fp = this.functionTypeFingerprint.subarray(id * 4, (id + 1) * 4);
-                        const fps = new Set();
                         for (const t of type.inputs) {
-                            this.buildFunctionTypeFingerprint(t, fp, fps);
+                            this.buildFunctionTypeFingerprint(t, fp);
                         }
                         for (const t of type.output) {
-                            this.buildFunctionTypeFingerprint(t, fp, fps);
+                            this.buildFunctionTypeFingerprint(t, fp);
                         }
                         for (const w of type.where_clause) {
                             for (const t of w) {
-                                this.buildFunctionTypeFingerprint(t, fp, fps);
+                                this.buildFunctionTypeFingerprint(t, fp);
                             }
                         }
                     }
@@ -1691,6 +1760,7 @@ class DocSearch {
                         itemPaths.get(itemReexports.get(i)) : path,
                     parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined,
                     type,
+                    paramNames,
                     id,
                     word,
                     normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
@@ -1701,6 +1771,7 @@ class DocSearch {
                 id += 1;
                 searchIndex.push(row);
                 lastPath = row.path;
+                lastParamNames = row.paramNames;
                 if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) {
                     descIndex += 1;
                 }
@@ -1786,8 +1857,7 @@ class DocSearch {
          */
         function newParsedQuery(userQuery) {
             return {
-                original: userQuery,
-                userQuery: userQuery.toLowerCase(),
+                userQuery,
                 elems: [],
                 returned: [],
                 // Total number of "top" elements (does not include generics).
@@ -1909,7 +1979,7 @@ class DocSearch {
             genericsElems: 0,
             typeFilter: null,
             isInBinding: null,
-            userQuery: userQuery.toLowerCase(),
+            userQuery,
         };
         let query = newParsedQuery(userQuery);
 
@@ -2046,18 +2116,21 @@ class DocSearch {
          * marked for removal.
          *
          * @param {[ResultObject]} results
+         * @param {"sig"|"elems"|"returned"|null} typeInfo
+         * @param {ParsedQuery} query
          * @returns {[ResultObject]}
          */
-        const transformResults = results => {
+        const transformResults = (results, typeInfo) => {
             const duplicates = new Set();
             const out = [];
 
             for (const result of results) {
                 if (result.id !== -1) {
-                    const obj = this.searchIndex[result.id];
-                    obj.dist = result.dist;
-                    const res = buildHrefAndPath(obj);
-                    obj.displayPath = pathSplitter(res[0]);
+                    const res = buildHrefAndPath(this.searchIndex[result.id]);
+                    const obj = Object.assign({
+                        dist: result.dist,
+                        displayPath: pathSplitter(res[0]),
+                    }, this.searchIndex[result.id]);
 
                     // To be sure than it some items aren't considered as duplicate.
                     obj.fullPath = res[2] + "|" + obj.ty;
@@ -2076,6 +2149,11 @@ class DocSearch {
                     duplicates.add(obj.fullPath);
                     duplicates.add(res[2]);
 
+                    if (typeInfo !== null) {
+                        obj.displayTypeSignature =
+                            this.formatDisplayTypeSignature(obj, typeInfo);
+                    }
+
                     obj.href = res[1];
                     out.push(obj);
                     if (out.length >= MAX_RESULTS) {
@@ -2087,6 +2165,330 @@ class DocSearch {
         };
 
         /**
+         * Add extra data to result objects, and filter items that have been
+         * marked for removal.
+         *
+         * The output is formatted as an array of hunks, where odd numbered
+         * hunks are highlighted and even numbered ones are not.
+         *
+         * @param {ResultObject} obj
+         * @param {"sig"|"elems"|"returned"|null} typeInfo
+         * @param {ParsedQuery} query
+         * @returns Promise<
+         *   "type": Array<string>,
+         *   "mappedNames": Map<string, string>,
+         *   "whereClause": Map<string, Array<string>>,
+         * >
+         */
+        this.formatDisplayTypeSignature = async(obj, typeInfo) => {
+            let fnInputs = null;
+            let fnOutput = null;
+            let mgens = null;
+            if (typeInfo !== "elems" && typeInfo !== "returned") {
+                fnInputs = unifyFunctionTypes(
+                    obj.type.inputs,
+                    parsedQuery.elems,
+                    obj.type.where_clause,
+                    null,
+                    mgensScratch => {
+                        fnOutput = unifyFunctionTypes(
+                            obj.type.output,
+                            parsedQuery.returned,
+                            obj.type.where_clause,
+                            mgensScratch,
+                            mgensOut => {
+                                mgens = mgensOut;
+                                return true;
+                            },
+                            0,
+                        );
+                        return !!fnOutput;
+                    },
+                    0,
+                );
+            } else {
+                const arr = typeInfo === "elems" ? obj.type.inputs : obj.type.output;
+                const highlighted = unifyFunctionTypes(
+                    arr,
+                    parsedQuery.elems,
+                    obj.type.where_clause,
+                    null,
+                    mgensOut => {
+                        mgens = mgensOut;
+                        return true;
+                    },
+                    0,
+                );
+                if (typeInfo === "elems") {
+                    fnInputs = highlighted;
+                } else {
+                    fnOutput = highlighted;
+                }
+            }
+            if (!fnInputs) {
+                fnInputs = obj.type.inputs;
+            }
+            if (!fnOutput) {
+                fnOutput = obj.type.output;
+            }
+            const mappedNames = new Map();
+            const whereClause = new Map();
+
+            const fnParamNames = obj.paramNames;
+            const queryParamNames = [];
+            /**
+             * Recursively writes a map of IDs to query generic names,
+             * which are later used to map query generic names to function generic names.
+             * For example, when the user writes `X -> Option<X>` and the function
+             * is actually written as `T -> Option<T>`, this function stores the
+             * mapping `(-1, "X")`, and the writeFn function looks up the entry
+             * for -1 to form the final, user-visible mapping of "X is T".
+             *
+             * @param {QueryElement} queryElem
+             */
+            const remapQuery = queryElem => {
+                if (queryElem.id < 0) {
+                    queryParamNames[-1 - queryElem.id] = queryElem.name;
+                }
+                if (queryElem.generics.length > 0) {
+                    queryElem.generics.forEach(remapQuery);
+                }
+                if (queryElem.bindings.size > 0) {
+                    [...queryElem.bindings.values()].flat().forEach(remapQuery);
+                }
+            };
+
+            parsedQuery.elems.forEach(remapQuery);
+            parsedQuery.returned.forEach(remapQuery);
+
+            /**
+             * Write text to a highlighting array.
+             * Index 0 is not highlighted, index 1 is highlighted,
+             * index 2 is not highlighted, etc.
+             *
+             * @param {{name: string, highlighted: bool|undefined}} fnType - input
+             * @param {[string]} result
+             */
+            const pushText = (fnType, result) => {
+                // If !!(result.length % 2) == false, then pushing a new slot starts an even
+                // numbered slot. Even numbered slots are not highlighted.
+                //
+                // `highlighted` will not be defined if an entire subtree is not highlighted,
+                // so `!!` is used to coerce it to boolean. `result.length % 2` is used to
+                // check if the number is even, but it evaluates to a number, so it also
+                // needs coerced to a boolean.
+                if (!!(result.length % 2) === !!fnType.highlighted) {
+                    result.push("");
+                } else if (result.length === 0 && !!fnType.highlighted) {
+                    result.push("");
+                    result.push("");
+                }
+
+                result[result.length - 1] += fnType.name;
+            };
+
+            /**
+             * Write a higher order function type: either a function pointer
+             * or a trait bound on Fn, FnMut, or FnOnce.
+             *
+             * @param {FunctionType} fnType - input
+             * @param {[string]} result
+             */
+            const writeHof = (fnType, result) => {
+                const hofOutput = fnType.bindings.get(this.typeNameIdOfOutput) || [];
+                const hofInputs = fnType.generics;
+                pushText(fnType, result);
+                pushText({name: " (", highlighted: false}, result);
+                let needsComma = false;
+                for (const fnType of hofInputs) {
+                    if (needsComma) {
+                        pushText({ name: ", ", highlighted: false }, result);
+                    }
+                    needsComma = true;
+                    writeFn(fnType, result);
+                }
+                pushText({
+                    name: hofOutput.length === 0 ? ")" : ") -> ",
+                    highlighted: false,
+                }, result);
+                if (hofOutput.length > 1) {
+                    pushText({name: "(", highlighted: false}, result);
+                }
+                needsComma = false;
+                for (const fnType of hofOutput) {
+                    if (needsComma) {
+                        pushText({ name: ", ", highlighted: false }, result);
+                    }
+                    needsComma = true;
+                    writeFn(fnType, result);
+                }
+                if (hofOutput.length > 1) {
+                    pushText({name: ")", highlighted: false}, result);
+                }
+            };
+
+            /**
+             * Write a primitive type with special syntax, like `!` or `[T]`.
+             * Returns `false` if the supplied type isn't special.
+             *
+             * @param {FunctionType} fnType
+             * @param {[string]} result
+             */
+            const writeSpecialPrimitive = (fnType, result) => {
+                if (fnType.id === this.typeNameIdOfArray || fnType.id === this.typeNameIdOfSlice ||
+                    fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) {
+                    const [ob, sb] =
+                        fnType.id === this.typeNameIdOfArray ||
+                            fnType.id === this.typeNameIdOfSlice ?
+                        ["[", "]"] :
+                        ["(", ")"];
+                    pushText({ name: ob, highlighted: fnType.highlighted }, result);
+                    onEachBtwn(
+                        fnType.generics,
+                        nested => writeFn(nested, result),
+                        () => pushText({ name: ", ", highlighted: false }, result),
+                    );
+                    pushText({ name: sb, highlighted: fnType.highlighted }, result);
+                    return true;
+                } else if (fnType.id === this.typeNameIdOfReference) {
+                    pushText({ name: "&", highlighted: fnType.highlighted }, result);
+                    let prevHighlighted = false;
+                    onEachBtwn(
+                        fnType.generics,
+                        value => {
+                            prevHighlighted = value.highlighted;
+                            writeFn(value, result);
+                        },
+                        value => pushText({
+                            name: " ",
+                            highlighted: prevHighlighted && value.highlighted,
+                        }, result),
+                    );
+                    return true;
+                } else if (fnType.id === this.typeNameIdOfFn) {
+                    writeHof(fnType, result);
+                    return true;
+                }
+                return false;
+            };
+            /**
+             * Write a type. This function checks for special types,
+             * like slices, with their own formatting. It also handles
+             * updating the where clause and generic type param map.
+             *
+             * @param {FunctionType} fnType
+             * @param {[string]} result
+             */
+            const writeFn = (fnType, result) => {
+                if (fnType.id < 0) {
+                    if (fnParamNames[-1 - fnType.id] === "") {
+                        for (const nested of fnType.generics) {
+                            writeFn(nested, result);
+                        }
+                        return;
+                    } 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],
+                        highlighted: !!fnType.highlighted,
+                    }, result);
+                    const where = [];
+                    onEachBtwn(
+                        fnType.generics,
+                        nested => writeFn(nested, where),
+                        () => pushText({ name: " + ", highlighted: false }, where),
+                    );
+                    if (where.length > 0) {
+                        whereClause.set(fnParamNames[-1 - fnType.id], where);
+                    }
+                } else {
+                    if (fnType.ty === TY_PRIMITIVE) {
+                        if (writeSpecialPrimitive(fnType, result)) {
+                            return;
+                        }
+                    } else if (fnType.ty === TY_TRAIT && (
+                        fnType.id === this.typeNameIdOfFn ||
+                            fnType.id === this.typeNameIdOfFnMut ||
+                            fnType.id === this.typeNameIdOfFnOnce)) {
+                        writeHof(fnType, result);
+                        return;
+                    }
+                    pushText(fnType, result);
+                    let hasBindings = false;
+                    if (fnType.bindings.size > 0) {
+                        onEachBtwn(
+                            fnType.bindings,
+                            ([key, values]) => {
+                                const name = this.assocTypeIdNameMap.get(key);
+                                if (values.length === 1 && values[0].id < 0 &&
+                                    `${fnType.name}::${name}` === fnParamNames[-1 - values[0].id]) {
+                                    // the internal `Item=Iterator::Item` type variable should be
+                                    // shown in the where clause and name mapping output, but is
+                                    // redundant in this spot
+                                    for (const value of values) {
+                                        writeFn(value, []);
+                                    }
+                                    return true;
+                                }
+                                if (!hasBindings) {
+                                    hasBindings = true;
+                                    pushText({ name: "<", highlighted: false }, result);
+                                }
+                                pushText({ name, highlighted: false }, result);
+                                pushText({
+                                    name: values.length !== 1 ? "=(" : "=",
+                                    highlighted: false,
+                                }, result);
+                                onEachBtwn(
+                                    values || [],
+                                    value => writeFn(value, result),
+                                    () => pushText({ name: " + ",  highlighted: false }, result),
+                                );
+                                if (values.length !== 1) {
+                                    pushText({ name: ")", highlighted: false }, result);
+                                }
+                            },
+                            () => pushText({ name: ", ",  highlighted: false }, result),
+                        );
+                    }
+                    if (fnType.generics.length > 0) {
+                        pushText({ name: hasBindings ? ", " : "<", highlighted: false }, result);
+                    }
+                    onEachBtwn(
+                        fnType.generics,
+                        value => writeFn(value, result),
+                        () => pushText({ name: ", ",  highlighted: false }, result),
+                    );
+                    if (hasBindings || fnType.generics.length > 0) {
+                        pushText({ name: ">", highlighted: false }, result);
+                    }
+                }
+            };
+            const type = [];
+            onEachBtwn(
+                fnInputs,
+                fnType => writeFn(fnType, type),
+                () => pushText({ name: ", ",  highlighted: false }, type),
+            );
+            pushText({ name: " -> ", highlighted: false }, type);
+            onEachBtwn(
+                fnOutput,
+                fnType => writeFn(fnType, type),
+                () => pushText({ name: ", ",  highlighted: false }, type),
+            );
+
+            return {type, mappedNames, whereClause};
+        };
+
+        /**
          * This function takes a result map, and sorts it by various criteria, including edit
          * distance, substring match, and the crate it comes from.
          *
@@ -2095,9 +2497,9 @@ class DocSearch {
          * @param {string} preferredCrate
          * @returns {Promise<[ResultObject]>}
          */
-        const sortResults = async(results, isType, preferredCrate) => {
+        const sortResults = async(results, typeInfo, preferredCrate) => {
             const userQuery = parsedQuery.userQuery;
-            const casedUserQuery = parsedQuery.original;
+            const normalizedUserQuery = parsedQuery.userQuery.toLowerCase();
             const result_list = [];
             for (const result of results.values()) {
                 result.item = this.searchIndex[result.id];
@@ -2109,15 +2511,15 @@ class DocSearch {
                 let a, b;
 
                 // sort by exact case-sensitive match
-                a = (aaa.item.name !== casedUserQuery);
-                b = (bbb.item.name !== casedUserQuery);
+                a = (aaa.item.name !== userQuery);
+                b = (bbb.item.name !== userQuery);
                 if (a !== b) {
                     return a - b;
                 }
 
                 // sort by exact match with regard to the last word (mismatch goes later)
-                a = (aaa.word !== userQuery);
-                b = (bbb.word !== userQuery);
+                a = (aaa.word !== normalizedUserQuery);
+                b = (bbb.word !== normalizedUserQuery);
                 if (a !== b) {
                     return a - b;
                 }
@@ -2205,30 +2607,30 @@ class DocSearch {
                 return 0;
             });
 
-            return transformResults(result_list);
+            return transformResults(result_list, typeInfo);
         };
 
         /**
          * This function checks if a list of search query `queryElems` can all be found in the
          * search index (`fnTypes`).
          *
-         * This function returns `true` on a match, or `false` if none. If `solutionCb` is
+         * This function returns highlighted results on a match, or `null`. If `solutionCb` is
          * supplied, it will call that function with mgens, and that callback can accept or
-         * reject the result bu returning `true` or `false`. If the callback returns false,
-         * then this function will try with a different solution, or bail with false if it
+         * reject the result by returning `true` or `false`. If the callback returns false,
+         * then this function will try with a different solution, or bail with null if it
          * runs out of candidates.
          *
          * @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 {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>>>>>
          *
-         * @return {boolean} - Returns true if a match, false otherwise.
+         * @return {[FunctionType]|null} - Returns highlighed results if a match, null otherwise.
          */
         function unifyFunctionTypes(
             fnTypesIn,
@@ -2239,17 +2641,17 @@ class DocSearch {
             unboxingDepth,
         ) {
             if (unboxingDepth >= UNBOXING_LIMIT) {
-                return false;
+                return null;
             }
             /**
              * @type Map<integer, integer>|null
              */
             const mgens = mgensIn === null ? null : new Map(mgensIn);
             if (queryElems.length === 0) {
-                return !solutionCb || solutionCb(mgens);
+                return solutionCb(mgens) ? fnTypesIn : null;
             }
             if (!fnTypesIn || fnTypesIn.length === 0) {
-                return false;
+                return null;
             }
             const ql = queryElems.length;
             const fl = fnTypesIn.length;
@@ -2258,26 +2660,45 @@ class DocSearch {
             if (ql === 1 && queryElems[0].generics.length === 0
                 && queryElems[0].bindings.size === 0) {
                 const queryElem = queryElems[0];
-                for (const fnType of fnTypesIn) {
+                for (const [i, fnType] of fnTypesIn.entries()) {
                     if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) {
                         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)) {
-                            return true;
+                            const highlighted = [...fnTypesIn];
+                            highlighted[i] = Object.assign({
+                                highlighted: true,
+                            }, fnType, {
+                                generics: whereClause[-1 - fnType.id],
+                            });
+                            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
-                        return true;
+                        const highlighted = [...fnTypesIn];
+                        highlighted[i] = Object.assign({
+                            highlighted: true,
+                        }, fnType, {
+                            generics: unifyGenericTypes(
+                                fnType.generics,
+                                queryElem.generics,
+                                whereClause,
+                                mgens ? new Map(mgens) : null,
+                                solutionCb,
+                                unboxingDepth,
+                            ) || fnType.generics,
+                        });
+                        return highlighted;
                     }
                 }
-                for (const fnType of fnTypesIn) {
+                for (const [i, fnType] of fnTypesIn.entries()) {
                     if (!unifyFunctionTypeIsUnboxCandidate(
                         fnType,
                         queryElem,
@@ -2288,31 +2709,42 @@ 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);
-                        if (unifyFunctionTypes(
+                        const highlightedGenerics = unifyFunctionTypes(
                             whereClause[(-fnType.id) - 1],
                             queryElems,
                             whereClause,
-                            mgensScratch,
+                            mgens,
                             solutionCb,
                             unboxingDepth + 1,
-                        )) {
-                            return true;
+                        );
+                        if (highlightedGenerics) {
+                            const highlighted = [...fnTypesIn];
+                            highlighted[i] = Object.assign({
+                                highlighted: true,
+                            }, fnType, {
+                                generics: highlightedGenerics,
+                            });
+                            return highlighted;
+                        }
+                    } else {
+                        const highlightedGenerics = unifyFunctionTypes(
+                            [...Array.from(fnType.bindings.values()).flat(), ...fnType.generics],
+                            queryElems,
+                            whereClause,
+                            mgens ? new Map(mgens) : null,
+                            solutionCb,
+                            unboxingDepth + 1,
+                        );
+                        if (highlightedGenerics) {
+                            const highlighted = [...fnTypesIn];
+                            highlighted[i] = Object.assign({}, fnType, {
+                                generics: highlightedGenerics,
+                                bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => {
+                                    return [k, highlightedGenerics.splice(0, v.length)];
+                                })),
+                            });
+                            return highlighted;
                         }
-                    } else if (unifyFunctionTypes(
-                        [...fnType.generics, ...Array.from(fnType.bindings.values()).flat()],
-                        queryElems,
-                        whereClause,
-                        mgens ? new Map(mgens) : null,
-                        solutionCb,
-                        unboxingDepth + 1,
-                    )) {
-                        return true;
                     }
                 }
                 return false;
@@ -2352,11 +2784,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;
                 }
@@ -2369,6 +2801,8 @@ class DocSearch {
                 if (!queryElemsTmp) {
                     queryElemsTmp = queryElems.slice(0, qlast);
                 }
+                let unifiedGenerics = [];
+                let unifiedGenericsMgens = null;
                 const passesUnification = unifyFunctionTypes(
                     fnTypes,
                     queryElemsTmp,
@@ -2377,7 +2811,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,
@@ -2391,7 +2825,7 @@ class DocSearch {
                         }
                         const simplifiedGenerics = solution.simplifiedGenerics;
                         for (const simplifiedMgens of solution.mgens) {
-                            const passesUnification = unifyFunctionTypes(
+                            unifiedGenerics = unifyGenericTypes(
                                 simplifiedGenerics,
                                 queryElem.generics,
                                 whereClause,
@@ -2399,7 +2833,8 @@ class DocSearch {
                                 solutionCb,
                                 unboxingDepth,
                             );
-                            if (passesUnification) {
+                            if (unifiedGenerics !== null) {
+                                unifiedGenericsMgens = simplifiedMgens;
                                 return true;
                             }
                         }
@@ -2408,7 +2843,23 @@ class DocSearch {
                     unboxingDepth,
                 );
                 if (passesUnification) {
-                    return true;
+                    passesUnification.length = fl;
+                    passesUnification[flast] = passesUnification[i];
+                    passesUnification[i] = Object.assign({}, fnType, {
+                        highlighted: true,
+                        generics: unifiedGenerics,
+                        bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => {
+                            return [k, queryElem.bindings.has(k) ? unifyFunctionTypes(
+                                v,
+                                queryElem.bindings.get(k),
+                                whereClause,
+                                unifiedGenericsMgens,
+                                solutionCb,
+                                unboxingDepth,
+                            ) : unifiedGenerics.splice(0, v.length)];
+                        })),
+                    });
+                    return passesUnification;
                 }
                 // backtrack
                 fnTypes[flast] = fnTypes[i];
@@ -2426,16 +2877,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;
@@ -2443,18 +2884,225 @@ class DocSearch {
                     Array.from(fnType.bindings.values()).flat() :
                     [];
                 const passesUnification = unifyFunctionTypes(
-                    fnTypes.toSpliced(i, 1, ...generics, ...bindings),
+                    fnTypes.toSpliced(i, 1, ...bindings, ...generics),
                     queryElems,
                     whereClause,
-                    mgensScratch,
+                    mgens,
                     solutionCb,
                     unboxingDepth + 1,
                 );
                 if (passesUnification) {
-                    return true;
+                    const highlightedGenerics = passesUnification.slice(
+                        i,
+                        i + generics.length + bindings.length,
+                    );
+                    const highlightedFnType = Object.assign({}, fnType, {
+                        generics: highlightedGenerics,
+                        bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => {
+                            return [k, highlightedGenerics.splice(0, v.length)];
+                        })),
+                    });
+                    return passesUnification.toSpliced(
+                        i,
+                        generics.length + bindings.length,
+                        highlightedFnType,
+                    );
                 }
             }
-            return false;
+            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.
@@ -2467,7 +3115,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) => {
@@ -2477,22 +3125,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 {
@@ -2569,7 +3208,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]}}
@@ -2625,7 +3264,7 @@ class DocSearch {
                     }
                 });
                 if (simplifiedGenerics.length > 0) {
-                    simplifiedGenerics = [...simplifiedGenerics, ...binds];
+                    simplifiedGenerics = [...binds, ...simplifiedGenerics];
                 } else {
                     simplifiedGenerics = binds;
                 }
@@ -2637,7 +3276,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}
          */
@@ -2651,19 +3290,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>`
@@ -2672,10 +3302,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(),
@@ -2719,7 +3350,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.
          */
@@ -2727,33 +3358,42 @@ class DocSearch {
             if (unboxingDepth >= UNBOXING_LIMIT) {
                 return false;
             }
-            if (row.bindings.size === 0 && elem.bindings.size === 0) {
-                if (elem.id < 0 && mgens === null) {
-                    return row.id < 0 || checkIfInList(
-                        row.generics,
-                        elem,
-                        whereClause,
-                        mgens,
-                        unboxingDepth + 1,
-                    );
-                }
-                if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 &&
-                    typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 &&
-                    // special case
-                    elem.id !== this.typeNameIdOfArrayOrSlice
-                    && elem.id !== this.typeNameIdOfTupleOrUnit
-                    && elem.id !== this.typeNameIdOfHof
-                ) {
-                    return row.id === elem.id || checkIfInList(
-                        row.generics,
-                        elem,
-                        whereClause,
-                        mgens,
-                        unboxingDepth,
-                    );
+            if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 &&
+                row.generics.length === 0 && elem.generics.length === 0 &&
+                row.bindings.size === 0 && elem.bindings.size === 0 &&
+                // special case
+                elem.id !== this.typeNameIdOfArrayOrSlice &&
+                elem.id !== this.typeNameIdOfHof &&
+                elem.id !== this.typeNameIdOfTupleOrUnit
+            ) {
+                return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty);
+            } else {
+                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 unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth);
+            return true;
         };
 
         /**
@@ -3075,7 +3715,7 @@ class DocSearch {
                         parsedQuery.returned,
                         row.type.where_clause,
                         mgens,
-                        null,
+                        checkTypeMgensForConflict,
                         0, // unboxing depth
                     );
                 },
@@ -3178,21 +3818,25 @@ class DocSearch {
                 if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
                     && elem.generics.length === 0 && elem.bindings.size === 0)
                     || elem.typeFilter === TY_GENERIC) {
-                    if (genericSymbols.has(elem.name)) {
-                        elem.id = genericSymbols.get(elem.name);
+                    if (genericSymbols.has(elem.normalizedPathLast)) {
+                        elem.id = genericSymbols.get(elem.normalizedPathLast);
                     } else {
                         elem.id = -(genericSymbols.size + 1);
-                        genericSymbols.set(elem.name, elem.id);
+                        genericSymbols.set(elem.normalizedPathLast, elem.id);
                     }
-                    if (elem.typeFilter === -1 && elem.name.length >= 3) {
+                    if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) {
                         // Silly heuristic to catch if the user probably meant
                         // to not write a generic parameter. We don't use it,
                         // just bring it up.
-                        const maxPartDistance = Math.floor(elem.name.length / 3);
+                        const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3);
                         let matchDist = maxPartDistance + 1;
                         let matchName = "";
                         for (const name of this.typeNameIdMap.keys()) {
-                            const dist = editDistance(name, elem.name, maxPartDistance);
+                            const dist = editDistance(
+                                name,
+                                elem.normalizedPathLast,
+                                maxPartDistance,
+                            );
                             if (dist <= matchDist && dist <= maxPartDistance) {
                                 if (dist === matchDist && matchName > name) {
                                     continue;
@@ -3239,14 +3883,13 @@ class DocSearch {
                 );
             };
 
-            const fps = new Set();
             for (const elem of parsedQuery.elems) {
                 convertNameToId(elem);
-                this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint, fps);
+                this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint);
             }
             for (const elem of parsedQuery.returned) {
                 convertNameToId(elem);
-                this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint, fps);
+                this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint);
             }
 
             if (parsedQuery.foundElems === 1 && !parsedQuery.hasReturnArrow) {
@@ -3294,17 +3937,18 @@ class DocSearch {
             innerRunQuery();
         }
 
+        const isType = parsedQuery.foundElems !== 1 || parsedQuery.hasReturnArrow;
         const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([
-            sortResults(results_in_args, true, currentCrate),
-            sortResults(results_returned, true, currentCrate),
-            sortResults(results_others, false, currentCrate),
+            sortResults(results_in_args, "elems", currentCrate),
+            sortResults(results_returned, "returned", currentCrate),
+            sortResults(results_others, (isType ? "query" : null), currentCrate),
         ]);
         const ret = createQueryResults(
             sorted_in_args,
             sorted_returned,
             sorted_others,
             parsedQuery);
-        await handleAliases(ret, parsedQuery.original.replace(/"/g, ""),
+        await handleAliases(ret, parsedQuery.userQuery.replace(/"/g, ""),
             filterCrates, currentCrate);
         await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => {
             const descs = await Promise.all(list.map(result => {
@@ -3324,6 +3968,7 @@ class DocSearch {
     }
 }
 
+
 // ==================== Core search logic end ====================
 
 let rawSearchIndex;
@@ -3455,15 +4100,18 @@ function focusSearchResult() {
  * @param {Array<?>}    array   - The search results for this tab
  * @param {ParsedQuery} query
  * @param {boolean}     display - True if this is the active tab
+ * @param {"sig"|"elems"|"returned"|null} typeInfo
  */
 async function addTab(array, query, display) {
     const extraClass = display ? " active" : "";
 
-    const output = document.createElement("div");
+    const output = document.createElement(
+        array.length === 0 && query.error === null ? "div" : "ul",
+    );
     if (array.length > 0) {
         output.className = "search-results " + extraClass;
 
-        for (const item of array) {
+        const lis = Promise.all(array.map(async item => {
             const name = item.name;
             const type = itemTypes[item.ty];
             const longType = longItemTypes[item.ty];
@@ -3473,7 +4121,7 @@ async function addTab(array, query, display) {
             link.className = "result-" + type;
             link.href = item.href;
 
-            const resultName = document.createElement("div");
+            const resultName = document.createElement("span");
             resultName.className = "result-name";
 
             resultName.insertAdjacentHTML(
@@ -3496,10 +4144,73 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             const description = document.createElement("div");
             description.className = "desc";
             description.insertAdjacentHTML("beforeend", item.desc);
+            if (item.displayTypeSignature) {
+                const {type, mappedNames, whereClause} = await item.displayTypeSignature;
+                const displayType = document.createElement("div");
+                type.forEach((value, index) => {
+                    if (index % 2 !== 0) {
+                        const highlight = document.createElement("strong");
+                        highlight.appendChild(document.createTextNode(value));
+                        displayType.appendChild(highlight);
+                    } else {
+                        displayType.appendChild(document.createTextNode(value));
+                    }
+                });
+                if (mappedNames.size > 0 || whereClause.size > 0) {
+                    let addWhereLineFn = () => {
+                        const line = document.createElement("div");
+                        line.className = "where";
+                        line.appendChild(document.createTextNode("where"));
+                        displayType.appendChild(line);
+                        addWhereLineFn = () => {};
+                    };
+                    for (const [qname, name] of mappedNames) {
+                        // don't care unless the generic name is different
+                        if (name === qname) {
+                            continue;
+                        }
+                        addWhereLineFn();
+                        const line = document.createElement("div");
+                        line.className = "where";
+                        line.appendChild(document.createTextNode(`    ${qname} matches `));
+                        const lineStrong = document.createElement("strong");
+                        lineStrong.appendChild(document.createTextNode(name));
+                        line.appendChild(lineStrong);
+                        displayType.appendChild(line);
+                    }
+                    for (const [name, innerType] of whereClause) {
+                        // don't care unless there's at least one highlighted entry
+                        if (innerType.length <= 1) {
+                            continue;
+                        }
+                        addWhereLineFn();
+                        const line = document.createElement("div");
+                        line.className = "where";
+                        line.appendChild(document.createTextNode(`    ${name}: `));
+                        innerType.forEach((value, index) => {
+                            if (index % 2 !== 0) {
+                                const highlight = document.createElement("strong");
+                                highlight.appendChild(document.createTextNode(value));
+                                line.appendChild(highlight);
+                            } else {
+                                line.appendChild(document.createTextNode(value));
+                            }
+                        });
+                        displayType.appendChild(line);
+                    }
+                }
+                displayType.className = "type-signature";
+                link.appendChild(displayType);
+            }
 
             link.appendChild(description);
-            output.appendChild(link);
-        }
+            return link;
+        }));
+        lis.then(lis => {
+            for (const li of lis) {
+                output.appendChild(li);
+            }
+        });
     } else if (query.error === null) {
         output.className = "search-failed" + extraClass;
         output.innerHTML = "No results :(<br/>" +
@@ -3516,7 +4227,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
             " <a href=\"https://crates.io/\">crates.io</a>.</li></ul>";
     }
-    return [output, array.length];
+    return output;
 }
 
 function makeTabHeader(tabNb, text, nbElems) {
@@ -3573,24 +4284,18 @@ async function showResults(results, go_to_first, filterCrates) {
 
     currentResults = results.query.userQuery;
 
-    const [ret_others, ret_in_args, ret_returned] = await Promise.all([
-        addTab(results.others, results.query, true),
-        addTab(results.in_args, results.query, false),
-        addTab(results.returned, results.query, false),
-    ]);
-
     // Navigate to the relevant tab if the current tab is empty, like in case users search
     // for "-> String". If they had selected another tab previously, they have to click on
     // it again.
     let currentTab = searchState.currentTab;
-    if ((currentTab === 0 && ret_others[1] === 0) ||
-        (currentTab === 1 && ret_in_args[1] === 0) ||
-        (currentTab === 2 && ret_returned[1] === 0)) {
-        if (ret_others[1] !== 0) {
+    if ((currentTab === 0 && results.others.length === 0) ||
+        (currentTab === 1 && results.in_args.length === 0) ||
+        (currentTab === 2 && results.returned.length === 0)) {
+        if (results.others.length !== 0) {
             currentTab = 0;
-        } else if (ret_in_args[1] !== 0) {
+        } else if (results.in_args.length) {
             currentTab = 1;
-        } else if (ret_returned[1] !== 0) {
+        } else if (results.returned.length) {
             currentTab = 2;
         }
     }
@@ -3619,14 +4324,14 @@ async function showResults(results, go_to_first, filterCrates) {
         });
         output += `<h3 class="error">Query parser error: "${error.join("")}".</h3>`;
         output += "<div id=\"search-tabs\">" +
-            makeTabHeader(0, "In Names", ret_others[1]) +
+            makeTabHeader(0, "In Names", results.others.length) +
             "</div>";
         currentTab = 0;
     } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) {
         output += "<div id=\"search-tabs\">" +
-            makeTabHeader(0, "In Names", ret_others[1]) +
-            makeTabHeader(1, "In Parameters", ret_in_args[1]) +
-            makeTabHeader(2, "In Return Types", ret_returned[1]) +
+            makeTabHeader(0, "In Names", results.others.length) +
+            makeTabHeader(1, "In Parameters", results.in_args.length) +
+            makeTabHeader(2, "In Return Types", results.returned.length) +
             "</div>";
     } else {
         const signatureTabTitle =
@@ -3634,7 +4339,7 @@ async function showResults(results, go_to_first, filterCrates) {
                 results.query.returned.length === 0 ? "In Function Parameters" :
                     "In Function Signatures";
         output += "<div id=\"search-tabs\">" +
-            makeTabHeader(0, signatureTabTitle, ret_others[1]) +
+            makeTabHeader(0, signatureTabTitle, results.others.length) +
             "</div>";
         currentTab = 0;
     }
@@ -3656,11 +4361,17 @@ async function showResults(results, go_to_first, filterCrates) {
             `Consider searching for "${targ}" instead.</h3>`;
     }
 
+    const [ret_others, ret_in_args, ret_returned] = await Promise.all([
+        addTab(results.others, results.query, currentTab === 0),
+        addTab(results.in_args, results.query, currentTab === 1),
+        addTab(results.returned, results.query, currentTab === 2),
+    ]);
+
     const resultsElem = document.createElement("div");
     resultsElem.id = "results";
-    resultsElem.appendChild(ret_others[0]);
-    resultsElem.appendChild(ret_in_args[0]);
-    resultsElem.appendChild(ret_returned[0]);
+    resultsElem.appendChild(ret_others);
+    resultsElem.appendChild(ret_in_args);
+    resultsElem.appendChild(ret_returned);
 
     search.innerHTML = output;
     if (searchState.rustdocToolbar) {
@@ -3724,11 +4435,11 @@ async function search(forced) {
     }
 
     // Update document title to maintain a meaningful browser history
-    searchState.title = "\"" + query.original + "\" Search - Rust";
+    searchState.title = "\"" + query.userQuery + "\" Search - Rust";
 
     // Because searching is incremental by character, only the most
     // recent search query is added to the browser history.
-    updateSearchHistory(buildUrl(query.original, filterCrates));
+    updateSearchHistory(buildUrl(query.userQuery, filterCrates));
 
     await showResults(
         await docSearch.execQuery(query, filterCrates, window.currentCrate),
@@ -3942,4 +4653,3 @@ if (typeof window !== "undefined") {
     // exports.
     initSearch(new Map());
 }
-})();
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index e162ba033cc..7aa5e102e6d 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -2,6 +2,14 @@
 const fs = require("fs");
 const path = require("path");
 
+
+function arrayToCode(array) {
+    return array.map((value, index) => {
+        value = value.split("&nbsp;").join(" ");
+        return (index % 2 === 1) ? ("`" + value + "`") : value;
+    }).join("");
+}
+
 function loadContent(content) {
     const Module = module.constructor;
     const m = new Module();
@@ -84,7 +92,6 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position)
     if (fullPath.length === 0) {
         fieldsToCheck = [
             "foundElems",
-            "original",
             "returned",
             "userQuery",
             "error",
@@ -181,15 +188,7 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 if (!result_v.forEach) {
                     throw result_v;
                 }
-                result_v.forEach((value, index) => {
-                    value = value.split("&nbsp;").join(" ");
-                    if (index % 2 === 1) {
-                        result_v[index] = "`" + value + "`";
-                    } else {
-                        result_v[index] = value;
-                    }
-                });
-                result_v = result_v.join("");
+                result_v = arrayToCode(result_v);
             }
             const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key;
             valueCheck(obj_path, expected[key], result_v, error_text, queryName);
@@ -437,9 +436,41 @@ function loadSearchJS(doc_folder, resource_suffix) {
     searchModule.initSearch(searchIndex.searchIndex);
     const docSearch = searchModule.docSearch;
     return {
-        doSearch: function(queryStr, filterCrate, currentCrate) {
-            return docSearch.execQuery(searchModule.parseQuery(queryStr),
+        doSearch: async function(queryStr, filterCrate, currentCrate) {
+            const result = await docSearch.execQuery(searchModule.parseQuery(queryStr),
                 filterCrate, currentCrate);
+            for (const tab in result) {
+                if (!Object.prototype.hasOwnProperty.call(result, tab)) {
+                    continue;
+                }
+                if (!(result[tab] instanceof Array)) {
+                    continue;
+                }
+                for (const entry of result[tab]) {
+                    for (const key in entry) {
+                        if (!Object.prototype.hasOwnProperty.call(entry, key)) {
+                            continue;
+                        }
+                        if (key === "displayTypeSignature") {
+                            const {type, mappedNames, whereClause} =
+                                await entry.displayTypeSignature;
+                            entry.displayType = arrayToCode(type);
+                            entry.displayMappedNames = [...mappedNames.entries()]
+                                .map(([name, qname]) => {
+                                    return `${name} = ${qname}`;
+                                }).join(", ");
+                            entry.displayWhereClause = [...whereClause.entries()]
+                                .flatMap(([name, value]) => {
+                                    if (value.length === 0) {
+                                        return [];
+                                    }
+                                    return [`${name}: ${arrayToCode(value)}`];
+                                }).join(", ");
+                        }
+                    }
+                }
+            }
+            return result;
         },
         getCorrections: function(queryStr, filterCrate, currentCrate) {
             const parsedQuery = searchModule.parseQuery(queryStr);
diff --git a/tests/rustdoc-gui/search-about-this-result.goml b/tests/rustdoc-gui/search-about-this-result.goml
new file mode 100644
index 00000000000..1d45c06dc43
--- /dev/null
+++ b/tests/rustdoc-gui/search-about-this-result.goml
@@ -0,0 +1,42 @@
+// Check the "About this Result" popover.
+// Try a complex result.
+go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=scroll_traits::Iterator<T>,(T->bool)->(Extend<T>,Extend<T>)"
+
+// These two commands are used to be sure the search will be run.
+focus: ".search-input"
+press-key: "Enter"
+
+wait-for: "#search-tabs"
+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()='(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']"
+assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='bool']"
+assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='Extend']"
+
+assert-text: ("div.type-signature div.where:nth-child(4)", "where")
+assert-text: ("div.type-signature div.where:nth-child(5)", "    T matches Iterator::Item")
+assert-text: ("div.type-signature div.where:nth-child(6)", "    F: FnMut (&Iterator::Item) -> bool")
+assert-text: ("div.type-signature div.where:nth-child(7)", "    B: Default + Extend<Iterator::Item>")
+
+// Try a simple result that *won't* give an info box.
+go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=F->lib2::WhereWhitespace<T>"
+
+// These two commands are used to be sure the search will be run.
+focus: ".search-input"
+press-key: "Enter"
+
+wait-for: "#search-tabs"
+assert-text: ("//div[@class='type-signature']", "F -> WhereWhitespace<T>")
+assert-count: ("#search-tabs button", 1)
+assert-count: (".search-results > a", 1)
+assert-count: ("//div[@class='type-signature']/div[@class='where']", 0)
+
+assert: "//div[@class='type-signature']/strong[text()='F']"
+assert: "//div[@class='type-signature']/strong[text()='WhereWhitespace']"
+assert: "//div[@class='type-signature']/strong[text()='T']"
diff --git a/tests/rustdoc-gui/search-corrections.goml b/tests/rustdoc-gui/search-corrections.goml
index b81b1f382a9..f80675730c4 100644
--- a/tests/rustdoc-gui/search-corrections.goml
+++ b/tests/rustdoc-gui/search-corrections.goml
@@ -24,7 +24,7 @@ assert-css: (".search-corrections", {
 })
 assert-text: (
     ".search-corrections",
-    "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
+    "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
 )
 
 // Corrections do get shown on the "In Return Type" tab.
@@ -35,7 +35,7 @@ assert-css: (".search-corrections", {
 })
 assert-text: (
     ".search-corrections",
-    "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
+    "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
 )
 
 // Now, explicit return values
@@ -52,7 +52,7 @@ assert-css: (".search-corrections", {
 })
 assert-text: (
     ".search-corrections",
-    "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
+    "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
 )
 
 // Now, generic correction
@@ -69,7 +69,7 @@ assert-css: (".search-corrections", {
 })
 assert-text: (
     ".search-corrections",
-    "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+    "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
 )
 
 // Now, generic correction plus error
@@ -86,7 +86,7 @@ assert-css: (".search-corrections", {
 })
 assert-text: (
     ".search-corrections",
-    "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+    "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
 )
 
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
@@ -102,5 +102,5 @@ assert-css: (".error", {
 })
 assert-text: (
     ".error",
-    "Query parser error: \"Generic type parameter notablestructwithlongnamr does not accept generic parameters\"."
+    "Query parser error: \"Generic type parameter NotableStructWithLongNamr does not accept generic parameters\"."
 )
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 e154fa707ab..3be18e6adf3 100644
--- a/tests/rustdoc-js-std/option-type-signatures.js
+++ b/tests/rustdoc-js-std/option-type-signatures.js
@@ -6,79 +6,198 @@ const EXPECTED = [
     {
         'query': 'option, fnonce -> option',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'map' },
+            {
+                'path': 'std::option::Option',
+                'name': 'map',
+                'displayType': '`Option`<T>, F -> `Option`<U>',
+                'displayWhereClause': "F: `FnOnce` (T) -> U",
+            },
+        ],
+    },
+    {
+        'query': 'option<t>, fnonce -> option',
+        'others': [
+            {
+                'path': 'std::option::Option',
+                'name': 'map',
+                'displayType': '`Option`<`T`>, F -> `Option`<U>',
+                'displayWhereClause': "F: `FnOnce` (T) -> U",
+            },
         ],
     },
     {
         'query': 'option -> default',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'unwrap_or_default' },
-            { 'path': 'std::option::Option', 'name': 'get_or_insert_default' },
+            {
+                'path': 'std::option::Option',
+                'name': 'unwrap_or_default',
+                'displayType': '`Option`<T> -> `T`',
+                'displayWhereClause': "T: `Default`",
+            },
+            {
+                'path': 'std::option::Option',
+                'name': 'get_or_insert_default',
+                'displayType': '&mut `Option`<T> -> &mut `T`',
+                'displayWhereClause': "T: `Default`",
+            },
         ],
     },
     {
         'query': 'option -> []',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'as_slice' },
-            { 'path': 'std::option::Option', 'name': 'as_mut_slice' },
+            {
+                'path': 'std::option::Option',
+                'name': 'as_slice',
+                'displayType': '&`Option`<T> -> &`[`T`]`',
+            },
+            {
+                'path': 'std::option::Option',
+                'name': 'as_mut_slice',
+                'displayType': '&mut `Option`<T> -> &mut `[`T`]`',
+            },
         ],
     },
     {
         'query': 'option<t>, option<t> -> option<t>',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'or' },
-            { 'path': 'std::option::Option', 'name': 'xor' },
+            {
+                'path': 'std::option::Option',
+                'name': 'or',
+                'displayType': '`Option`<`T`>, `Option`<`T`> -> `Option`<`T`>',
+            },
+            {
+                'path': 'std::option::Option',
+                'name': 'xor',
+                'displayType': '`Option`<`T`>, `Option`<`T`> -> `Option`<`T`>',
+            },
         ],
     },
     {
         'query': 'option<t>, option<u> -> option<u>',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'and' },
-            { 'path': 'std::option::Option', 'name': 'zip' },
+            {
+                'path': 'std::option::Option',
+                'name': 'and',
+                'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>',
+            },
         ],
     },
     {
         'query': 'option<t>, option<u> -> option<t>',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'and' },
-            { 'path': 'std::option::Option', 'name': 'zip' },
+            {
+                'path': 'std::option::Option',
+                'name': 'and',
+                'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>',
+            },
+            {
+                'path': 'std::option::Option',
+                'name': 'zip',
+                'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, U)>',
+            },
         ],
     },
     {
-        'query': 'option<t>, option<u> -> option<t, u>',
+        'query': 'option<t>, option<u> -> option<(t, u)>',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'zip' },
+            {
+                'path': 'std::option::Option',
+                'name': 'zip',
+                'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`(T`, `U)`>',
+            },
         ],
     },
     {
         'query': 'option<t>, e -> result<t, e>',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'ok_or' },
-            { 'path': 'std::result::Result', 'name': 'transpose' },
+            {
+                'path': 'std::option::Option',
+                'name': 'ok_or',
+                'displayType': '`Option`<`T`>, `E` -> `Result`<`T`, `E`>',
+            },
+            {
+                'path': 'std::result::Result',
+                'name': 'transpose',
+                'displayType': 'Result<`Option`<`T`>, `E`> -> Option<`Result`<`T`, `E`>>',
+            },
         ],
     },
     {
         'query': 'result<option<t>, e> -> option<result<t, e>>',
         'others': [
-            { 'path': 'std::result::Result', 'name': 'transpose' },
+            {
+                'path': 'std::result::Result',
+                'name': 'transpose',
+                'displayType': '`Result`<`Option`<`T`>, `E`> -> `Option`<`Result`<`T`, `E`>>',
+            },
         ],
     },
     {
         'query': 'option<t>, option<t> -> bool',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'eq' },
+            {
+                'path': 'std::option::Option',
+                'name': 'eq',
+                'displayType': '&`Option`<`T`>, &`Option`<`T`> -> `bool`',
+            },
         ],
     },
     {
         'query': 'option<option<t>> -> option<t>',
         'others': [
-            { 'path': 'std::option::Option', 'name': 'flatten' },
+            {
+                'path': 'std::option::Option',
+                'name': 'flatten',
+                'displayType': '`Option`<`Option`<`T`>> -> `Option`<`T`>',
+            },
         ],
     },
     {
         'query': 'option<t>',
         'returned': [
-            { 'path': 'std::result::Result', 'name': 'ok' },
+            {
+                'path': 'std::result::Result',
+                'name': 'ok',
+                'displayType': 'Result<T, E> -> `Option`<`T`>',
+            },
+        ],
+    },
+    {
+        'query': 'option<t>, (fnonce () -> u) -> option',
+        'others': [
+            {
+                'path': 'std::option::Option',
+                'name': 'map',
+                'displayType': '`Option`<`T`>, F -> `Option`<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`,
+                'displayWhereClause': "F: `FnOnce` (T) -> Option<`U`>",
+            },
+            {
+                'path': 'std::option::Option',
+                'name': 'zip_with',
+                'displayType': 'Option<T>, `Option`<`U`>, F -> `Option`<R>',
+                'displayMappedNames': `t = U, u = R`,
+                'displayWhereClause': "F: `FnOnce` (T, U) -> `R`",
+            },
+        ],
+    },
+    {
+        'query': 'option<t>, (fnonce () -> option<u>) -> option',
+        'others': [
+            {
+                'path': 'std::option::Option',
+                'name': 'and_then',
+                'displayType': '`Option`<`T`>, F -> `Option`<U>',
+                'displayMappedNames': `t = T, u = U`,
+                'displayWhereClause': "F: `FnOnce` (T) -> `Option`<`U`>",
+            },
         ],
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-bindings.js b/tests/rustdoc-js-std/parser-bindings.js
index c4909c6242d..bd379f139ff 100644
--- a/tests/rustdoc-js-std/parser-bindings.js
+++ b/tests/rustdoc-js-std/parser-bindings.js
@@ -3,20 +3,22 @@ const PARSED = [
         query: 'A<B=C>',
         elems: [
             {
-                name: "a",
+                name: "A",
                 fullPath: ["a"],
                 pathWithoutLast: [],
                 pathLast: "a",
+                normalizedPathLast: "a",
                 generics: [],
                 bindings: [
                     [
                         'b',
                         [
                             {
-                                name: "c",
+                                name: "C",
                                 fullPath: ["c"],
                                 pathWithoutLast: [],
                                 pathLast: "c",
+                                normalizedPathLast: "c",
                                 generics: [],
                                 typeFilter: -1,
                             },
@@ -27,16 +29,15 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'A<B=C>',
+        userQuery: 'A<B=C>',
         returned: [],
-        userQuery: 'a<b=c>',
         error: null,
     },
     {
         query: 'A<B = C>',
         elems: [
             {
-                name: "a",
+                name: "A",
                 fullPath: ["a"],
                 pathWithoutLast: [],
                 pathLast: "a",
@@ -45,7 +46,7 @@ const PARSED = [
                     [
                         'b',
                         [{
-                            name: "c",
+                            name: "C",
                             fullPath: ["c"],
                             pathWithoutLast: [],
                             pathLast: "c",
@@ -58,16 +59,15 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'A<B = C>',
+        userQuery: 'A<B = C>',
         returned: [],
-        userQuery: 'a<b = c>',
         error: null,
     },
     {
         query: 'A<B=!>',
         elems: [
             {
-                name: "a",
+                name: "A",
                 fullPath: ["a"],
                 pathWithoutLast: [],
                 pathLast: "a",
@@ -89,16 +89,15 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'A<B=!>',
+        userQuery: 'A<B=!>',
         returned: [],
-        userQuery: 'a<b=!>',
         error: null,
     },
     {
         query: 'A<B=[]>',
         elems: [
             {
-                name: "a",
+                name: "A",
                 fullPath: ["a"],
                 pathWithoutLast: [],
                 pathLast: "a",
@@ -120,16 +119,15 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'A<B=[]>',
+        userQuery: 'A<B=[]>',
         returned: [],
-        userQuery: 'a<b=[]>',
         error: null,
     },
     {
         query: 'A<B=[!]>',
         elems: [
             {
-                name: "a",
+                name: "A",
                 fullPath: ["a"],
                 pathWithoutLast: [],
                 pathLast: "a",
@@ -160,52 +158,47 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'A<B=[!]>',
+        userQuery: 'A<B=[!]>',
         returned: [],
-        userQuery: 'a<b=[!]>',
         error: null,
     },
     {
         query: 'A<B=C=>',
         elems: [],
         foundElems: 0,
-        original: 'A<B=C=>',
+        userQuery: 'A<B=C=>',
         returned: [],
-        userQuery: 'a<b=c=>',
         error: "Cannot write `=` twice in a binding",
     },
     {
         query: 'A<B=>',
         elems: [],
         foundElems: 0,
-        original: 'A<B=>',
+        userQuery: 'A<B=>',
         returned: [],
-        userQuery: 'a<b=>',
         error: "Unexpected `>` after `=`",
     },
     {
         query: 'B=C',
         elems: [],
         foundElems: 0,
-        original: 'B=C',
+        userQuery: 'B=C',
         returned: [],
-        userQuery: 'b=c',
         error: "Type parameter `=` must be within generics list",
     },
     {
         query: '[B=C]',
         elems: [],
         foundElems: 0,
-        original: '[B=C]',
+        userQuery: '[B=C]',
         returned: [],
-        userQuery: '[b=c]',
         error: "Type parameter `=` cannot be within slice `[]`",
     },
     {
         query: 'A<B<X>=C>',
         elems: [
             {
-                name: "a",
+                name: "A",
                 fullPath: ["a"],
                 pathWithoutLast: [],
                 pathLast: "a",
@@ -215,7 +208,7 @@ const PARSED = [
                         'b',
                         [
                             {
-                                name: "c",
+                                name: "C",
                                 fullPath: ["c"],
                                 pathWithoutLast: [],
                                 pathLast: "c",
@@ -223,7 +216,7 @@ const PARSED = [
                                 typeFilter: -1,
                             },
                             {
-                                name: "x",
+                                name: "X",
                                 fullPath: ["x"],
                                 pathWithoutLast: [],
                                 pathLast: "x",
@@ -237,9 +230,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'A<B<X>=C>',
+        userQuery: 'A<B<X>=C>',
         returned: [],
-        userQuery: 'a<b<x>=c>',
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js
index 5ce35bf511d..068298e7236 100644
--- a/tests/rustdoc-js-std/parser-errors.js
+++ b/tests/rustdoc-js-std/parser-errors.js
@@ -3,450 +3,400 @@ const PARSED = [
         query: '<P>',
         elems: [],
         foundElems: 0,
-        original: "<P>",
+        userQuery: "<P>",
         returned: [],
-        userQuery: "<p>",
         error: "Found generics without a path",
     },
     {
         query: '-> <P>',
         elems: [],
         foundElems: 0,
-        original: "-> <P>",
+        userQuery: "-> <P>",
         returned: [],
-        userQuery: "-> <p>",
         error: "Found generics without a path",
     },
     {
         query: '-> *',
         elems: [],
         foundElems: 0,
-        original: "-> *",
-        returned: [],
         userQuery: "-> *",
+        returned: [],
         error: "Unexpected `*` after ` ` (not a valid identifier)",
     },
     {
         query: 'a<"P">',
         elems: [],
         foundElems: 0,
-        original: "a<\"P\">",
+        userQuery: "a<\"P\">",
         returned: [],
-        userQuery: "a<\"p\">",
         error: "Unexpected `\"` in generics",
     },
     {
         query: '"P" "P"',
         elems: [],
         foundElems: 0,
-        original: "\"P\" \"P\"",
+        userQuery: "\"P\" \"P\"",
         returned: [],
-        userQuery: "\"p\" \"p\"",
         error: "Cannot have more than one element if you use quotes",
     },
     {
         query: '"P","P"',
         elems: [],
         foundElems: 0,
-        original: "\"P\",\"P\"",
+        userQuery: "\"P\",\"P\"",
         returned: [],
-        userQuery: "\"p\",\"p\"",
         error: "Cannot have more than one literal search element",
     },
     {
         query: "P,\"P\"",
         elems: [],
         foundElems: 0,
-        original: "P,\"P\"",
+        userQuery: "P,\"P\"",
         returned: [],
-        userQuery: "p,\"p\"",
         error: "Cannot use literal search when there is more than one element",
     },
     {
         query: '"p" p',
         elems: [],
         foundElems: 0,
-        original: "\"p\" p",
-        returned: [],
         userQuery: "\"p\" p",
+        returned: [],
         error: "Cannot have more than one element if you use quotes",
     },
     {
         query: '"p",p',
         elems: [],
         foundElems: 0,
-        original: "\"p\",p",
-        returned: [],
         userQuery: "\"p\",p",
+        returned: [],
         error: "Cannot have more than one element if you use quotes",
     },
     {
         query: '"const": p',
         elems: [],
         foundElems: 0,
-        original: "\"const\": p",
-        returned: [],
         userQuery: "\"const\": p",
+        returned: [],
         error: "Cannot use quotes on type filter",
     },
     {
         query: "a<:a>",
         elems: [],
         foundElems: 0,
-        original: "a<:a>",
-        returned: [],
         userQuery: "a<:a>",
+        returned: [],
         error: "Expected type filter before `:`",
     },
     {
         query: "a<::a>",
         elems: [],
         foundElems: 0,
-        original: "a<::a>",
-        returned: [],
         userQuery: "a<::a>",
+        returned: [],
         error: "Unexpected `::`: paths cannot start with `::`",
     },
     {
         query: "(p -> p",
         elems: [],
         foundElems: 0,
-        original: "(p -> p",
-        returned: [],
         userQuery: "(p -> p",
+        returned: [],
         error: "Unclosed `(`",
     },
     {
         query: "::a::b",
         elems: [],
         foundElems: 0,
-        original: "::a::b",
-        returned: [],
         userQuery: "::a::b",
+        returned: [],
         error: "Paths cannot start with `::`",
     },
     {
         query: " ::a::b",
         elems: [],
         foundElems: 0,
-        original: "::a::b",
-        returned: [],
         userQuery: "::a::b",
+        returned: [],
         error: "Paths cannot start with `::`",
     },
     {
         query: "a::::b",
         elems: [],
         foundElems: 0,
-        original: "a::::b",
-        returned: [],
         userQuery: "a::::b",
+        returned: [],
         error: "Unexpected `::::`",
     },
     {
         query: "a:: ::b",
         elems: [],
         foundElems: 0,
-        original: "a:: ::b",
-        returned: [],
         userQuery: "a:: ::b",
+        returned: [],
         error: "Unexpected `:: ::`",
     },
     {
         query: "a::\t::b",
         elems: [],
         foundElems: 0,
-        original: "a:: ::b",
-        returned: [],
         userQuery: "a:: ::b",
+        returned: [],
         error: "Unexpected `:: ::`",
     },
     {
         query: "a::b::",
         elems: [],
         foundElems: 0,
-        original: "a::b::",
-        returned: [],
         userQuery: "a::b::",
+        returned: [],
         error: "Paths cannot end with `::`",
     },
     {
         query: ":a",
         elems: [],
         foundElems: 0,
-        original: ":a",
-        returned: [],
         userQuery: ":a",
+        returned: [],
         error: "Expected type filter before `:`",
     },
     {
         query: "a,b:",
         elems: [],
         foundElems: 0,
-        original: "a,b:",
-        returned: [],
         userQuery: "a,b:",
+        returned: [],
         error: "Unexpected `:` (expected path after type filter `b:`)",
     },
     {
         query: "a (b:",
         elems: [],
         foundElems: 0,
-        original: "a (b:",
-        returned: [],
         userQuery: "a (b:",
+        returned: [],
         error: "Unclosed `(`",
     },
     {
         query: "_:",
         elems: [],
         foundElems: 0,
-        original: "_:",
-        returned: [],
         userQuery: "_:",
+        returned: [],
         error: "Unexpected `_` (not a valid identifier)",
     },
     {
         query: "ab:",
         elems: [],
         foundElems: 0,
-        original: "ab:",
-        returned: [],
         userQuery: "ab:",
+        returned: [],
         error: "Unexpected `:` (expected path after type filter `ab:`)",
     },
     {
         query: "a:b",
         elems: [],
         foundElems: 0,
-        original: "a:b",
-        returned: [],
         userQuery: "a:b",
+        returned: [],
         error: "Unknown type filter `a`",
     },
     {
         query: "a-bb",
         elems: [],
         foundElems: 0,
-        original: "a-bb",
-        returned: [],
         userQuery: "a-bb",
+        returned: [],
         error: "Unexpected `-` (did you mean `->`?)",
     },
     {
         query: "a>bb",
         elems: [],
         foundElems: 0,
-        original: "a>bb",
-        returned: [],
         userQuery: "a>bb",
+        returned: [],
         error: "Unexpected `>` (did you mean `->`?)",
     },
     {
         query: "ab'",
         elems: [],
         foundElems: 0,
-        original: "ab'",
-        returned: [],
         userQuery: "ab'",
+        returned: [],
         error: "Unexpected `'` after `b` (not a valid identifier)",
     },
     {
         query: '"p" <a>',
         elems: [],
         foundElems: 0,
-        original: '"p" <a>',
-        returned: [],
         userQuery: '"p" <a>',
+        returned: [],
         error: "Cannot have more than one element if you use quotes",
     },
     {
         query: '"p",<a>',
         elems: [],
         foundElems: 0,
-        original: '"p",<a>',
-        returned: [],
         userQuery: '"p",<a>',
+        returned: [],
         error: "Found generics without a path",
     },
     {
         query: '"p" a<a>',
         elems: [],
         foundElems: 0,
-        original: '"p" a<a>',
-        returned: [],
         userQuery: '"p" a<a>',
+        returned: [],
         error: "Cannot have more than one element if you use quotes",
     },
     {
         query: '"p",a<a>',
         elems: [],
         foundElems: 0,
-        original: '"p",a<a>',
-        returned: [],
         userQuery: '"p",a<a>',
+        returned: [],
         error: "Cannot have more than one element if you use quotes",
     },
     {
         query: "a,<",
         elems: [],
         foundElems: 0,
-        original: 'a,<',
-        returned: [],
         userQuery: 'a,<',
+        returned: [],
         error: 'Found generics without a path',
     },
     {
         query: "aaaaa<>b",
         elems: [],
         foundElems: 0,
-        original: 'aaaaa<>b',
-        returned: [],
         userQuery: 'aaaaa<>b',
+        returned: [],
         error: 'Expected `,`, `:` or `->` after `>`, found `b`',
     },
     {
         query: "fn:aaaaa<>b",
         elems: [],
         foundElems: 0,
-        original: 'fn:aaaaa<>b',
-        returned: [],
         userQuery: 'fn:aaaaa<>b',
+        returned: [],
         error: 'Expected `,`, `:` or `->` after `>`, found `b`',
     },
     {
         query: "->a<>b",
         elems: [],
         foundElems: 0,
-        original: '->a<>b',
-        returned: [],
         userQuery: '->a<>b',
+        returned: [],
         error: 'Expected `,` or `=` after `>`, found `b`',
     },
     {
         query: "a<->",
         elems: [],
         foundElems: 0,
-        original: 'a<->',
-        returned: [],
         userQuery: 'a<->',
+        returned: [],
         error: 'Unclosed `<`',
     },
     {
         query: "a<a>:",
         elems: [],
         foundElems: 0,
-        original: "a<a>:",
-        returned: [],
         userQuery: "a<a>:",
+        returned: [],
         error: 'Unexpected `<` in type filter (before `:`)',
     },
     {
         query: "a<>:",
         elems: [],
         foundElems: 0,
-        original: "a<>:",
-        returned: [],
         userQuery: "a<>:",
+        returned: [],
         error: 'Unexpected `<` in type filter (before `:`)',
     },
     {
         query: "a,:",
         elems: [],
         foundElems: 0,
-        original: "a,:",
-        returned: [],
         userQuery: "a,:",
+        returned: [],
         error: 'Expected type filter before `:`',
     },
     {
         query: "a!:",
         elems: [],
         foundElems: 0,
-        original: "a!:",
-        returned: [],
         userQuery: "a!:",
+        returned: [],
         error: 'Unexpected `!` in type filter (before `:`)',
     },
     {
         query: "  a<>  :",
         elems: [],
         foundElems: 0,
-        original: "a<>  :",
-        returned: [],
         userQuery: "a<>  :",
+        returned: [],
         error: 'Expected `,`, `:` or `->` after `>`, found `:`',
     },
     {
         query: "mod : :",
         elems: [],
         foundElems: 0,
-        original: "mod : :",
-        returned: [],
         userQuery: "mod : :",
+        returned: [],
         error: 'Unexpected `:` (expected path after type filter `mod:`)',
     },
     {
         query: "mod: :",
         elems: [],
         foundElems: 0,
-        original: "mod: :",
-        returned: [],
         userQuery: "mod: :",
+        returned: [],
         error: 'Unexpected `:` (expected path after type filter `mod:`)',
     },
     {
         query: "a!a",
         elems: [],
         foundElems: 0,
-        original: "a!a",
-        returned: [],
         userQuery: "a!a",
+        returned: [],
         error: 'Unexpected `!`: it can only be at the end of an ident',
     },
     {
         query: "a!!",
         elems: [],
         foundElems: 0,
-        original: "a!!",
-        returned: [],
         userQuery: "a!!",
+        returned: [],
         error: 'Cannot have more than one `!` in an ident',
     },
     {
         query: "mod:a!",
         elems: [],
         foundElems: 0,
-        original: "mod:a!",
-        returned: [],
         userQuery: "mod:a!",
+        returned: [],
         error: 'Invalid search type: macro `!` and `mod` both specified',
     },
     {
         query: "mod:!",
         elems: [],
         foundElems: 0,
-        original: "mod:!",
-        returned: [],
         userQuery: "mod:!",
+        returned: [],
         error: 'Invalid search type: primitive never type `!` and `mod` both specified',
     },
     {
         query: "a!::a",
         elems: [],
         foundElems: 0,
-        original: "a!::a",
-        returned: [],
         userQuery: "a!::a",
+        returned: [],
         error: 'Cannot have associated items in macros',
     },
     {
         query: "a<",
         elems: [],
         foundElems: 0,
-        original: "a<",
-        returned: [],
         userQuery: "a<",
+        returned: [],
         error: "Unclosed `<`",
     },
     {
@@ -479,9 +429,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "p<x> , y",
-        returned: [],
         userQuery: "p<x> , y",
+        returned: [],
         error: null,
     },
     {
@@ -514,9 +463,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "p<x , y>",
-        returned: [],
         userQuery: "p<x , y>",
+        returned: [],
         error: null,
     },
     {
@@ -548,9 +496,8 @@ const PARSED = [
             },
         ],
         foundElems: 3,
-        original: "p ,x , y",
-        returned: [],
         userQuery: "p ,x , y",
+        returned: [],
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-filter.js b/tests/rustdoc-js-std/parser-filter.js
index a1dd0ea3b5a..cda950461f7 100644
--- a/tests/rustdoc-js-std/parser-filter.js
+++ b/tests/rustdoc-js-std/parser-filter.js
@@ -10,9 +10,8 @@ const PARSED = [
             typeFilter: 7,
         }],
         foundElems: 1,
-        original: "fn:foo",
-        returned: [],
         userQuery: "fn:foo",
+        returned: [],
         error: null,
     },
     {
@@ -26,18 +25,16 @@ const PARSED = [
             typeFilter: 6,
         }],
         foundElems: 1,
-        original: "enum : foo",
-        returned: [],
         userQuery: "enum : foo",
+        returned: [],
         error: null,
     },
     {
         query: 'macro<f>:foo',
         elems: [],
         foundElems: 0,
-        original: "macro<f>:foo",
-        returned: [],
         userQuery: "macro<f>:foo",
+        returned: [],
         error: "Unexpected `<` in type filter (before `:`)",
     },
     {
@@ -51,9 +48,8 @@ const PARSED = [
             typeFilter: 16,
         }],
         foundElems: 1,
-        original: "macro!",
-        returned: [],
         userQuery: "macro!",
+        returned: [],
         error: null,
     },
     {
@@ -67,9 +63,8 @@ const PARSED = [
             typeFilter: 16,
         }],
         foundElems: 1,
-        original: "macro:mac!",
-        returned: [],
         userQuery: "macro:mac!",
+        returned: [],
         error: null,
     },
     {
@@ -83,16 +78,15 @@ const PARSED = [
             typeFilter: 16,
         }],
         foundElems: 1,
-        original: "a::mac!",
-        returned: [],
         userQuery: "a::mac!",
+        returned: [],
         error: null,
     },
     {
         query: '-> fn:foo',
         elems: [],
         foundElems: 1,
-        original: "-> fn:foo",
+        userQuery: "-> fn:foo",
         returned: [{
             name: "foo",
             fullPath: ["foo"],
@@ -101,14 +95,13 @@ const PARSED = [
             generics: [],
             typeFilter: 7,
         }],
-        userQuery: "-> fn:foo",
         error: null,
     },
     {
         query: '-> fn:foo<fn:bar>',
         elems: [],
         foundElems: 1,
-        original: "-> fn:foo<fn:bar>",
+        userQuery: "-> fn:foo<fn:bar>",
         returned: [{
             name: "foo",
             fullPath: ["foo"],
@@ -126,14 +119,13 @@ const PARSED = [
             ],
             typeFilter: 7,
         }],
-        userQuery: "-> fn:foo<fn:bar>",
         error: null,
     },
     {
         query: '-> fn:foo<fn:bar, enum : baz::fuzz>',
         elems: [],
         foundElems: 1,
-        original: "-> fn:foo<fn:bar, enum : baz::fuzz>",
+        userQuery: "-> fn:foo<fn:bar, enum : baz::fuzz>",
         returned: [{
             name: "foo",
             fullPath: ["foo"],
@@ -159,7 +151,6 @@ const PARSED = [
             ],
             typeFilter: 7,
         }],
-        userQuery: "-> fn:foo<fn:bar, enum : baz::fuzz>",
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-generics.js b/tests/rustdoc-js-std/parser-generics.js
index 726ee56c2c1..8b8d95bcb88 100644
--- a/tests/rustdoc-js-std/parser-generics.js
+++ b/tests/rustdoc-js-std/parser-generics.js
@@ -3,9 +3,8 @@ const PARSED = [
         query: 'A<B<C<D>,  E>',
         elems: [],
         foundElems: 0,
-        original: 'A<B<C<D>,  E>',
+        userQuery: 'A<B<C<D>,  E>',
         returned: [],
-        userQuery: 'a<b<c<d>,  e>',
         error: 'Unclosed `<`',
     },
     {
@@ -29,9 +28,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "p<>,u8",
-        returned: [],
         userQuery: "p<>,u8",
+        returned: [],
         error: null,
     },
     {
@@ -55,9 +53,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: '"p"<a>',
-        returned: [],
         userQuery: '"p"<a>',
+        returned: [],
         error: null,
     },
     {
@@ -89,9 +86,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'p<u<x>>',
-        returned: [],
         userQuery: 'p<u<x>>',
+        returned: [],
         error: null,
     },
     {
@@ -130,9 +126,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'p<u<x>, r>',
-        returned: [],
         userQuery: 'p<u<x>, r>',
+        returned: [],
         error: null,
     },
     {
@@ -171,9 +166,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: 'p<u<x, r>>',
-        returned: [],
         userQuery: 'p<u<x, r>>',
+        returned: [],
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-hof.js b/tests/rustdoc-js-std/parser-hof.js
index 0b99c45b7a9..ca761015412 100644
--- a/tests/rustdoc-js-std/parser-hof.js
+++ b/tests/rustdoc-js-std/parser-hof.js
@@ -12,13 +12,13 @@ const PARSED = [
                 [
                     "output",
                     [{
-                        name: "f",
+                        name: "F",
                         fullPath: ["f"],
                         pathWithoutLast: [],
                         pathLast: "f",
                         generics: [
                             {
-                                name: "p",
+                                name: "P",
                                 fullPath: ["p"],
                                 pathWithoutLast: [],
                                 pathLast: "p",
@@ -32,9 +32,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(-> F<P>)",
+        userQuery: "(-> F<P>)",
         returned: [],
-        userQuery: "(-> f<p>)",
         error: null,
     },
     {
@@ -49,7 +48,7 @@ const PARSED = [
                 [
                     "output",
                     [{
-                        name: "p",
+                        name: "P",
                         fullPath: ["p"],
                         pathWithoutLast: [],
                         pathLast: "p",
@@ -61,9 +60,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(-> P)",
+        userQuery: "(-> P)",
         returned: [],
-        userQuery: "(-> p)",
         error: null,
     },
     {
@@ -90,9 +88,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(->,a)",
-        returned: [],
         userQuery: "(->,a)",
+        returned: [],
         error: null,
     },
     {
@@ -103,13 +100,13 @@ const PARSED = [
             pathWithoutLast: [],
             pathLast: "->",
             generics: [{
-                name: "f",
+                name: "F",
                 fullPath: ["f"],
                 pathWithoutLast: [],
                 pathLast: "f",
                 generics: [
                     {
-                        name: "p",
+                        name: "P",
                         fullPath: ["p"],
                         pathWithoutLast: [],
                         pathLast: "p",
@@ -127,9 +124,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(F<P> ->)",
+        userQuery: "(F<P> ->)",
         returned: [],
-        userQuery: "(f<p> ->)",
         error: null,
     },
     {
@@ -140,7 +136,7 @@ const PARSED = [
             pathWithoutLast: [],
             pathLast: "->",
             generics: [{
-                name: "p",
+                name: "P",
                 fullPath: ["p"],
                 pathWithoutLast: [],
                 pathLast: "p",
@@ -156,9 +152,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(P ->)",
+        userQuery: "(P ->)",
         returned: [],
-        userQuery: "(p ->)",
         error: null,
     },
     {
@@ -185,9 +180,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(,a->)",
-        returned: [],
         userQuery: "(,a->)",
+        returned: [],
         error: null,
     },
     {
@@ -221,9 +215,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(aaaaa->a)",
-        returned: [],
         userQuery: "(aaaaa->a)",
+        returned: [],
         error: null,
     },
     {
@@ -267,9 +260,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(aaaaa, b -> a)",
-        returned: [],
         userQuery: "(aaaaa, b -> a)",
+        returned: [],
         error: null,
     },
     {
@@ -313,9 +305,8 @@ const PARSED = [
             typeFilter: 1,
         }],
         foundElems: 1,
-        original: "primitive:(aaaaa, b -> a)",
-        returned: [],
         userQuery: "primitive:(aaaaa, b -> a)",
+        returned: [],
         error: null,
     },
     {
@@ -369,16 +360,15 @@ const PARSED = [
             }
         ],
         foundElems: 2,
-        original: "x, trait:(aaaaa, b -> a)",
-        returned: [],
         userQuery: "x, trait:(aaaaa, b -> a)",
+        returned: [],
         error: null,
     },
     // Rust-style HOF
     {
         query: "Fn () -> F<P>",
         elems: [{
-            name: "fn",
+            name: "Fn",
             fullPath: ["fn"],
             pathWithoutLast: [],
             pathLast: "fn",
@@ -387,13 +377,13 @@ const PARSED = [
                 [
                     "output",
                     [{
-                        name: "f",
+                        name: "F",
                         fullPath: ["f"],
                         pathWithoutLast: [],
                         pathLast: "f",
                         generics: [
                             {
-                                name: "p",
+                                name: "P",
                                 fullPath: ["p"],
                                 pathWithoutLast: [],
                                 pathLast: "p",
@@ -407,15 +397,14 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "Fn () -> F<P>",
+        userQuery: "Fn () -> F<P>",
         returned: [],
-        userQuery: "fn () -> f<p>",
         error: null,
     },
     {
         query: "FnMut() -> P",
         elems: [{
-            name: "fnmut",
+            name: "FnMut",
             fullPath: ["fnmut"],
             pathWithoutLast: [],
             pathLast: "fnmut",
@@ -424,7 +413,7 @@ const PARSED = [
                 [
                     "output",
                     [{
-                        name: "p",
+                        name: "P",
                         fullPath: ["p"],
                         pathWithoutLast: [],
                         pathLast: "p",
@@ -436,15 +425,14 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "FnMut() -> P",
+        userQuery: "FnMut() -> P",
         returned: [],
-        userQuery: "fnmut() -> p",
         error: null,
     },
     {
         query: "(FnMut() -> P)",
         elems: [{
-            name: "fnmut",
+            name: "FnMut",
             fullPath: ["fnmut"],
             pathWithoutLast: [],
             pathLast: "fnmut",
@@ -453,7 +441,7 @@ const PARSED = [
                 [
                     "output",
                     [{
-                        name: "p",
+                        name: "P",
                         fullPath: ["p"],
                         pathWithoutLast: [],
                         pathLast: "p",
@@ -465,26 +453,25 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "(FnMut() -> P)",
+        userQuery: "(FnMut() -> P)",
         returned: [],
-        userQuery: "(fnmut() -> p)",
         error: null,
     },
     {
         query: "Fn(F<P>)",
         elems: [{
-            name: "fn",
+            name: "Fn",
             fullPath: ["fn"],
             pathWithoutLast: [],
             pathLast: "fn",
             generics: [{
-                name: "f",
+                name: "F",
                 fullPath: ["f"],
                 pathWithoutLast: [],
                 pathLast: "f",
                 generics: [
                     {
-                        name: "p",
+                        name: "P",
                         fullPath: ["p"],
                         pathWithoutLast: [],
                         pathLast: "p",
@@ -502,9 +489,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "Fn(F<P>)",
+        userQuery: "Fn(F<P>)",
         returned: [],
-        userQuery: "fn(f<p>)",
         error: null,
     },
     {
@@ -548,9 +534,8 @@ const PARSED = [
             typeFilter: 1,
         }],
         foundElems: 1,
-        original: "primitive:fnonce(aaaaa, b) -> a",
-        returned: [],
         userQuery: "primitive:fnonce(aaaaa, b) -> a",
+        returned: [],
         error: null,
     },
     {
@@ -594,9 +579,8 @@ const PARSED = [
             typeFilter: 1,
         }],
         foundElems: 1,
-        original: "primitive:fnonce(aaaaa, keyword:b) -> trait:a",
-        returned: [],
         userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a",
+        returned: [],
         error: null,
     },
     {
@@ -665,9 +649,8 @@ const PARSED = [
             }
         ],
         foundElems: 2,
-        original: "x, trait:fn(aaaaa, b -> a)",
-        returned: [],
         userQuery: "x, trait:fn(aaaaa, b -> a)",
+        returned: [],
         error: null,
     },
     {
@@ -704,9 +687,8 @@ const PARSED = [
             }
         ],
         foundElems: 2,
-        original: "a,b(c)",
-        returned: [],
         userQuery: "a,b(c)",
+        returned: [],
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-ident.js b/tests/rustdoc-js-std/parser-ident.js
index cc79c58f1da..f65391b1571 100644
--- a/tests/rustdoc-js-std/parser-ident.js
+++ b/tests/rustdoc-js-std/parser-ident.js
@@ -2,7 +2,7 @@ const PARSED = [
     {
         query: "R<!>",
         elems: [{
-            name: "r",
+            name: "R",
             fullPath: ["r"],
             pathWithoutLast: [],
             pathLast: "r",
@@ -19,9 +19,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "R<!>",
+        userQuery: "R<!>",
         returned: [],
-        userQuery: "r<!>",
         error: null,
     },
     {
@@ -35,9 +34,8 @@ const PARSED = [
             typeFilter: 1,
         }],
         foundElems: 1,
-        original: "!",
-        returned: [],
         userQuery: "!",
+        returned: [],
         error: null,
     },
     {
@@ -51,27 +49,24 @@ const PARSED = [
             typeFilter: 16,
         }],
         foundElems: 1,
-        original: "a!",
-        returned: [],
         userQuery: "a!",
+        returned: [],
         error: null,
     },
     {
         query: "a!::b",
         elems: [],
         foundElems: 0,
-        original: "a!::b",
-        returned: [],
         userQuery: "a!::b",
+        returned: [],
         error: "Cannot have associated items in macros",
     },
     {
         query: "!<T>",
         elems: [],
         foundElems: 0,
-        original: "!<T>",
+        userQuery: "!<T>",
         returned: [],
-        userQuery: "!<t>",
         error: "Never type `!` does not accept generic parameters",
     },
     {
@@ -85,36 +80,32 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "!::b",
-        returned: [],
         userQuery: "!::b",
+        returned: [],
         error: null,
     },
     {
         query: "b::!",
         elems: [],
         foundElems: 0,
-        original: "b::!",
-        returned: [],
         userQuery: "b::!",
+        returned: [],
         error: "Never type `!` is not associated item",
     },
     {
         query: "!::!",
         elems: [],
         foundElems: 0,
-        original: "!::!",
-        returned: [],
         userQuery: "!::!",
+        returned: [],
         error: "Never type `!` is not associated item",
     },
     {
         query: "b::!::c",
         elems: [],
         foundElems: 0,
-        original: "b::!::c",
-        returned: [],
         userQuery: "b::!::c",
+        returned: [],
         error: "Never type `!` is not associated item",
     },
     {
@@ -126,7 +117,7 @@ const PARSED = [
             pathLast: "b",
             generics: [
                 {
-                    name: "t",
+                    name: "T",
                     fullPath: ["t"],
                     pathWithoutLast: [],
                     pathLast: "t",
@@ -137,18 +128,16 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "!::b<T>",
+        userQuery: "!::b<T>",
         returned: [],
-        userQuery: "!::b<t>",
         error: null,
     },
     {
         query: "a!::b!",
         elems: [],
         foundElems: 0,
-        original: "a!::b!",
-        returned: [],
         userQuery: "a!::b!",
+        returned: [],
         error: "Cannot have associated items in macros",
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-literal.js b/tests/rustdoc-js-std/parser-literal.js
index 87c06224dbf..63e07a246a1 100644
--- a/tests/rustdoc-js-std/parser-literal.js
+++ b/tests/rustdoc-js-std/parser-literal.js
@@ -2,13 +2,13 @@ const PARSED = [
     {
         query: 'R<P>',
         elems: [{
-            name: "r",
+            name: "R",
             fullPath: ["r"],
             pathWithoutLast: [],
             pathLast: "r",
             generics: [
                 {
-                    name: "p",
+                    name: "P",
                     fullPath: ["p"],
                     pathWithoutLast: [],
                     pathLast: "p",
@@ -18,9 +18,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "R<P>",
+        userQuery: "R<P>",
         returned: [],
-        userQuery: "r<p>",
         error: null,
     }
 ];
diff --git a/tests/rustdoc-js-std/parser-paths.js b/tests/rustdoc-js-std/parser-paths.js
index 774e5d028cc..bb34e22e518 100644
--- a/tests/rustdoc-js-std/parser-paths.js
+++ b/tests/rustdoc-js-std/parser-paths.js
@@ -2,7 +2,7 @@ const PARSED = [
     {
         query: 'A::B',
         elems: [{
-            name: "a::b",
+            name: "A::B",
             fullPath: ["a", "b"],
             pathWithoutLast: ["a"],
             pathLast: "b",
@@ -10,9 +10,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "A::B",
+        userQuery: "A::B",
         returned: [],
-        userQuery: "a::b",
         error: null,
     },
     {
@@ -26,9 +25,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: 'a:: a',
-        returned: [],
         userQuery: 'a:: a',
+        returned: [],
         error: null,
     },
     {
@@ -42,9 +40,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: 'a ::a',
-        returned: [],
         userQuery: 'a ::a',
+        returned: [],
         error: null,
     },
     {
@@ -58,16 +55,15 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: 'a  ::  a',
-        returned: [],
         userQuery: 'a  ::  a',
+        returned: [],
         error: null,
     },
     {
         query: 'A::B,C',
         elems: [
             {
-                name: "a::b",
+                name: "A::B",
                 fullPath: ["a", "b"],
                 pathWithoutLast: ["a"],
                 pathLast: "b",
@@ -75,7 +71,7 @@ const PARSED = [
                 typeFilter: -1,
             },
             {
-                name: "c",
+                name: "C",
                 fullPath: ["c"],
                 pathWithoutLast: [],
                 pathLast: "c",
@@ -84,16 +80,15 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: 'A::B,C',
+        userQuery: 'A::B,C',
         returned: [],
-        userQuery: 'a::b,c',
         error: null,
     },
     {
         query: 'A::B<f>,C',
         elems: [
             {
-                name: "a::b",
+                name: "A::B",
                 fullPath: ["a", "b"],
                 pathWithoutLast: ["a"],
                 pathLast: "b",
@@ -109,7 +104,7 @@ const PARSED = [
                 typeFilter: -1,
             },
             {
-                name: "c",
+                name: "C",
                 fullPath: ["c"],
                 pathWithoutLast: [],
                 pathLast: "c",
@@ -118,9 +113,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: 'A::B<f>,C',
+        userQuery: 'A::B<f>,C',
         returned: [],
-        userQuery: 'a::b<f>,c',
         error: null,
     },
     {
@@ -134,9 +128,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "mod::a",
-        returned: [],
         userQuery: "mod::a",
+        returned: [],
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-quote.js b/tests/rustdoc-js-std/parser-quote.js
index 731673cf463..b485047e385 100644
--- a/tests/rustdoc-js-std/parser-quote.js
+++ b/tests/rustdoc-js-std/parser-quote.js
@@ -3,7 +3,7 @@ const PARSED = [
         query: '-> "p"',
         elems: [],
         foundElems: 1,
-        original: '-> "p"',
+        userQuery: '-> "p"',
         returned: [{
             name: "p",
             fullPath: ["p"],
@@ -12,7 +12,6 @@ const PARSED = [
             generics: [],
             typeFilter: -1,
         }],
-        userQuery: '-> "p"',
         error: null,
     },
     {
@@ -26,54 +25,48 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: '"p",',
-        returned: [],
         userQuery: '"p",',
+        returned: [],
         error: null,
     },
     {
         query: '"p" -> a',
         elems: [],
         foundElems: 0,
-        original: '"p" -> a',
-        returned: [],
         userQuery: '"p" -> a',
+        returned: [],
         error: "Cannot have more than one element if you use quotes",
     },
     {
         query: '"a" -> "p"',
         elems: [],
         foundElems: 0,
-        original: '"a" -> "p"',
-        returned: [],
         userQuery: '"a" -> "p"',
+        returned: [],
         error: "Cannot have more than one literal search element",
     },
     {
         query: '->"-"',
         elems: [],
         foundElems: 0,
-        original: '->"-"',
-        returned: [],
         userQuery: '->"-"',
+        returned: [],
         error: 'Unexpected `-` in a string element',
     },
     {
         query: '"a',
         elems: [],
         foundElems: 0,
-        original: '"a',
-        returned: [],
         userQuery: '"a',
+        returned: [],
         error: 'Unclosed `"`',
     },
     {
         query: '""',
         elems: [],
         foundElems: 0,
-        original: '""',
-        returned: [],
         userQuery: '""',
+        returned: [],
         error: 'Cannot have empty string element',
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-reference.js b/tests/rustdoc-js-std/parser-reference.js
index 6b1250146be..0fa07ae9895 100644
--- a/tests/rustdoc-js-std/parser-reference.js
+++ b/tests/rustdoc-js-std/parser-reference.js
@@ -3,18 +3,16 @@ const PARSED = [
         query: '&[',
         elems: [],
         foundElems: 0,
-        original: '&[',
-        returned: [],
         userQuery: '&[',
+        returned: [],
         error: 'Unclosed `[`',
     },
     {
         query: '[&',
         elems: [],
         foundElems: 0,
-        original: '[&',
-        returned: [],
         userQuery: '[&',
+        returned: [],
         error: 'Unclosed `[`',
     },
     {
@@ -39,7 +37,7 @@ const PARSED = [
                                 pathLast: "reference",
                                 generics: [
                                     {
-                                        name: "d",
+                                        name: "D",
                                         fullPath: ["d"],
                                         pathWithoutLast: [],
                                         pathLast: "d",
@@ -65,9 +63,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: '&&&D, []',
+        userQuery: '&&&D, []',
         returned: [],
-        userQuery: '&&&d, []',
         error: null,
     },
     {
@@ -98,7 +95,7 @@ const PARSED = [
                                         pathLast: "[]",
                                         generics: [
                                             {
-                                                name: "d",
+                                                name: "D",
                                                 fullPath: ["d"],
                                                 pathWithoutLast: [],
                                                 pathLast: "d",
@@ -119,9 +116,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: '&&&[D]',
+        userQuery: '&&&[D]',
         returned: [],
-        userQuery: '&&&[d]',
         error: null,
     },
     {
@@ -137,9 +133,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: '&',
-        returned: [],
         userQuery: '&',
+        returned: [],
         error: null,
     },
     {
@@ -164,9 +159,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: '&mut',
-        returned: [],
         userQuery: '&mut',
+        returned: [],
         error: null,
     },
     {
@@ -190,9 +184,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "&,u8",
-        returned: [],
         userQuery: "&,u8",
+        returned: [],
         error: null,
     },
     {
@@ -225,9 +218,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "&mut,u8",
-        returned: [],
         userQuery: "&mut,u8",
+        returned: [],
         error: null,
     },
     {
@@ -252,9 +244,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "&u8",
-        returned: [],
         userQuery: "&u8",
+        returned: [],
         error: null,
     },
     {
@@ -288,9 +279,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "&u8<u8>",
-        returned: [],
         userQuery: "&u8<u8>",
+        returned: [],
         error: null,
     },
     {
@@ -324,9 +314,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "u8<&u8>",
-        returned: [],
         userQuery: "u8<&u8>",
+        returned: [],
         error: null,
     },
     {
@@ -368,9 +357,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "u8<&u8, u8>",
-        returned: [],
         userQuery: "u8<&u8, u8>",
+        returned: [],
         error: null,
     },
     {
@@ -404,9 +392,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "u8<&u8>",
-        returned: [],
         userQuery: "u8<&u8>",
+        returned: [],
         error: null,
     },
     {
@@ -456,9 +443,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "u8<&mut u8, u8>",
-        returned: [],
         userQuery: "u8<&mut u8, u8>",
+        returned: [],
         error: null,
     },
     {
@@ -483,18 +469,16 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "primitive:&u8",
-        returned: [],
         userQuery: "primitive:&u8",
+        returned: [],
         error: null,
     },
     {
         query: 'macro:&u8',
         elems: [],
         foundElems: 0,
-        original: "macro:&u8",
-        returned: [],
         userQuery: "macro:&u8",
+        returned: [],
         error: "Invalid search type: primitive `&` and `macro` both specified",
     },
     {
@@ -519,9 +503,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "&macro:u8",
-        returned: [],
         userQuery: "&macro:u8",
+        returned: [],
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-returned.js b/tests/rustdoc-js-std/parser-returned.js
index 8f68209bb96..30ce26a8920 100644
--- a/tests/rustdoc-js-std/parser-returned.js
+++ b/tests/rustdoc-js-std/parser-returned.js
@@ -3,15 +3,15 @@ const PARSED = [
         query: "-> F<P>",
         elems: [],
         foundElems: 1,
-        original: "-> F<P>",
+        userQuery: "-> F<P>",
         returned: [{
-            name: "f",
+            name: "F",
             fullPath: ["f"],
             pathWithoutLast: [],
             pathLast: "f",
             generics: [
                 {
-                    name: "p",
+                    name: "P",
                     fullPath: ["p"],
                     pathWithoutLast: [],
                     pathLast: "p",
@@ -20,30 +20,28 @@ const PARSED = [
             ],
             typeFilter: -1,
         }],
-        userQuery: "-> f<p>",
         error: null,
     },
     {
         query: "-> P",
         elems: [],
         foundElems: 1,
-        original: "-> P",
+        userQuery: "-> P",
         returned: [{
-            name: "p",
+            name: "P",
             fullPath: ["p"],
             pathWithoutLast: [],
             pathLast: "p",
             generics: [],
             typeFilter: -1,
         }],
-        userQuery: "-> p",
         error: null,
     },
     {
         query: "->,a",
         elems: [],
         foundElems: 1,
-        original: "->,a",
+        userQuery: "->,a",
         returned: [{
             name: "a",
             fullPath: ["a"],
@@ -52,7 +50,6 @@ const PARSED = [
             generics: [],
             typeFilter: -1,
         }],
-        userQuery: "->,a",
         error: null,
     },
     {
@@ -66,7 +63,7 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 2,
-        original: "aaaaa->a",
+        userQuery: "aaaaa->a",
         returned: [{
             name: "a",
             fullPath: ["a"],
@@ -75,14 +72,13 @@ const PARSED = [
             generics: [],
             typeFilter: -1,
         }],
-        userQuery: "aaaaa->a",
         error: null,
     },
     {
         query: "-> !",
         elems: [],
         foundElems: 1,
-        original: "-> !",
+        userQuery: "-> !",
         returned: [{
             name: "never",
             fullPath: ["never"],
@@ -91,7 +87,6 @@ const PARSED = [
             generics: [],
             typeFilter: 1,
         }],
-        userQuery: "-> !",
         error: null,
     },
     {
@@ -105,9 +100,8 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "a->",
-        returned: [],
         userQuery: "a->",
+        returned: [],
         hasReturnArrow: true,
         error: null,
     },
@@ -122,9 +116,8 @@ const PARSED = [
             typeFilter: 1,
         }],
         foundElems: 1,
-        original: "!->",
-        returned: [],
         userQuery: "!->",
+        returned: [],
         hasReturnArrow: true,
         error: null,
     },
@@ -139,9 +132,8 @@ const PARSED = [
             typeFilter: 1,
         }],
         foundElems: 1,
-        original: "! ->",
-        returned: [],
         userQuery: "! ->",
+        returned: [],
         hasReturnArrow: true,
         error: null,
     },
@@ -156,9 +148,8 @@ const PARSED = [
             typeFilter: 1,
         }],
         foundElems: 1,
-        original: "primitive:!->",
-        returned: [],
         userQuery: "primitive:!->",
+        returned: [],
         hasReturnArrow: true,
         error: null,
     },
diff --git a/tests/rustdoc-js-std/parser-separators.js b/tests/rustdoc-js-std/parser-separators.js
index 7f95f61b006..cf271c80cdc 100644
--- a/tests/rustdoc-js-std/parser-separators.js
+++ b/tests/rustdoc-js-std/parser-separators.js
@@ -14,9 +14,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "aaaaaa b",
-        returned: [],
         userQuery: "aaaaaa b",
+        returned: [],
         error: null,
     },
     {
@@ -40,9 +39,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "aaaaaa, b",
-        returned: [],
         userQuery: "aaaaaa, b",
+        returned: [],
         error: null,
     },
     {
@@ -58,9 +56,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "a b",
-        returned: [],
         userQuery: "a b",
+        returned: [],
         error: null,
     },
     {
@@ -84,9 +81,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "a,b",
-        returned: [],
         userQuery: "a,b",
+        returned: [],
         error: null,
     },
     {
@@ -102,9 +98,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "a b",
-        returned: [],
         userQuery: "a b",
+        returned: [],
         error: null,
     },
     {
@@ -128,9 +123,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "a<b c>",
-        returned: [],
         userQuery: "a<b c>",
+        returned: [],
         error: null,
     },
     {
@@ -161,9 +155,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "a<b,c>",
-        returned: [],
         userQuery: "a<b,c>",
+        returned: [],
         error: null,
     },
     {
@@ -187,9 +180,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "a<b c>",
-        returned: [],
         userQuery: "a<b c>",
+        returned: [],
         error: null,
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-slice-array.js b/tests/rustdoc-js-std/parser-slice-array.js
index 1de52af94e6..65797945535 100644
--- a/tests/rustdoc-js-std/parser-slice-array.js
+++ b/tests/rustdoc-js-std/parser-slice-array.js
@@ -3,9 +3,8 @@ const PARSED = [
         query: '[[[D, []]]',
         elems: [],
         foundElems: 0,
-        original: '[[[D, []]]',
+        userQuery: '[[[D, []]]',
         returned: [],
-        userQuery: '[[[d, []]]',
         error: 'Unclosed `[`',
     },
     {
@@ -30,7 +29,7 @@ const PARSED = [
                                 pathLast: "[]",
                                 generics: [
                                     {
-                                        name: "d",
+                                        name: "D",
                                         fullPath: ["d"],
                                         pathWithoutLast: [],
                                         pathLast: "d",
@@ -56,9 +55,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: '[[[D, []]]]',
+        userQuery: '[[[D, []]]]',
         returned: [],
-        userQuery: '[[[d, []]]]',
         error: null,
     },
     {
@@ -82,9 +80,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "[],u8",
-        returned: [],
         userQuery: "[],u8",
+        returned: [],
         error: null,
     },
     {
@@ -109,9 +106,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "[u8]",
-        returned: [],
         userQuery: "[u8]",
+        returned: [],
         error: null,
     },
     {
@@ -144,9 +140,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "[u8,u8]",
-        returned: [],
         userQuery: "[u8,u8]",
+        returned: [],
         error: null,
     },
     {
@@ -180,9 +175,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "[u8<u8>]",
-        returned: [],
         userQuery: "[u8<u8>]",
+        returned: [],
         error: null,
     },
     {
@@ -198,90 +192,80 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "[]",
-        returned: [],
         userQuery: "[]",
+        returned: [],
         error: null,
     },
     {
         query: '[>',
         elems: [],
         foundElems: 0,
-        original: "[>",
-        returned: [],
         userQuery: "[>",
+        returned: [],
         error: "Unexpected `>` after `[`",
     },
     {
         query: '[<',
         elems: [],
         foundElems: 0,
-        original: "[<",
-        returned: [],
         userQuery: "[<",
+        returned: [],
         error: "Found generics without a path",
     },
     {
         query: '[a>',
         elems: [],
         foundElems: 0,
-        original: "[a>",
-        returned: [],
         userQuery: "[a>",
+        returned: [],
         error: "Unexpected `>` after `[`",
     },
     {
         query: '[a<',
         elems: [],
         foundElems: 0,
-        original: "[a<",
-        returned: [],
         userQuery: "[a<",
+        returned: [],
         error: "Unclosed `<`",
     },
     {
         query: '[a',
         elems: [],
         foundElems: 0,
-        original: "[a",
-        returned: [],
         userQuery: "[a",
+        returned: [],
         error: "Unclosed `[`",
     },
     {
         query: '[',
         elems: [],
         foundElems: 0,
-        original: "[",
-        returned: [],
         userQuery: "[",
+        returned: [],
         error: "Unclosed `[`",
     },
     {
         query: ']',
         elems: [],
         foundElems: 0,
-        original: "]",
-        returned: [],
         userQuery: "]",
+        returned: [],
         error: "Unexpected `]`",
     },
     {
         query: '[a<b>',
         elems: [],
         foundElems: 0,
-        original: "[a<b>",
-        returned: [],
         userQuery: "[a<b>",
+        returned: [],
         error: "Unclosed `[`",
     },
     {
         query: 'a<b>]',
         elems: [],
         foundElems: 0,
-        original: "a<b>]",
-        returned: [],
         userQuery: "a<b>]",
+        returned: [],
         error: "Unexpected `]` after `>`",
     },
     {
@@ -306,18 +290,16 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "primitive:[u8]",
-        returned: [],
         userQuery: "primitive:[u8]",
+        returned: [],
         error: null,
     },
     {
         query: 'macro:[u8]',
         elems: [],
         foundElems: 0,
-        original: "macro:[u8]",
-        returned: [],
         userQuery: "macro:[u8]",
+        returned: [],
         error: "Invalid search type: primitive `[]` and `macro` both specified",
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-tuple.js b/tests/rustdoc-js-std/parser-tuple.js
index eb16289d3c0..61925068387 100644
--- a/tests/rustdoc-js-std/parser-tuple.js
+++ b/tests/rustdoc-js-std/parser-tuple.js
@@ -3,9 +3,8 @@ const PARSED = [
         query: '(((D, ()))',
         elems: [],
         foundElems: 0,
-        original: '(((D, ()))',
+        userQuery: '(((D, ()))',
         returned: [],
-        userQuery: '(((d, ()))',
         error: 'Unclosed `(`',
     },
     {
@@ -18,7 +17,7 @@ const PARSED = [
                 pathLast: "()",
                 generics: [
                     {
-                        name: "d",
+                        name: "D",
                         fullPath: ["d"],
                         pathWithoutLast: [],
                         pathLast: "d",
@@ -38,9 +37,8 @@ const PARSED = [
             }
         ],
         foundElems: 1,
-        original: '(((D, ())))',
+        userQuery: '(((D, ())))',
         returned: [],
-        userQuery: '(((d, ())))',
         error: null,
     },
     {
@@ -64,9 +62,8 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "(),u8",
-        returned: [],
         userQuery: "(),u8",
+        returned: [],
         error: null,
     },
     // Parens act as grouping operators when:
@@ -88,9 +85,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "(u8)",
-        returned: [],
         userQuery: "(u8)",
+        returned: [],
         error: null,
     },
     {
@@ -115,9 +111,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "(u8,)",
-        returned: [],
         userQuery: "(u8,)",
+        returned: [],
         error: null,
     },
     {
@@ -142,9 +137,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "(,u8)",
-        returned: [],
         userQuery: "(,u8)",
+        returned: [],
         error: null,
     },
     {
@@ -169,9 +163,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "primitive:(u8)",
-        returned: [],
         userQuery: "primitive:(u8)",
+        returned: [],
         error: null,
     },
     {
@@ -187,9 +180,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "(primitive:u8)",
-        returned: [],
         userQuery: "(primitive:u8)",
+        returned: [],
         error: null,
     },
     {
@@ -222,9 +214,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "(u8,u8)",
-        returned: [],
         userQuery: "(u8,u8)",
+        returned: [],
         error: null,
     },
     {
@@ -249,9 +240,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "(u8<u8>)",
-        returned: [],
         userQuery: "(u8<u8>)",
+        returned: [],
         error: null,
     },
     {
@@ -267,99 +257,88 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "()",
-        returned: [],
         userQuery: "()",
+        returned: [],
         error: null,
     },
     {
         query: '(>',
         elems: [],
         foundElems: 0,
-        original: "(>",
-        returned: [],
         userQuery: "(>",
+        returned: [],
         error: "Unexpected `>` after `(`",
     },
     {
         query: '(<',
         elems: [],
         foundElems: 0,
-        original: "(<",
-        returned: [],
         userQuery: "(<",
+        returned: [],
         error: "Found generics without a path",
     },
     {
         query: '(a>',
         elems: [],
         foundElems: 0,
-        original: "(a>",
-        returned: [],
         userQuery: "(a>",
+        returned: [],
         error: "Unexpected `>` after `(`",
     },
     {
         query: '(a<',
         elems: [],
         foundElems: 0,
-        original: "(a<",
-        returned: [],
         userQuery: "(a<",
+        returned: [],
         error: "Unclosed `<`",
     },
     {
         query: '(a',
         elems: [],
         foundElems: 0,
-        original: "(a",
-        returned: [],
         userQuery: "(a",
+        returned: [],
         error: "Unclosed `(`",
     },
     {
         query: '(',
         elems: [],
         foundElems: 0,
-        original: "(",
-        returned: [],
         userQuery: "(",
+        returned: [],
         error: "Unclosed `(`",
     },
     {
         query: ')',
         elems: [],
         foundElems: 0,
-        original: ")",
-        returned: [],
         userQuery: ")",
+        returned: [],
         error: "Unexpected `)`",
     },
     {
         query: '(a<b>',
         elems: [],
         foundElems: 0,
-        original: "(a<b>",
-        returned: [],
         userQuery: "(a<b>",
+        returned: [],
         error: "Unclosed `(`",
     },
     {
         query: 'a<b>)',
         elems: [],
         foundElems: 0,
-        original: "a<b>)",
-        returned: [],
         userQuery: "a<b>)",
+        returned: [],
         error: "Unexpected `)` after `>`",
     },
     {
         query: 'macro:(u8)',
         elems: [],
         foundElems: 0,
-        original: "macro:(u8)",
-        returned: [],
         userQuery: "macro:(u8)",
+        returned: [],
         error: "Invalid search type: primitive `()` and `macro` both specified",
     },
 ];
diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js
index 499b82a3469..828b0a7d9f6 100644
--- a/tests/rustdoc-js-std/parser-weird-queries.js
+++ b/tests/rustdoc-js-std/parser-weird-queries.js
@@ -15,9 +15,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "a b",
-        returned: [],
         userQuery: "a b",
+        returned: [],
         error: null,
     },
     {
@@ -32,9 +31,8 @@ const PARSED = [
             },
         ],
         foundElems: 1,
-        original: "a   b",
-        returned: [],
         userQuery: "a   b",
+        returned: [],
         error: null,
     },
     {
@@ -56,36 +54,32 @@ const PARSED = [
             },
         ],
         foundElems: 2,
-        original: "aaa,a",
-        returned: [],
         userQuery: "aaa,a",
+        returned: [],
         error: null,
     },
     {
         query: ',,,,',
         elems: [],
         foundElems: 0,
-        original: ",,,,",
-        returned: [],
         userQuery: ",,,,",
+        returned: [],
         error: null,
     },
     {
         query: 'mod    :',
         elems: [],
         foundElems: 0,
-        original: 'mod    :',
-        returned: [],
         userQuery: 'mod    :',
+        returned: [],
         error: "Unexpected `:` (expected path after type filter `mod:`)",
     },
     {
         query: 'mod\t:',
         elems: [],
         foundElems: 0,
-        original: 'mod :',
-        returned: [],
         userQuery: 'mod :',
+        returned: [],
         error: "Unexpected `:` (expected path after type filter `mod:`)",
     },
 ];
diff --git a/tests/rustdoc-js-std/simd-type-signatures.js b/tests/rustdoc-js-std/simd-type-signatures.js
index c07f15dcbe8..4fc14e65ac4 100644
--- a/tests/rustdoc-js-std/simd-type-signatures.js
+++ b/tests/rustdoc-js-std/simd-type-signatures.js
@@ -22,11 +22,6 @@ const EXPECTED = [
             },
             {
                 'path': 'std::simd::prelude::Simd',
-                'name': 'simd_clamp',
-                'href': '../std/simd/prelude/struct.Simd.html#impl-SimdOrd-for-Simd%3Ci16,+N%3E/method.simd_clamp'
-            },
-            {
-                'path': 'std::simd::prelude::Simd',
                 'name': 'saturating_add',
                 'href': '../std/simd/prelude/struct.Simd.html#impl-SimdInt-for-Simd%3Ci16,+N%3E/method.saturating_add'
             },
@@ -35,6 +30,11 @@ const EXPECTED = [
                 'name': 'saturating_sub',
                 'href': '../std/simd/prelude/struct.Simd.html#impl-SimdInt-for-Simd%3Ci16,+N%3E/method.saturating_sub'
             },
+            {
+                'path': 'std::simd::prelude::Simd',
+                'name': 'simd_clamp',
+                'href': '../std/simd/prelude/struct.Simd.html#impl-SimdOrd-for-Simd%3Ci16,+N%3E/method.simd_clamp'
+            },
         ],
     },
     {
@@ -52,11 +52,6 @@ const EXPECTED = [
             },
             {
                 'path': 'std::simd::prelude::Simd',
-                'name': 'simd_clamp',
-                'href': '../std/simd/prelude/struct.Simd.html#impl-SimdOrd-for-Simd%3Ci8,+N%3E/method.simd_clamp'
-            },
-            {
-                'path': 'std::simd::prelude::Simd',
                 'name': 'saturating_add',
                 'href': '../std/simd/prelude/struct.Simd.html#impl-SimdInt-for-Simd%3Ci8,+N%3E/method.saturating_add'
             },
@@ -65,6 +60,11 @@ const EXPECTED = [
                 'name': 'saturating_sub',
                 'href': '../std/simd/prelude/struct.Simd.html#impl-SimdInt-for-Simd%3Ci8,+N%3E/method.saturating_sub'
             },
+            {
+                'path': 'std::simd::prelude::Simd',
+                'name': 'simd_clamp',
+                'href': '../std/simd/prelude/struct.Simd.html#impl-SimdOrd-for-Simd%3Ci8,+N%3E/method.simd_clamp'
+            },
         ],
     },
 ];
diff --git a/tests/rustdoc-js-std/transmute-fail.js b/tests/rustdoc-js-std/transmute-fail.js
new file mode 100644
index 00000000000..c4dddf3cf3c
--- /dev/null
+++ b/tests/rustdoc-js-std/transmute-fail.js
@@ -0,0 +1,13 @@
+// should-fail
+const EXPECTED = [
+    {
+        // Keep this test case identical to `transmute`, except the
+        // should-fail tag and the search query below:
+        'query': 'generic:T -> generic:T',
+        'others': [
+            { 'path': 'std::intrinsics::simd', 'name': 'simd_as' },
+            { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' },
+            { 'path': 'std::intrinsics', 'name': 'transmute' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js-std/transmute.js b/tests/rustdoc-js-std/transmute.js
new file mode 100644
index 00000000000..0e52e21e0de
--- /dev/null
+++ b/tests/rustdoc-js-std/transmute.js
@@ -0,0 +1,12 @@
+const EXPECTED = [
+    {
+        // Keep this test case identical to `transmute-fail`, except the
+        // should-fail tag and the search query below:
+        'query': 'generic:T -> generic:U',
+        'others': [
+            { 'path': 'std::intrinsics::simd', 'name': 'simd_as' },
+            { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' },
+            { 'path': 'std::intrinsics', 'name': 'transmute' },
+        ],
+    },
+];
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
new file mode 100644
index 00000000000..9fd14f9d9fb
--- /dev/null
+++ b/tests/rustdoc-js/assoc-type-unbound.js
@@ -0,0 +1,39 @@
+// exact-check
+
+const EXPECTED = [
+    // Trait-associated types (that is, associated types with no constraints)
+    // are treated like type parameters, so that you can "pattern match"
+    // them. We should avoid redundant output (no `Item=MyIter::Item` stuff)
+    // and should give reasonable results
+    {
+        'query': 'MyIter<T> -> Option<T>',
+        'correction': null,
+        'others': [
+            {
+                'path': 'assoc_type_unbound::MyIter',
+                'name': 'next',
+                'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
+                'displayMappedNames': 'T = MyIter::Item',
+                'displayWhereClause': '',
+            },
+        ],
+    },
+    {
+        'query': 'MyIter<Item=T> -> Option<T>',
+        'correction': null,
+        'others': [
+            {
+                'path': 'assoc_type_unbound::MyIter',
+                'name': 'next',
+                'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
+                'displayMappedNames': 'T = MyIter::Item',
+                'displayWhereClause': '',
+            },
+        ],
+    },
+    {
+        'query': 'MyIter<T> -> Option<Item=T>',
+        'correction': null,
+        'others': [],
+    },
+];
diff --git a/tests/rustdoc-js/assoc-type-unbound.rs b/tests/rustdoc-js/assoc-type-unbound.rs
new file mode 100644
index 00000000000..713b77b5007
--- /dev/null
+++ b/tests/rustdoc-js/assoc-type-unbound.rs
@@ -0,0 +1,4 @@
+pub trait MyIter {
+    type Item;
+    fn next(&mut self) -> Option<Self::Item>;
+}
diff --git a/tests/rustdoc-js/assoc-type.js b/tests/rustdoc-js/assoc-type.js
index eec4e7a8258..0edf10e794e 100644
--- a/tests/rustdoc-js/assoc-type.js
+++ b/tests/rustdoc-js/assoc-type.js
@@ -7,16 +7,40 @@ const EXPECTED = [
         'query': 'iterator<something> -> u32',
         'correction': null,
         'others': [
-            { 'path': 'assoc_type::my', 'name': 'other_fn' },
-            { 'path': 'assoc_type', 'name': 'my_fn' },
+            {
+                'path': 'assoc_type::my',
+                'name': 'other_fn',
+                'displayType': 'X -> `u32`',
+                'displayMappedNames': '',
+                'displayWhereClause': 'X: `Iterator`<`Something`>',
+            },
+            {
+                'path': 'assoc_type',
+                'name': 'my_fn',
+                'displayType': 'X -> `u32`',
+                'displayMappedNames': '',
+                'displayWhereClause': 'X: `Iterator`<Item=`Something`>',
+            },
         ],
     },
     {
         'query': 'iterator<something>',
         'correction': null,
         'in_args': [
-            { 'path': 'assoc_type::my', 'name': 'other_fn' },
-            { 'path': 'assoc_type', 'name': 'my_fn' },
+            {
+                'path': 'assoc_type::my',
+                'name': 'other_fn',
+                'displayType': 'X -> u32',
+                'displayMappedNames': '',
+                'displayWhereClause': 'X: `Iterator`<`Something`>',
+            },
+            {
+                'path': 'assoc_type',
+                'name': 'my_fn',
+                'displayType': 'X -> u32',
+                'displayMappedNames': '',
+                'displayWhereClause': 'X: `Iterator`<Item=`Something`>',
+            },
         ],
     },
     {
@@ -26,8 +50,20 @@ const EXPECTED = [
             { 'path': 'assoc_type', 'name': 'Something' },
         ],
         'in_args': [
-            { 'path': 'assoc_type::my', 'name': 'other_fn' },
-            { 'path': 'assoc_type', 'name': 'my_fn' },
+            {
+                'path': 'assoc_type::my',
+                'name': 'other_fn',
+                'displayType': '`X` -> u32',
+                'displayMappedNames': '',
+                'displayWhereClause': 'X: Iterator<`Something`>',
+            },
+            {
+                'path': 'assoc_type',
+                'name': 'my_fn',
+                'displayType': '`X` -> u32',
+                'displayMappedNames': '',
+                'displayWhereClause': 'X: Iterator<Item=`Something`>',
+            },
         ],
     },
     // if I write an explicit binding, only it shows up
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-trait.js b/tests/rustdoc-js/generics-trait.js
index a71393b5e05..8da9c67050e 100644
--- a/tests/rustdoc-js/generics-trait.js
+++ b/tests/rustdoc-js/generics-trait.js
@@ -5,10 +5,22 @@ const EXPECTED = [
         'query': 'Result<SomeTrait>',
         'correction': null,
         'in_args': [
-            { 'path': 'generics_trait', 'name': 'beta' },
+            {
+                'path': 'generics_trait',
+                'name': 'beta',
+                'displayType': '`Result`<`T`, ()> -> ()',
+                'displayMappedNames': '',
+                'displayWhereClause': 'T: `SomeTrait`',
+            },
         ],
         'returned': [
-            { 'path': 'generics_trait', 'name': 'bet' },
+            {
+                'path': 'generics_trait',
+                'name': 'bet',
+                'displayType': ' -> `Result`<`T`, ()>',
+                'displayMappedNames': '',
+                'displayWhereClause': 'T: `SomeTrait`',
+            },
         ],
     },
     {
@@ -25,20 +37,44 @@ const EXPECTED = [
         'query': 'OtherThingxxxxxxxx',
         'correction': null,
         'in_args': [
-            { 'path': 'generics_trait', 'name': 'alpha' },
+            {
+                'path': 'generics_trait',
+                'name': 'alpha',
+                'displayType': 'Result<`T`, ()> -> ()',
+                'displayMappedNames': '',
+                'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
+            },
         ],
         'returned': [
-            { 'path': 'generics_trait', 'name': 'alef' },
+            {
+                'path': 'generics_trait',
+                'name': 'alef',
+                'displayType': ' -> Result<`T`, ()>',
+                'displayMappedNames': '',
+                'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
+            },
         ],
     },
     {
         'query': 'OtherThingxxxxxxxy',
         'correction': 'OtherThingxxxxxxxx',
         'in_args': [
-            { 'path': 'generics_trait', 'name': 'alpha' },
+            {
+                'path': 'generics_trait',
+                'name': 'alpha',
+                'displayType': 'Result<`T`, ()> -> ()',
+                'displayMappedNames': '',
+                'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
+            },
         ],
         'returned': [
-            { 'path': 'generics_trait', 'name': 'alef' },
+            {
+                'path': 'generics_trait',
+                'name': 'alef',
+                'displayType': ' -> Result<`T`, ()>',
+                'displayMappedNames': '',
+                'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
+            },
         ],
     },
 ];
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/impl-trait.js b/tests/rustdoc-js/impl-trait.js
index 8bb3f2d3e99..3d7d0ca5bcd 100644
--- a/tests/rustdoc-js/impl-trait.js
+++ b/tests/rustdoc-js/impl-trait.js
@@ -23,8 +23,8 @@ const EXPECTED = [
         'others': [
             { 'path': 'impl_trait', 'name': 'bbbbbbb' },
             { 'path': 'impl_trait::Ccccccc', 'name': 'ddddddd' },
-            { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' },
             { 'path': 'impl_trait::Ccccccc', 'name': 'ggggggg' },
+            { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' },
         ],
     },
     {
@@ -39,14 +39,14 @@ const EXPECTED = [
             { 'path': 'impl_trait', 'name': 'Aaaaaaa' },
         ],
         'in_args': [
-            { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' },
             { 'path': 'impl_trait::Ccccccc', 'name': 'eeeeeee' },
+            { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' },
         ],
         'returned': [
             { 'path': 'impl_trait', 'name': 'bbbbbbb' },
             { 'path': 'impl_trait::Ccccccc', 'name': 'ddddddd' },
-            { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' },
             { 'path': 'impl_trait::Ccccccc', 'name': 'ggggggg' },
+            { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' },
         ],
     },
 ];
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/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js
index 1765a69152a..6a2c4cc637c 100644
--- a/tests/rustdoc-js/non-english-identifier.js
+++ b/tests/rustdoc-js/non-english-identifier.js
@@ -11,30 +11,29 @@ const PARSED = [
         }],
         returned: [],
         foundElems: 1,
-        original: "中文",
         userQuery: "中文",
         error: null,
     },
     {
         query: '_0Mixed中英文',
         elems: [{
-            name: "_0mixed中英文",
+            name: "_0Mixed中英文",
             fullPath: ["_0mixed中英文"],
             pathWithoutLast: [],
             pathLast: "_0mixed中英文",
+            normalizedPathLast: "0mixed中英文",
             generics: [],
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "_0Mixed中英文",
+        userQuery: "_0Mixed中英文",
         returned: [],
-        userQuery: "_0mixed中英文",
         error: null,
     },
     {
         query: 'my_crate::中文API',
         elems: [{
-            name: "my_crate::中文api",
+            name: "my_crate::中文API",
             fullPath: ["my_crate", "中文api"],
             pathWithoutLast: ["my_crate"],
             pathLast: "中文api",
@@ -42,26 +41,25 @@ const PARSED = [
             typeFilter: -1,
         }],
         foundElems: 1,
-        original: "my_crate::中文API",
+        userQuery: "my_crate::中文API",
         returned: [],
-        userQuery: "my_crate::中文api",
         error: null,
     },
     {
         query: '类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>',
         elems: [{
-            name: "类型a",
+            name: "类型A",
             fullPath: ["类型a"],
             pathWithoutLast: [],
             pathLast: "类型a",
             generics: [],
         }, {
-            name: "类型b",
+            name: "类型B",
             fullPath: ["类型b"],
             pathWithoutLast: [],
             pathLast: "类型b",
             generics: [{
-                name: "约束c",
+                name: "约束C",
                 fullPath: ["约束c"],
                 pathWithoutLast: [],
                 pathLast: "约束c",
@@ -71,15 +69,21 @@ const PARSED = [
         foundElems: 3,
         totalElems: 5,
         literalSearch: true,
-        original: "类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>",
+        userQuery: "类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>",
         returned: [{
             name: "返回类型",
             fullPath: ["返回类型"],
             pathWithoutLast: [],
             pathLast: "返回类型",
             generics: [],
+            bindings: [["关联类型", [{
+                name: "路径::约束D",
+                fullPath: ["路径", "约束d"],
+                pathWithoutLast: ["路径"],
+                pathLast: "约束d",
+                generics: [],
+            }]]],
         }],
-        userQuery: "类型a,类型b<约束c>->返回类型<关联类型=路径::约束d>",
         error: null,
     },
     {
@@ -93,18 +97,16 @@ const PARSED = [
             typeFilter: 16,
         }],
         foundElems: 1,
-        original: "my_crate 中文宏!",
-        returned: [],
         userQuery: "my_crate 中文宏!",
+        returned: [],
         error: null,
     },
     {
         query: '非法符号——',
         elems: [],
         foundElems: 0,
-        original: "非法符号——",
-        returned: [],
         userQuery: "非法符号——",
+        returned: [],
         error: "Unexpected `—` after `号` (not a valid identifier)",
     }
 ]
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/rustdoc-js/type-parameters.js b/tests/rustdoc-js/type-parameters.js
index e045409e507..fa2b8d2ebfd 100644
--- a/tests/rustdoc-js/type-parameters.js
+++ b/tests/rustdoc-js/type-parameters.js
@@ -11,9 +11,9 @@ const EXPECTED = [
     {
         query: '-> generic:T',
         others: [
-            { path: 'foo', name: 'beta' },
             { path: 'foo', name: 'bet' },
             { path: 'foo', name: 'alef' },
+            { path: 'foo', name: 'beta' },
         ],
     },
     {
@@ -50,8 +50,8 @@ const EXPECTED = [
     {
         query: 'generic:T',
         in_args: [
-            { path: 'foo', name: 'beta' },
             { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'beta' },
             { path: 'foo', name: 'alternate' },
             { path: 'foo', name: 'other' },
         ],
@@ -59,8 +59,8 @@ const EXPECTED = [
     {
         query: 'generic:Other',
         in_args: [
-            { path: 'foo', name: 'beta' },
             { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'beta' },
             { path: 'foo', name: 'alternate' },
             { path: 'foo', name: 'other' },
         ],
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`.