From bbdd6e81252696a626375e2051efef9785cd288b Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 10 Jul 2024 14:00:35 -0700 Subject: [PATCH] rustdoc-search: simplify rules for generics and type params This commit is a response to feedback on the displayed type signatures results, by making type params looser and generics act stricter. Type params are loosened by allowing a single function type param to match multiple query params. In other words, the relationship is loosened to N:1 instead of the older 1:1 rule. This change also allows a type param to be unboxed in one spot and matched in another. Generics are tightened by making order significant. This means `Vec` 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` 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. Find the discussion on: * * --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_passes/messages.ftl | 3 + compiler/rustc_passes/src/check_attr.rs | 28 +- compiler/rustc_passes/src/errors.rs | 7 + compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/boxed.rs | 1 + library/core/src/future/future.rs | 1 + library/core/src/option.rs | 1 + library/core/src/result.rs | 1 + .../rustdoc/src/read-documentation/search.md | 12 +- src/librustdoc/html/render/search_index.rs | 74 ++++- src/librustdoc/html/static/js/search.js | 308 ++++++++++++++---- .../rustdoc-gui/search-about-this-result.goml | 4 +- tests/rustdoc-js-std/bufread-fill-buf.js | 11 +- .../rustdoc-js-std/option-type-signatures.js | 31 +- tests/rustdoc-js-std/vec-type-signatures.js | 4 +- tests/rustdoc-js/assoc-type-backtrack.js | 57 +++- tests/rustdoc-js/assoc-type-backtrack.rs | 4 + tests/rustdoc-js/assoc-type-unbound.js | 4 +- tests/rustdoc-js/assoc-type.rs | 12 +- tests/rustdoc-js/generics-impl.js | 8 +- tests/rustdoc-js/generics-impl.rs | 4 +- .../generics-match-ambiguity-no-unbox.js | 68 ++++ .../generics-match-ambiguity-no-unbox.rs | 18 + tests/rustdoc-js/generics-match-ambiguity.js | 12 +- tests/rustdoc-js/generics-match-ambiguity.rs | 6 +- tests/rustdoc-js/generics-nested.js | 5 +- tests/rustdoc-js/generics-unbox.js | 4 - tests/rustdoc-js/generics-unbox.rs | 8 + tests/rustdoc-js/generics.js | 17 +- tests/rustdoc-js/hof.js | 25 +- tests/rustdoc-js/looks-like-rustc-interner.js | 2 + tests/rustdoc-js/nested-unboxed.js | 9 +- tests/rustdoc-js/nested-unboxed.rs | 3 + tests/rustdoc-js/reference.js | 15 +- tests/rustdoc-js/tuple-unit.js | 4 +- .../feature-gate-rustdoc_internals.rs | 4 + .../feature-gate-rustdoc_internals.stderr | 12 +- 38 files changed, 581 insertions(+), 208 deletions(-) create mode 100644 tests/rustdoc-js/generics-match-ambiguity-no-unbox.js create mode 100644 tests/rustdoc-js/generics-match-ambiguity-no-unbox.rs diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 3ceb8e0711a21..261db37557523 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -217,6 +217,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 0318d34fb7322..d1ba0f112baca 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -226,6 +226,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 bd157d1d61973..f2fa9826f8627 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -13,11 +13,12 @@ use rustc_ast::{ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use rustc_hir as hir; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, self, FnSig, ForeignItem, HirId, Item, ItemKind, MethodKind, Safety, Target, - TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, + self as hir, self, AssocItemKind, FnSig, ForeignItem, HirId, Item, ItemKind, MethodKind, + Safety, Target, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -962,6 +963,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_doc_search_unbox(&self, meta: &NestedMetaItem, 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 @@ -1180,6 +1198,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::html_favicon_url | sym::html_logo_url | sym::html_playground_url diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 36dfc40e7628b..18f831d108deb 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -230,6 +230,13 @@ pub struct DocKeywordOnlyImpl { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_doc_search_unbox_invalid)] +pub struct DocSearchUnboxInvalid { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(passes_doc_inline_conflict)] #[help] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9cb729ec48588..8dd48949d0d05 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1704,6 +1704,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 7de412595993a..1958883f07eba 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -230,6 +230,7 @@ mod thin; #[lang = "owned_box"] #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] +#[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/core/src/future/future.rs b/library/core/src/future/future.rs index ca1c2d1ca1f2e..234914c20fc31 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 6c89c81018038..9da752afa49d5 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -568,6 +568,7 @@ use crate::{cmp, convert, hint, mem, slice}; #[lang = "Option"] #[stable(feature = "rust1", since = "1.0.0")] #[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is manually implemented equivalently +#[cfg_attr(not(bootstrap), doc(search_unbox))] pub enum Option { /// No value. #[lang = "None"] diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 7f278296b7b88..c5f28dea27f77 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -524,6 +524,7 @@ use crate::{convert, fmt, hint}; #[must_use = "this `Result` may be an `Err` variant, which should be handled"] #[rustc_diagnostic_item = "Result"] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), doc(search_unbox))] pub enum Result { /// Contains the success value #[lang = "Ok"] diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index e912ca0fe5b45..718d2201c3a5d 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 -> bool -/// MyTrait -> bool /// MyTrait -> bool -/// MyTrait -> bool /// /// The following queries, however, will *not* match it: /// /// MyTrait -> bool /// MyTrait -> bool +/// MyTrait -> bool +/// MyTrait -> bool pub fn my_fn(x: impl MyTrait) -> 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, Error>` will match these queries: * `&mut Read -> Result, Error>` * `Read -> Result, Error>` -* `Read -> Result` * `Read -> Result>` * `Read -> u8` -But it *does not* match `Result` or `Result>`. +But it *does not* match `Result` or `Result>`, +because those are nested incorrectly, and it does not match +`Result>` or `Result`, because those are +in the wrong order. To search for a function that accepts a function as a parameter, like `Iterator::all`, wrap the nested signature in parenthesis, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index e8954feb8922b..ddb0647f4dd79 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -12,8 +12,8 @@ use rustc_span::symbol::{kw, Symbol}; use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer}; use thin_vec::ThinVec; -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; @@ -64,7 +64,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, Option>)> = vec![]; + let mut crate_paths: Vec<(ItemType, Vec, Option>, bool)> = vec![]; // Attach all orphan items to the type's definition if the type // has since been learned. @@ -130,10 +130,11 @@ pub(crate) fn build_index<'tcx>( map: &mut FxHashMap, itemid: F, lastpathid: &mut isize, - crate_paths: &mut Vec<(ItemType, Vec, Option>)>, + crate_paths: &mut Vec<(ItemType, Vec, Option>, 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()), @@ -145,6 +146,7 @@ pub(crate) fn build_index<'tcx>( item_type, path.to_vec(), exact_path.map(|path| path.to_vec()), + search_unbox, )); RenderTypeId::Index(pathid) } @@ -158,9 +160,21 @@ pub(crate) fn build_index<'tcx>( primitives: &mut FxHashMap, associated_types: &mut FxHashMap, lastpathid: &mut isize, - crate_paths: &mut Vec<(ItemType, Vec, Option>)>, + crate_paths: &mut Vec<(ItemType, Vec, Option>, bool)>, + tcx: TyCtxt<'_>, ) -> Option { + 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, @@ -170,6 +184,7 @@ pub(crate) fn build_index<'tcx>( ItemType::Keyword, &[kw::Mut], None, + search_unbox, )), RenderTypeId::DefId(defid) => { if let Some(&(ref fqp, item_type)) = @@ -193,6 +208,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 @@ -208,6 +224,7 @@ pub(crate) fn build_index<'tcx>( ItemType::Primitive, &[sym], None, + search_unbox, )) } RenderTypeId::Index(_) => Some(id), @@ -219,6 +236,7 @@ pub(crate) fn build_index<'tcx>( ItemType::AssocType, &[sym], None, + search_unbox, )), } } @@ -230,7 +248,8 @@ pub(crate) fn build_index<'tcx>( primitives: &mut FxHashMap, associated_types: &mut FxHashMap, lastpathid: &mut isize, - crate_paths: &mut Vec<(ItemType, Vec, Option>)>, + crate_paths: &mut Vec<(ItemType, Vec, Option>, bool)>, + tcx: TyCtxt<'_>, ) { if let Some(generics) = &mut ty.generics { for item in generics { @@ -242,6 +261,7 @@ pub(crate) fn build_index<'tcx>( associated_types, lastpathid, crate_paths, + tcx, ); } } @@ -255,6 +275,7 @@ pub(crate) fn build_index<'tcx>( associated_types, lastpathid, crate_paths, + tcx, ); let Some(converted_associated_type) = converted_associated_type else { return false; @@ -269,6 +290,7 @@ pub(crate) fn build_index<'tcx>( associated_types, lastpathid, crate_paths, + tcx, ); } true @@ -286,6 +308,7 @@ pub(crate) fn build_index<'tcx>( associated_types, lastpathid, crate_paths, + tcx, ); } if let Some(search_type) = &mut item.search_type { @@ -298,6 +321,7 @@ pub(crate) fn build_index<'tcx>( &mut associated_types, &mut lastpathid, &mut crate_paths, + tcx, ); } for item in &mut search_type.output { @@ -309,6 +333,7 @@ pub(crate) fn build_index<'tcx>( &mut associated_types, &mut lastpathid, &mut crate_paths, + tcx, ); } for constraint in &mut search_type.where_clause { @@ -321,6 +346,7 @@ pub(crate) fn build_index<'tcx>( &mut associated_types, &mut lastpathid, &mut crate_paths, + tcx, ); } } @@ -348,7 +374,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 @@ -429,7 +460,7 @@ pub(crate) fn build_index<'tcx>( struct CrateData<'a> { items: Vec<&'a IndexItem>, - paths: Vec<(ItemType, Vec, Option>)>, + paths: Vec<(ItemType, Vec, Option>, 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`. @@ -448,6 +479,7 @@ pub(crate) fn build_index<'tcx>( name: Symbol, path: Option, exact_path: Option, + search_unbox: bool, } impl Serialize for Paths { @@ -465,6 +497,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::)?; + } + if self.exact_path.is_none() { + seq.serialize_element(&None::)?; + } + seq.serialize_element(&1)?; + } seq.end() } } @@ -487,9 +528,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]); @@ -515,10 +562,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; } @@ -530,10 +578,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) => { @@ -542,10 +591,11 @@ pub(crate) fn build_index<'tcx>( revert_extra_paths.insert(index, full_path); } paths.push(Paths { - ty: *ty, + ty, name: *path.last().unwrap(), path: Some(index), exact_path, + search_unbox, }); } } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 96700028e1a62..2f01382f409d1 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1630,17 +1630,20 @@ function initSearch(rawSearchIndex) { */ function writeFn(fnType, result) { if (fnType.id < 0) { - const queryId = mgens && mgens.has(fnType.id) ? mgens.get(fnType.id) : null; if (fnParamNames[-1 - fnType.id] === "") { for (const nested of fnType.generics) { writeFn(nested, result); } return; - } else if (queryId < 0) { - mappedNames.set( - fnParamNames[-1 - fnType.id], - queryParamNames[-1 - queryId], - ); + } else if (mgens) { + for (const [queryId, fnId] of mgens) { + if (fnId === fnType.id) { + mappedNames.set( + queryParamNames[-1 - queryId], + fnParamNames[-1 - fnType.id], + ); + } + } } pushText({ name: fnParamNames[-1 - fnType.id], @@ -1863,7 +1866,7 @@ function initSearch(rawSearchIndex) { * @param {Array} queryElems - The elements from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn - * - Map functions generics to query generics (never modified). + * - Map query generics to function generics (never modified). * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, @@ -1904,12 +1907,12 @@ function initSearch(rawSearchIndex) { continue; } if (fnType.id < 0 && queryElem.id < 0) { - if (mgens && mgens.has(fnType.id) && - mgens.get(fnType.id) !== queryElem.id) { + if (mgens && mgens.has(queryElem.id) && + mgens.get(queryElem.id) !== fnType.id) { continue; } const mgensScratch = new Map(mgens); - mgensScratch.set(fnType.id, queryElem.id); + mgensScratch.set(queryElem.id, fnType.id); if (!solutionCb || solutionCb(mgensScratch)) { const highlighted = [...fnTypesIn]; highlighted[i] = Object.assign({ @@ -1925,7 +1928,7 @@ function initSearch(rawSearchIndex) { highlighted[i] = Object.assign({ highlighted: true, }, fnType, { - generics: unifyFunctionTypes( + generics: unifyGenericTypes( fnType.generics, queryElem.generics, whereClause, @@ -1948,17 +1951,11 @@ function initSearch(rawSearchIndex) { continue; } if (fnType.id < 0) { - if (mgens && mgens.has(fnType.id) && - mgens.get(fnType.id) !== 0) { - continue; - } - const mgensScratch = new Map(mgens); - mgensScratch.set(fnType.id, 0); const highlightedGenerics = unifyFunctionTypes( whereClause[(-fnType.id) - 1], queryElems, whereClause, - mgensScratch, + mgens, solutionCb, unboxingDepth + 1, ); @@ -2029,11 +2026,11 @@ function initSearch(rawSearchIndex) { 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; } @@ -2070,7 +2067,7 @@ function initSearch(rawSearchIndex) { } const simplifiedGenerics = solution.simplifiedGenerics; for (const simplifiedMgens of solution.mgens) { - unifiedGenerics = unifyFunctionTypes( + unifiedGenerics = unifyGenericTypes( simplifiedGenerics, queryElem.generics, whereClause, @@ -2078,7 +2075,7 @@ function initSearch(rawSearchIndex) { solutionCb, unboxingDepth, ); - if (unifiedGenerics) { + if (unifiedGenerics !== null) { unifiedGenericsMgens = simplifiedMgens; return true; } @@ -2122,16 +2119,6 @@ function initSearch(rawSearchIndex) { )) { 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; @@ -2142,7 +2129,7 @@ function initSearch(rawSearchIndex) { fnTypes.toSpliced(i, 1, ...bindings, ...generics), queryElems, whereClause, - mgensScratch, + mgens, solutionCb, unboxingDepth + 1, ); @@ -2166,6 +2153,199 @@ function initSearch(rawSearchIndex) { } 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` only matches an actual + * `Vec` of `Allocators` and not the implicit `Allocator` parameter that every + * `Vec` has. + * + * @param {Array} fnTypesIn - The objects to check. + * @param {Array} queryElems - The elements from the parsed query. + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgensIn + * - Map functions generics to query generics (never modified). + * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {number} unboxingDepth + * - Limit checks that Ty matches Vec, + * but not Vec>>>> + * + * @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|null + */ + const mgens = mgensIn === null ? null : new Map(mgensIn); + if (queryElems.length === 0) { + return (!solutionCb || 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. * @@ -2177,7 +2357,7 @@ function initSearch(rawSearchIndex) { * * @param {FunctionType} fnType * @param {QueryElement} queryElem - * @param {Map|null} mgensIn - Map functions generics to query generics. + * @param {Map|null} mgensIn - Map query generics to function generics. * @returns {boolean} */ function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgensIn) { @@ -2187,22 +2367,13 @@ function initSearch(rawSearchIndex) { } // 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 { @@ -2279,7 +2450,7 @@ function initSearch(rawSearchIndex) { * @param {FunctionType} fnType * @param {QueryElement} queryElem * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map} mgensIn - Map functions generics to query generics. + * @param {Map} mgensIn - Map query generics to function generics. * Never modified. * @param {number} unboxingDepth * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} @@ -2347,7 +2518,7 @@ function initSearch(rawSearchIndex) { * @param {FunctionType} fnType * @param {QueryElement} queryElem * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. + * @param {Map|null} mgens - Map query generics to function generics. * @param {number} unboxingDepth * @returns {boolean} */ @@ -2361,19 +2532,10 @@ function initSearch(rawSearchIndex) { 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) -> Result` @@ -2382,10 +2544,11 @@ function initSearch(rawSearchIndex) { 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(), @@ -3143,7 +3306,7 @@ ${item.displayPath}${name}\ displayType.appendChild(line); addWhereLineFn = () => {}; }; - for (const [name, qname] of mappedNames) { + for (const [qname, name] of mappedNames) { // don't care unless the generic name is different if (name === qname) { continue; @@ -3528,6 +3691,7 @@ ${item.displayPath}${name}\ exactPath: null, generics, bindings, + unboxFlag: true, }; } else if (pathIndex === 0) { // `0` is used as a sentinel because it's fewer bytes than `null` @@ -3539,6 +3703,7 @@ ${item.displayPath}${name}\ exactPath: null, generics, bindings, + unboxFlag: true, }; } else { const item = lowercasePaths[pathIndex - 1]; @@ -3554,6 +3719,7 @@ ${item.displayPath}${name}\ exactPath: item.exactPath, generics, bindings, + unboxFlag: item.unboxFlag, }; } const cr = TYPES_POOL.get(result.id); @@ -3591,6 +3757,7 @@ ${item.displayPath}${name}\ if (cr.ty === result.ty && cr.path === result.path && cr.bindings === result.bindings && cr.generics === result.generics && cr.ty === result.ty && cr.name === result.name + && cr.unboxFlag === result.unboxFlag ) { return cr; } @@ -4085,14 +4252,17 @@ ${item.displayPath}${name}\ 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. diff --git a/tests/rustdoc-gui/search-about-this-result.goml b/tests/rustdoc-gui/search-about-this-result.goml index 62780d01ed7e8..1d45c06dc43d4 100644 --- a/tests/rustdoc-gui/search-about-this-result.goml +++ b/tests/rustdoc-gui/search-about-this-result.goml @@ -11,8 +11,8 @@ assert-count: ("#search-tabs button", 1) assert-count: (".search-results > a", 1) assert: "//div[@class='type-signature']/strong[text()='Iterator']" -assert: "//div[@class='type-signature']/strong[text()='(']" -assert: "//div[@class='type-signature']/strong[text()=')']" +assert: "//div[@class='type-signature']/strong[text()='(B']" +assert: "//div[@class='type-signature']/strong[text()='B)']" assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='FnMut']" assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='Iterator::Item']" diff --git a/tests/rustdoc-js-std/bufread-fill-buf.js b/tests/rustdoc-js-std/bufread-fill-buf.js index 3828cf76026ea..6b9309f686408 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', + '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 -> option>>', + '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 1690d5dc8b5bb..3be18e6adf324 100644 --- a/tests/rustdoc-js-std/option-type-signatures.js +++ b/tests/rustdoc-js-std/option-type-signatures.js @@ -80,11 +80,6 @@ const EXPECTED = [ 'name': 'and', 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>', }, - { - 'path': 'std::option::Option', - 'name': 'zip', - 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(T, `U`)>', - }, ], }, { @@ -103,12 +98,12 @@ const EXPECTED = [ ], }, { - 'query': 'option, option -> option', + 'query': 'option, option -> option<(t, u)>', 'others': [ { 'path': 'std::option::Option', 'name': 'zip', - 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, `U`)>', + 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`(T`, `U)`>', }, ], }, @@ -174,37 +169,23 @@ const EXPECTED = [ 'path': 'std::option::Option', 'name': 'map', 'displayType': '`Option`<`T`>, F -> `Option`', - 'displayMappedNames': `T = t, U = u`, + 'displayMappedNames': `t = T, u = U`, 'displayWhereClause': "F: `FnOnce` (T) -> `U`", }, { 'path': 'std::option::Option', 'name': 'and_then', 'displayType': '`Option`<`T`>, F -> `Option`', - 'displayMappedNames': `T = t, U = u`, + 'displayMappedNames': `t = T, u = U`, 'displayWhereClause': "F: `FnOnce` (T) -> Option<`U`>", }, { 'path': 'std::option::Option', 'name': 'zip_with', 'displayType': 'Option, `Option`<`U`>, F -> `Option`', - 'displayMappedNames': `U = t, R = u`, + 'displayMappedNames': `t = U, u = R`, 'displayWhereClause': "F: `FnOnce` (T, U) -> `R`", }, - { - 'path': 'std::task::Poll', - 'name': 'map_ok', - 'displayType': 'Poll<`Option`>>, F -> Poll<`Option`>>', - 'displayMappedNames': `T = t, U = u`, - 'displayWhereClause': "F: `FnOnce` (T) -> `U`", - }, - { - 'path': 'std::task::Poll', - 'name': 'map_err', - 'displayType': 'Poll<`Option`>>, F -> Poll<`Option`>>', - 'displayMappedNames': `T = t, U = u`, - 'displayWhereClause': "F: `FnOnce` (E) -> `U`", - }, ], }, { @@ -214,7 +195,7 @@ const EXPECTED = [ 'path': 'std::option::Option', 'name': 'and_then', 'displayType': '`Option`<`T`>, F -> `Option`', - 'displayMappedNames': `T = t, U = u`, + 'displayMappedNames': `t = T, u = U`, 'displayWhereClause': "F: `FnOnce` (T) -> `Option`<`U`>", }, ], diff --git a/tests/rustdoc-js-std/vec-type-signatures.js b/tests/rustdoc-js-std/vec-type-signatures.js index 4e93caf53beeb..3c2ff30a83330 100644 --- a/tests/rustdoc-js-std/vec-type-signatures.js +++ b/tests/rustdoc-js-std/vec-type-signatures.js @@ -20,12 +20,12 @@ const EXPECTED = [ ], }, { - 'query': 'vec -> Box<[T]>', + 'query': 'vec -> Box<[T]>', 'others': [ { 'path': 'std::boxed::Box', 'name': 'from', - 'displayType': '`Vec` -> `Box`<`[T]`, A>', + '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 493e1a9910d18..87abc80753d94 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, mytrait2 -> T', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' }, + ], + }, + { + 'query': 'cloned>, 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 -> Option', + 'query': 'cloned> -> Option', 'correction': null, 'others': [ { 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' }, ], }, { - 'query': 'mytrait -> Option', + 'query': 'cloned> -> Option', 'correction': null, 'others': [ { 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' }, @@ -89,19 +100,21 @@ const EXPECTED = [ ], }, { - 'query': 'myintofuture> -> myfuture', + 'query': 'myintofuture> -> myfuture', '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 -> myfuture', '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, myintofuture -> myfuture', + 'query': 'myintofuture, myintofuture -> myfuture', 'correction': null, 'others': [ { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, @@ -132,32 +145,42 @@ 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, myintofuture> -> myfuture', 'correction': null, - 'others': [], + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], }, { 'query': 'myintofuture>, myintofuture -> myfuture', 'correction': null, - 'others': [], + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], }, { 'query': 'myintofuture>, myintofuture> -> t', 'correction': null, - 'others': [], + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], }, - // different generics don't match up either + // different generics will match up (didn't used to, but does now) { 'query': 'myintofuture>, myintofuture> -> myfuture', 'correction': null, - 'others': [], + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], }, { 'query': 'myintofuture -> myfuture', 'correction': null, - 'others': [], + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' }, + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], }, ]; diff --git a/tests/rustdoc-js/assoc-type-backtrack.rs b/tests/rustdoc-js/assoc-type-backtrack.rs index 2dfede9dc3831..8a74685b30bb3 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 { 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; diff --git a/tests/rustdoc-js/assoc-type-unbound.js b/tests/rustdoc-js/assoc-type-unbound.js index 611b8bd1501c7..9fd14f9d9fb59 100644 --- a/tests/rustdoc-js/assoc-type-unbound.js +++ b/tests/rustdoc-js/assoc-type-unbound.js @@ -13,7 +13,7 @@ const EXPECTED = [ 'path': 'assoc_type_unbound::MyIter', 'name': 'next', 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>', - 'displayMappedNames': 'MyIter::Item = T', + 'displayMappedNames': 'T = MyIter::Item', 'displayWhereClause': '', }, ], @@ -26,7 +26,7 @@ const EXPECTED = [ 'path': 'assoc_type_unbound::MyIter', 'name': 'next', 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>', - 'displayMappedNames': 'MyIter::Item = T', + 'displayMappedNames': 'T = MyIter::Item', 'displayWhereClause': '', }, ], diff --git a/tests/rustdoc-js/assoc-type.rs b/tests/rustdoc-js/assoc-type.rs index e12e73cb546df..aee8f4b1c3fa8 100644 --- a/tests/rustdoc-js/assoc-type.rs +++ b/tests/rustdoc-js/assoc-type.rs @@ -1,12 +1,22 @@ -pub fn my_fn>(_x: X) -> u32 { +#![feature(rustdoc_internals)] + +pub fn my_fn>(_x: X) -> u32 { 3 } pub struct Something; pub mod my { + #[doc(search_unbox)] pub trait Iterator {} pub fn other_fn>(_: 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 5e33e224876fe..c104730dcbd53 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', '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 -> 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 27d44fdd7e92a..f9fe7f390f3cd 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 { + fn read(&mut self, out: &mut [u8]) -> io::Result { 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 0000000000000..ea4c26d311c3d --- /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', + 'in_args': [ + { 'path': 'generics_match_ambiguity', 'name': 'bar' }, + { 'path': 'generics_match_ambiguity', 'name': 'foo' }, + ], + }, + { + 'query': 'Wrap, Wrap', + 'others': [ + { 'path': 'generics_match_ambiguity', 'name': 'bar' }, + { 'path': 'generics_match_ambiguity', 'name': 'foo' }, + ], + }, + { + 'query': 'Wrap, Wrap', + 'others': [ + { 'path': 'generics_match_ambiguity', 'name': 'bar' }, + { 'path': 'generics_match_ambiguity', 'name': 'foo' }, + ], + }, + { + 'query': 'W3, W3', + 'others': [ + { 'path': 'generics_match_ambiguity', 'name': 'baaa' }, + { 'path': 'generics_match_ambiguity', 'name': 'baab' }, + ], + }, + { + 'query': 'W3, W3', + 'others': [ + { 'path': 'generics_match_ambiguity', 'name': 'baaa' }, + { 'path': 'generics_match_ambiguity', 'name': 'baab' }, + ], + }, + { + // strict generics matching; W2 doesn't match W2>, + // even though W2 works just fine (ignoring the W3) + 'query': 'W2, W2', + 'others': [], + }, + { + 'query': 'W2, W2', + 'others': [], + }, + { + 'query': 'W2, W3', + 'others': [], + }, + { + 'query': 'W2, W2', + '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 0000000000000..43c2896fa2cdf --- /dev/null +++ b/tests/rustdoc-js/generics-match-ambiguity-no-unbox.rs @@ -0,0 +1,18 @@ +#![crate_name = "generics_match_ambiguity"] + +pub struct Wrap(pub T, pub U); + +pub fn foo(a: Wrap, b: Wrap) {} +pub fn bar(a: Wrap, b: Wrap) {} + +pub struct W2(pub T); +pub struct W3(pub T, pub U); + +pub fn baaa(a: W3, b: W3) {} +pub fn baab(a: W3, b: W3) {} +pub fn baac(a: W2>, b: W3) {} +pub fn baad(a: W2>, b: W3) {} +pub fn baae(a: W3, b: W2>) {} +pub fn baaf(a: W3, b: W2>) {} +pub fn baag(a: W2>, b: W2>) {} +pub fn baah(a: W2>, b: W2>) {} diff --git a/tests/rustdoc-js/generics-match-ambiguity.js b/tests/rustdoc-js/generics-match-ambiguity.js index edce4268c5ac7..aadb179321cf2 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 doesn't match W2>, + // even though W2 works just fine (ignoring the W3) 'query': 'W2, W2', - 'others': [ - { 'path': 'generics_match_ambiguity', 'name': 'baag' }, - { 'path': 'generics_match_ambiguity', 'name': 'baah' }, - ], + 'others': [], }, { 'query': 'W2, W2', - 'others': [ - { 'path': 'generics_match_ambiguity', 'name': 'baag' }, - { 'path': 'generics_match_ambiguity', 'name': 'baah' }, - ], + 'others': [], }, { 'query': 'W2, W3', diff --git a/tests/rustdoc-js/generics-match-ambiguity.rs b/tests/rustdoc-js/generics-match-ambiguity.rs index 79c493856ebc7..7aadbbd609caf 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(pub T, pub U); pub fn foo(a: Wrap, b: Wrap) {} pub fn bar(a: Wrap, b: Wrap) {} +#[doc(search_unbox)] pub struct W2(pub T); +#[doc(search_unbox)] pub struct W3(pub T, pub U); pub fn baaa(a: W3, b: W3) {} @@ -14,4 +19,3 @@ pub fn baae(a: W3, b: W2>) {} pub fn baaf(a: W3, b: W2>) {} pub fn baag(a: W2>, b: W2>) {} pub fn baah(a: W2>, b: W2>) {} -// diff --git a/tests/rustdoc-js/generics-nested.js b/tests/rustdoc-js/generics-nested.js index 294c194907489..b3184dde0d0c9 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', - 'others': [ - { 'path': 'generics_nested', 'name': 'bet' }, - ], + 'others': [], }, ]; diff --git a/tests/rustdoc-js/generics-unbox.js b/tests/rustdoc-js/generics-unbox.js index 9cdfc7ac8b6e5..6baf00c814bba 100644 --- a/tests/rustdoc-js/generics-unbox.js +++ b/tests/rustdoc-js/generics-unbox.js @@ -11,20 +11,17 @@ const EXPECTED = [ 'query': 'Inside -> Out3', 'others': [ { 'path': 'generics_unbox', 'name': 'beta' }, - { 'path': 'generics_unbox', 'name': 'gamma' }, ], }, { 'query': 'Inside -> Out4', 'others': [ - { 'path': 'generics_unbox', 'name': 'beta' }, { 'path': 'generics_unbox', 'name': 'gamma' }, ], }, { 'query': 'Inside -> Out3', 'others': [ - { 'path': 'generics_unbox', 'name': 'beta' }, { 'path': 'generics_unbox', 'name': 'gamma' }, ], }, @@ -32,7 +29,6 @@ const EXPECTED = [ 'query': 'Inside -> Out4', '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 bef34f891e95c..c2578575997b3 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: A, b: B, } +#[doc(search_unbox)] pub struct Out1 { a: [A; N], } +#[doc(search_unbox)] pub struct Out2 { a: [A; N], } +#[doc(search_unbox)] pub struct Out3 { a: A, b: B, } +#[doc(search_unbox)] pub struct Out4 { a: A, b: B, } +#[doc(search_unbox)] pub struct Inside(T); pub fn alpha(_: Inside) -> Out, Out2> { diff --git a/tests/rustdoc-js/generics.js b/tests/rustdoc-js/generics.js index b3ca0af3056a5..afab5cb4bafa7 100644 --- a/tests/rustdoc-js/generics.js +++ b/tests/rustdoc-js/generics.js @@ -30,21 +30,8 @@ const EXPECTED = [ 'others': [ { 'path': 'generics', 'name': 'P' }, ], - 'returned': [ - { 'path': 'generics', 'name': 'alef' }, - ], - 'in_args': [ - { 'path': 'generics', 'name': 'alpha' }, - ], - }, - { - 'query': 'P', - 'returned': [ - { 'path': 'generics', 'name': 'alef' }, - ], - 'in_args': [ - { 'path': 'generics', 'name': 'alpha' }, - ], + 'returned': [], + 'in_args': [], }, { 'query': '"ExtraCreditStructMulti"', diff --git a/tests/rustdoc-js/hof.js b/tests/rustdoc-js/hof.js index 5e6c9d83c7c7f..c1142f106688b 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 -> !) -> ()', 'others': [ {"path": "hof", "name": "fn_ptr"}, ], }, { - 'query': 'u8, (u32 -> !) -> ()', + 'query': 'u8, (second -> !) -> ()', 'others': [ {"path": "hof", "name": "fn_once"}, ], }, { - 'query': 'i8, (u32 -> !) -> ()', + 'query': 'i8, (third -> !) -> ()', '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) -> ! -> ()', 'others': [ {"path": "hof", "name": "fn_ptr"}, ], }, { - 'query': 'u8, fnonce(u32) -> ! -> ()', + 'query': 'u8, fnonce(second) -> ! -> ()', 'others': [ {"path": "hof", "name": "fn_once"}, ], }, { - 'query': 'u8, fn(u32) -> ! -> ()', + 'query': 'u8, fn(second) -> ! -> ()', // fnonce != fn 'others': [], }, { - 'query': 'i8, fnmut(u32) -> ! -> ()', + 'query': 'i8, fnmut(third) -> ! -> ()', 'others': [ {"path": "hof", "name": "fn_mut"}, ], }, { - 'query': 'i8, fn(u32) -> ! -> ()', + 'query': 'i8, fn(third) -> ! -> ()', // fnmut != fn 'others': [], }, @@ -152,7 +149,7 @@ const EXPECTED = [ ], }, { - 'query': 'fn(u32) -> ! -> ()', + 'query': 'fn() -> ! -> ()', 'others': [ // fn matches primitive:fn and trait:Fn {"path": "hof", "name": "fn_"}, @@ -160,14 +157,14 @@ const EXPECTED = [ ], }, { - 'query': 'trait:fn(u32) -> ! -> ()', + 'query': 'trait:fn() -> ! -> ()', 'others': [ // fn matches primitive:fn and trait:Fn {"path": "hof", "name": "fn_"}, ], }, { - 'query': 'primitive:fn(u32) -> ! -> ()', + 'query': 'primitive:fn() -> ! -> ()', 'others': [ // fn matches primitive:fn and trait:Fn {"path": "hof", "name": "fn_ptr"}, diff --git a/tests/rustdoc-js/looks-like-rustc-interner.js b/tests/rustdoc-js/looks-like-rustc-interner.js index a4806d2349929..f35029968876c 100644 --- a/tests/rustdoc-js/looks-like-rustc-interner.js +++ b/tests/rustdoc-js/looks-like-rustc-interner.js @@ -3,6 +3,8 @@ const EXPECTED = { 'query': 'canonicalvarinfo, intoiterator -> intoiterator', + 'others': [], + 'query': '[canonicalvarinfo], interner -> 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 44f784eb1f638..5f9eabc12f60f 100644 --- a/tests/rustdoc-js/nested-unboxed.js +++ b/tests/rustdoc-js/nested-unboxed.js @@ -33,9 +33,8 @@ const EXPECTED = [ }, { 'query': '-> Result', - 'others': [ - { 'path': 'nested_unboxed', 'name': 'something' }, - ], + // can't put nested generics out of order + 'others': [], }, { 'query': '-> Result, bool>', @@ -45,9 +44,7 @@ const EXPECTED = [ }, { 'query': '-> Result, bool>', - 'others': [ - { 'path': 'nested_unboxed', 'name': 'something' }, - ], + 'others': [], }, { 'query': '-> Result, u32, bool>', diff --git a/tests/rustdoc-js/nested-unboxed.rs b/tests/rustdoc-js/nested-unboxed.rs index 57f9592b79147..6c8b1bd6aa144 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); pub fn something() -> Result, bool> { diff --git a/tests/rustdoc-js/reference.js b/tests/rustdoc-js/reference.js index b4a1fb15d369a..378fc03475b87 100644 --- a/tests/rustdoc-js/reference.js +++ b/tests/rustdoc-js/reference.js @@ -79,9 +79,8 @@ const EXPECTED = [ }, { 'query': 'reference, reference -> ()', - 'others': [ - { 'path': 'reference::Ring', 'name': 'wear' }, - ], + // can't leave out the `mut`, because can't reorder like that + 'others': [], }, { 'query': 'reference, reference -> ()', @@ -102,9 +101,8 @@ const EXPECTED = [ }, { 'query': 'reference, reference -> ()', - 'others': [ - { 'path': 'reference', 'name': 'show' }, - ], + // can't leave out the mut + 'others': [], }, { 'query': 'reference, reference -> ()', @@ -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 d24a3da328c56..6a9b861cf948c 100644 --- a/tests/rustdoc-js/tuple-unit.js +++ b/tests/rustdoc-js/tuple-unit.js @@ -57,7 +57,7 @@ const EXPECTED = [ 'in_args': [], }, { - 'query': '(Q, ())', + 'query': '(Q, R<()>)', 'returned': [ { 'path': 'tuple_unit', 'name': 'nest' }, ], @@ -71,7 +71,7 @@ const EXPECTED = [ 'in_args': [], }, { - 'query': '(u32)', + 'query': 'R<(u32)>', 'returned': [ { 'path': 'tuple_unit', 'name': 'nest' }, ], diff --git a/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs b/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs index 58306a4cfc9c9..cd5c9a798d860 100644 --- a/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs +++ b/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs @@ -7,4 +7,8 @@ trait Mine {} #[doc(fake_variadic)] //~ ERROR: `#[doc(fake_variadic)]` is meant for internal use only impl Mine for (T,) {} + +#[doc(search_unbox)] //~ ERROR: `#[doc(search_unbox)]` is meant for internal use only +struct Wrap (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 bbb9edd58f096..e84e7db84ccdd 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:11:1 + | +LL | #[doc(search_unbox)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #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`.