From 488d5b0d8e3dd25321615ae702689348574cc77f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Sep 2024 11:01:51 -0700 Subject: [PATCH 1/7] rustdoc-search: add type param names to index --- src/librustdoc/html/render/mod.rs | 1 + src/librustdoc/html/render/search_index.rs | 45 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8446235fb1881..a17c7626148ab 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, output: Vec, where_clause: Vec>, + param_names: Vec, } impl IndexItemFunctionType { diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index d1939adc1a501..ee2fc1f44b345 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -646,9 +646,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::>() + && 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 +676,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", ¶m_names)?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; } @@ -758,7 +775,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 +788,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 +1302,7 @@ fn get_fn_inputs_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>, cache: &Cache, -) -> (Vec, Vec, Vec>) { +) -> (Vec, Vec, Vec, Vec>) { let decl = &func.decl; let mut rgen: FxIndexMap)> = Default::default(); @@ -1331,7 +1348,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::>(); - 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::>(); + 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(), + ) } From 2cc1c0c39a481be5a4498dbee4f89a479dabd9c9 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Sep 2024 11:04:04 -0700 Subject: [PATCH 2/7] rustdoc-search: simplify the checkTypes fast path This reduces code size while still matching the common case for plain, concrete types. --- src/librustdoc/html/static/js/search.js | 37 ++++++++----------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index eed64d024c046..995c2e041941c 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2727,33 +2727,18 @@ 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, null, unboxingDepth); } - return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); }; /** From 5c7e7dfe10acde559ad78a700dfefbfaf0ed8772 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Sep 2024 12:33:09 -0700 Subject: [PATCH 3/7] rustdoc-search: pass original names through AST --- src/librustdoc/html/static/js/search.js | 46 +++--- src/tools/rustdoc-js/tester.js | 1 - tests/rustdoc-gui/search-corrections.goml | 12 +- tests/rustdoc-js-std/parser-bindings.js | 52 +++--- tests/rustdoc-js-std/parser-errors.js | 159 +++++++------------ tests/rustdoc-js-std/parser-filter.js | 27 ++-- tests/rustdoc-js-std/parser-generics.js | 18 +-- tests/rustdoc-js-std/parser-hof.js | 86 ++++------ tests/rustdoc-js-std/parser-ident.js | 37 ++--- tests/rustdoc-js-std/parser-literal.js | 7 +- tests/rustdoc-js-std/parser-paths.js | 31 ++-- tests/rustdoc-js-std/parser-quote.js | 21 +-- tests/rustdoc-js-std/parser-reference.js | 55 +++---- tests/rustdoc-js-std/parser-returned.js | 33 ++-- tests/rustdoc-js-std/parser-separators.js | 24 +-- tests/rustdoc-js-std/parser-slice-array.js | 56 +++---- tests/rustdoc-js-std/parser-tuple.js | 65 +++----- tests/rustdoc-js-std/parser-weird-queries.js | 18 +-- tests/rustdoc-js/non-english-identifier.js | 34 ++-- 19 files changed, 295 insertions(+), 487 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 995c2e041941c..a4471f1d885a4 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -276,7 +276,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 +686,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 `

`, there is no name. if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) { @@ -726,7 +726,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; @@ -1786,8 +1789,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 +1911,7 @@ class DocSearch { genericsElems: 0, typeFilter: null, isInBinding: null, - userQuery: userQuery.toLowerCase(), + userQuery, }; let query = newParsedQuery(userQuery); @@ -2097,7 +2099,7 @@ class DocSearch { */ const sortResults = async(results, isType, 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 +2111,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; } @@ -3163,21 +3165,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; @@ -3289,7 +3295,7 @@ class DocSearch { 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 => { @@ -3709,11 +3715,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), diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index e162ba033cc55..63cda4111e6ad 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -84,7 +84,6 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position) if (fullPath.length === 0) { fieldsToCheck = [ "foundElems", - "original", "returned", "userQuery", "error", diff --git a/tests/rustdoc-gui/search-corrections.goml b/tests/rustdoc-gui/search-corrections.goml index b81b1f382a957..f80675730c41a 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/parser-bindings.js b/tests/rustdoc-js-std/parser-bindings.js index c4909c6242d75..bd379f139ffa2 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', 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', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', 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', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -89,16 +89,15 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -120,16 +119,15 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -160,52 +158,47 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [], foundElems: 0, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: "Cannot write `=` twice in a binding", }, { query: 'A', elems: [], foundElems: 0, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', 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=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=C>', + userQuery: 'A=C>', returned: [], - userQuery: 'a=c>', error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index 5ce35bf511d75..068298e72360a 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -3,450 +3,400 @@ const PARSED = [ query: '

', elems: [], foundElems: 0, - original: "

", + userQuery: "

", returned: [], - userQuery: "

", error: "Found generics without a path", }, { query: '->

', elems: [], foundElems: 0, - original: "->

", + userQuery: "->

", returned: [], - userQuery: "->

", 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" ', elems: [], foundElems: 0, - original: '"p" ', - returned: [], userQuery: '"p" ', + returned: [], error: "Cannot have more than one element if you use quotes", }, { query: '"p",', elems: [], foundElems: 0, - original: '"p",', - returned: [], userQuery: '"p",', + returned: [], error: "Found generics without a path", }, { 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: "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:", 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: '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 , y", - returned: [], userQuery: "p , y", + returned: [], error: null, }, { @@ -514,9 +463,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "p", - returned: [], userQuery: "p", + 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 a1dd0ea3b5a99..cda950461f7e0 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:foo', elems: [], foundElems: 0, - original: "macro:foo", - returned: [], userQuery: "macro: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', elems: [], foundElems: 1, - original: "-> fn:foo", + userQuery: "-> fn:foo", returned: [{ name: "foo", fullPath: ["foo"], @@ -126,14 +119,13 @@ const PARSED = [ ], typeFilter: 7, }], - userQuery: "-> fn:foo", error: null, }, { query: '-> fn:foo', elems: [], foundElems: 1, - original: "-> fn:foo", + userQuery: "-> fn:foo", returned: [{ name: "foo", fullPath: ["foo"], @@ -159,7 +151,6 @@ const PARSED = [ ], typeFilter: 7, }], - userQuery: "-> fn:foo", error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-generics.js b/tests/rustdoc-js-std/parser-generics.js index 726ee56c2c1ba..8b8d95bcb8883 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, E>', elems: [], foundElems: 0, - original: 'A, E>', + userQuery: 'A, E>', returned: [], - userQuery: 'a, 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"', - returned: [], userQuery: '"p"', + returned: [], error: null, }, { @@ -89,9 +86,8 @@ const PARSED = [ }, ], foundElems: 1, - original: 'p>', - returned: [], userQuery: 'p>', + returned: [], error: null, }, { @@ -130,9 +126,8 @@ const PARSED = [ }, ], foundElems: 1, - original: 'p, r>', - returned: [], userQuery: 'p, r>', + returned: [], error: null, }, { @@ -171,9 +166,8 @@ const PARSED = [ }, ], foundElems: 1, - original: 'p>', - returned: [], userQuery: 'p>', + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-hof.js b/tests/rustdoc-js-std/parser-hof.js index 0b99c45b7a922..ca76101541280 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

)", + userQuery: "(-> F

)", returned: [], - userQuery: "(-> f

)", 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

->)", + userQuery: "(F

->)", returned: [], - userQuery: "(f

->)", 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

", 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

", + userQuery: "Fn () -> F

", returned: [], - userQuery: "fn () -> f

", 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

)", 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

)", + userQuery: "Fn(F

)", returned: [], - userQuery: "fn(f

)", 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 cc79c58f1da3a..f65391b157188 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: "!", elems: [], foundElems: 0, - original: "!", + userQuery: "!", returned: [], - userQuery: "!", 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", + userQuery: "!::b", returned: [], - userQuery: "!::b", 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 87c06224dbf2d..63e07a246a13d 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

', 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

", + userQuery: "R

", returned: [], - userQuery: "r

", error: null, } ]; diff --git a/tests/rustdoc-js-std/parser-paths.js b/tests/rustdoc-js-std/parser-paths.js index 774e5d028cc2c..bb34e22e518a4 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,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,C', + userQuery: 'A::B,C', returned: [], - userQuery: 'a::b,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 731673cf4633c..b485047e385eb 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 6b1250146bef8..0fa07ae989528 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", - returned: [], userQuery: "&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: "¯o:u8", - returned: [], userQuery: "¯o:u8", + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-returned.js b/tests/rustdoc-js-std/parser-returned.js index 8f68209bb966f..30ce26a892059 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

", elems: [], foundElems: 1, - original: "-> F

", + userQuery: "-> F

", 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

", 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 7f95f61b006bb..cf271c80cdce2 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", - returned: [], userQuery: "a", + returned: [], error: null, }, { @@ -161,9 +155,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a", - returned: [], userQuery: "a", + returned: [], error: null, }, { @@ -187,9 +180,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a", - returned: [], userQuery: "a", + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-slice-array.js b/tests/rustdoc-js-std/parser-slice-array.js index 1de52af94e6b0..6579794553599 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]", - returned: [], userQuery: "[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', elems: [], foundElems: 0, - original: "[a", - returned: [], userQuery: "[a", + returned: [], error: "Unclosed `[`", }, { query: 'a]', elems: [], foundElems: 0, - original: "a]", - returned: [], userQuery: "a]", + 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 eb16289d3c055..6192506838708 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)", - returned: [], userQuery: "(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', elems: [], foundElems: 0, - original: "(a", - returned: [], userQuery: "(a", + returned: [], error: "Unclosed `(`", }, { query: 'a)', elems: [], foundElems: 0, - original: "a)", - returned: [], userQuery: "a)", + 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 499b82a346948..828b0a7d9f66a 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/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js index 1765a69152a8f..6a2c4cc637c96 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)", } ] From 5973005d93c182fe38c79449ec87e968dc23de62 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Sep 2024 12:36:05 -0700 Subject: [PATCH 4/7] rustdoc-search: use correct type annotations --- src/librustdoc/html/static/js/externs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index 3d3e0a8f8381c..fe66c8536b715 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, * pathWithoutLast: Array, * pathLast: string, * generics: Array, - * bindings: Map>, + * bindings: Map>, * }} */ let QueryElement; From 20a4b4fea1e5f3005973ae1391b039722d207119 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Sep 2024 14:31:44 -0700 Subject: [PATCH 5/7] rustdoc-search: show types signatures in results --- src/librustdoc/html/static/css/rustdoc.css | 21 +- src/librustdoc/html/static/js/externs.js | 3 + src/librustdoc/html/static/js/search.js | 688 ++++++++++++++++-- src/tools/rustdoc-js/tester.js | 54 +- .../rustdoc-gui/search-about-this-result.goml | 42 ++ .../rustdoc-js-std/option-type-signatures.js | 174 ++++- tests/rustdoc-js/assoc-type-unbound.js | 39 + tests/rustdoc-js/assoc-type-unbound.rs | 4 + tests/rustdoc-js/assoc-type.js | 48 +- tests/rustdoc-js/generics-trait.js | 48 +- 10 files changed, 995 insertions(+), 126 deletions(-) create mode 100644 tests/rustdoc-gui/search-about-this-result.goml create mode 100644 tests/rustdoc-js/assoc-type-unbound.js create mode 100644 tests/rustdoc-js/assoc-type-unbound.rs diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 1042d254749e3..66a8a19892886 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 fe66c8536b715..c4faca1c0c3bc 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -92,6 +92,9 @@ let Results; * parent: (Object|undefined), * path: string, * ty: number, + * type: FunctionSearchType?, + * displayType: Promise>>|null, + * displayTypeMappedNames: Promise]>>|null, * }} */ let ResultObject; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index a4471f1d885a4..0458b81d35257 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. @@ -1117,6 +1128,13 @@ class DocSearch { * @type {Map} */ this.typeNameIdMap = new Map(); + /** + * Map from type ID to associated type name. Used for display, + * not for search. + * + * @type {Map} + */ + this.assocTypeIdNameMap = new Map(); this.ALIASES = new Map(); this.rootPath = rootPath; this.searchState = searchState; @@ -1161,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. @@ -1237,9 +1263,9 @@ class DocSearch { * * @return {Array} */ - 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; } @@ -1248,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; @@ -1261,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) { @@ -1277,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 { @@ -1294,6 +1321,7 @@ 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, @@ -1304,6 +1332,7 @@ class DocSearch { // `0` is used as a sentinel because it's fewer bytes than `null` result = { id: null, + name: "", ty: null, path: null, exactPath: null, @@ -1312,8 +1341,13 @@ class DocSearch { }; } 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, @@ -1355,7 +1389,7 @@ class DocSearch { } if (cr.ty === result.ty && cr.path === result.path && cr.bindings === result.bindings && cr.generics === result.generics - && cr.ty === result.ty + && cr.ty === result.ty && cr.name === result.name ) { return cr; } @@ -1466,11 +1500,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; @@ -1480,11 +1515,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, ); } @@ -1493,12 +1533,14 @@ class DocSearch { output = [ this.buildItemSearchType( functionSearchType[OUTPUT_DATA], + paths, lowercasePaths, ), ]; } else { output = this.buildItemSearchTypeAll( functionSearchType[OUTPUT_DATA], + paths, lowercasePaths, ); } @@ -1509,8 +1551,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, @@ -1554,6 +1600,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 @@ -1568,6 +1621,7 @@ class DocSearch { desc: crateCorpus.doc, parent: undefined, type: null, + paramNames: lastParamNames, id, word: crate, normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), @@ -1604,6 +1658,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 = []; @@ -1611,7 +1669,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 @@ -1662,6 +1720,9 @@ 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) { @@ -1694,6 +1755,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, ""), @@ -1704,6 +1766,7 @@ class DocSearch { id += 1; searchIndex.push(row); lastPath = row.path; + lastParamNames = row.paramNames; if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descIndex += 1; } @@ -2048,18 +2111,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; @@ -2078,6 +2144,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) { @@ -2088,6 +2159,327 @@ class DocSearch { return out; }; + /** + * 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, + * "mappedNames": Map, + * "whereClause": Map>, + * > + */ + 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` and the function + * is actually written as `T -> Option`, 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) { + 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], + ); + } + 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. @@ -2097,7 +2489,7 @@ 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 normalizedUserQuery = parsedQuery.userQuery.toLowerCase(); const result_list = []; @@ -2207,17 +2599,17 @@ 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} fnTypesIn - The objects to check. @@ -2230,7 +2622,7 @@ class DocSearch { * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {boolean} - Returns true if a match, false otherwise. + * @return {[FunctionType]|null} - Returns highlighed results if a match, null otherwise. */ function unifyFunctionTypes( fnTypesIn, @@ -2241,17 +2633,17 @@ class DocSearch { unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { - return false; + return null; } /** * @type Map|null */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { - return !solutionCb || solutionCb(mgens); + return (!solutionCb || solutionCb(mgens)) ? fnTypesIn : null; } if (!fnTypesIn || fnTypesIn.length === 0) { - return false; + return null; } const ql = queryElems.length; const fl = fnTypesIn.length; @@ -2260,7 +2652,7 @@ 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; } @@ -2272,14 +2664,33 @@ class DocSearch { const mgensScratch = new Map(mgens); mgensScratch.set(fnType.id, queryElem.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)) { // unifyFunctionTypeIsMatchCandidate already checks that ids match - return true; + const highlighted = [...fnTypesIn]; + highlighted[i] = Object.assign({ + highlighted: true, + }, fnType, { + generics: unifyFunctionTypes( + 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, @@ -2296,25 +2707,42 @@ class DocSearch { } const mgensScratch = new Map(mgens); mgensScratch.set(fnType.id, 0); - if (unifyFunctionTypes( + const highlightedGenerics = unifyFunctionTypes( whereClause[(-fnType.id) - 1], queryElems, whereClause, mgensScratch, 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; @@ -2371,6 +2799,8 @@ class DocSearch { if (!queryElemsTmp) { queryElemsTmp = queryElems.slice(0, qlast); } + let unifiedGenerics = []; + let unifiedGenericsMgens = null; const passesUnification = unifyFunctionTypes( fnTypes, queryElemsTmp, @@ -2393,7 +2823,7 @@ class DocSearch { } const simplifiedGenerics = solution.simplifiedGenerics; for (const simplifiedMgens of solution.mgens) { - const passesUnification = unifyFunctionTypes( + unifiedGenerics = unifyFunctionTypes( simplifiedGenerics, queryElem.generics, whereClause, @@ -2401,7 +2831,8 @@ class DocSearch { solutionCb, unboxingDepth, ); - if (passesUnification) { + if (unifiedGenerics) { + unifiedGenericsMgens = simplifiedMgens; return true; } } @@ -2410,7 +2841,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]; @@ -2445,7 +2892,7 @@ 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, @@ -2453,10 +2900,24 @@ class DocSearch { 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; } /** * Check if this function is a match candidate. @@ -2627,7 +3088,7 @@ class DocSearch { } }); if (simplifiedGenerics.length > 0) { - simplifiedGenerics = [...simplifiedGenerics, ...binds]; + simplifiedGenerics = [...binds, ...simplifiedGenerics]; } else { simplifiedGenerics = binds; } @@ -3285,10 +3746,11 @@ 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, @@ -3315,6 +3777,7 @@ class DocSearch { } } + // ==================== Core search logic end ==================== let rawSearchIndex; @@ -3446,15 +3909,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]; @@ -3464,7 +3930,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( @@ -3487,10 +3953,73 @@ ${item.displayPath}${name}\ 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 [name, qname] 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 :(
" + @@ -3507,7 +4036,7 @@ ${item.displayPath}${name}\ "href=\"https://docs.rs\">Docs.rs
for documentation of crates released on" + " crates.io."; } - return [output, array.length]; + return output; } function makeTabHeader(tabNb, text, nbElems) { @@ -3564,24 +4093,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; } } @@ -3610,14 +4133,14 @@ async function showResults(results, go_to_first, filterCrates) { }); output += `

Query parser error: "${error.join("")}".

`; output += "
" + - makeTabHeader(0, "In Names", ret_others[1]) + + makeTabHeader(0, "In Names", results.others.length) + "
"; currentTab = 0; } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { output += "
" + - 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) + "
"; } else { const signatureTabTitle = @@ -3625,7 +4148,7 @@ async function showResults(results, go_to_first, filterCrates) { results.query.returned.length === 0 ? "In Function Parameters" : "In Function Signatures"; output += "
" + - makeTabHeader(0, signatureTabTitle, ret_others[1]) + + makeTabHeader(0, signatureTabTitle, results.others.length) + "
"; currentTab = 0; } @@ -3647,11 +4170,17 @@ async function showResults(results, go_to_first, filterCrates) { `Consider searching for "${targ}" instead.`; } + 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) { @@ -3933,4 +4462,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 63cda4111e6ad..7aa5e102e6d2a 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(" ").join(" "); + return (index % 2 === 1) ? ("`" + value + "`") : value; + }).join(""); +} + function loadContent(content) { const Module = module.constructor; const m = new Module(); @@ -180,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(" ").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); @@ -436,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 0000000000000..62780d01ed7e8 --- /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->bool)->(Extend,Extend)" + +// 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()='(']" +assert: "//div[@class='type-signature']/strong[text()=')']" + +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") + +// Try a simple result that *won't* give an info box. +go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=F->lib2::WhereWhitespace" + +// 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") +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-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js index e154fa707ab3d..1690d5dc8b5bb 100644 --- a/tests/rustdoc-js-std/option-type-signatures.js +++ b/tests/rustdoc-js-std/option-type-signatures.js @@ -6,79 +6,217 @@ const EXPECTED = [ { 'query': 'option, fnonce -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'map' }, + { + 'path': 'std::option::Option', + 'name': 'map', + 'displayType': '`Option`, F -> `Option`', + 'displayWhereClause': "F: `FnOnce` (T) -> U", + }, + ], + }, + { + 'query': 'option, fnonce -> option', + 'others': [ + { + 'path': 'std::option::Option', + 'name': 'map', + 'displayType': '`Option`<`T`>, F -> `Option`', + '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`', + 'displayWhereClause': "T: `Default`", + }, + { + 'path': 'std::option::Option', + 'name': 'get_or_insert_default', + 'displayType': '&mut `Option` -> &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`]`', + }, + { + 'path': 'std::option::Option', + 'name': 'as_mut_slice', + 'displayType': '&mut `Option` -> &mut `[`T`]`', + }, ], }, { 'query': 'option, option -> option', '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, option -> option', '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, option -> option', '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, option -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'zip' }, + { + 'path': 'std::option::Option', + 'name': 'zip', + 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, `U`)>', + }, ], }, { 'query': 'option, e -> result', '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, e> -> option>', 'others': [ - { 'path': 'std::result::Result', 'name': 'transpose' }, + { + 'path': 'std::result::Result', + 'name': 'transpose', + 'displayType': '`Result`<`Option`<`T`>, `E`> -> `Option`<`Result`<`T`, `E`>>', + }, ], }, { 'query': 'option, option -> bool', 'others': [ - { 'path': 'std::option::Option', 'name': 'eq' }, + { + 'path': 'std::option::Option', + 'name': 'eq', + 'displayType': '&`Option`<`T`>, &`Option`<`T`> -> `bool`', + }, ], }, { 'query': 'option> -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'flatten' }, + { + 'path': 'std::option::Option', + 'name': 'flatten', + 'displayType': '`Option`<`Option`<`T`>> -> `Option`<`T`>', + }, ], }, { 'query': 'option', 'returned': [ - { 'path': 'std::result::Result', 'name': 'ok' }, + { + 'path': 'std::result::Result', + 'name': 'ok', + 'displayType': 'Result -> `Option`<`T`>', + }, + ], + }, + { + 'query': 'option, (fnonce () -> u) -> option', + 'others': [ + { + 'path': 'std::option::Option', + 'name': 'map', + 'displayType': '`Option`<`T`>, F -> `Option`', + '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`, + 'displayWhereClause': "F: `FnOnce` (T) -> Option<`U`>", + }, + { + 'path': 'std::option::Option', + 'name': 'zip_with', + 'displayType': 'Option, `Option`<`U`>, F -> `Option`', + 'displayMappedNames': `U = t, R = u`, + '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`", + }, + ], + }, + { + 'query': 'option, (fnonce () -> option) -> option', + 'others': [ + { + 'path': 'std::option::Option', + 'name': 'and_then', + 'displayType': '`Option`<`T`>, F -> `Option`', + 'displayMappedNames': `T = t, U = u`, + 'displayWhereClause': "F: `FnOnce` (T) -> `Option`<`U`>", + }, ], }, ]; diff --git a/tests/rustdoc-js/assoc-type-unbound.js b/tests/rustdoc-js/assoc-type-unbound.js new file mode 100644 index 0000000000000..611b8bd1501c7 --- /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 -> Option', + 'correction': null, + 'others': [ + { + 'path': 'assoc_type_unbound::MyIter', + 'name': 'next', + 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>', + 'displayMappedNames': 'MyIter::Item = T', + 'displayWhereClause': '', + }, + ], + }, + { + 'query': 'MyIter -> Option', + 'correction': null, + 'others': [ + { + 'path': 'assoc_type_unbound::MyIter', + 'name': 'next', + 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>', + 'displayMappedNames': 'MyIter::Item = T', + 'displayWhereClause': '', + }, + ], + }, + { + 'query': 'MyIter -> Option', + '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 0000000000000..713b77b500723 --- /dev/null +++ b/tests/rustdoc-js/assoc-type-unbound.rs @@ -0,0 +1,4 @@ +pub trait MyIter { + type Item; + fn next(&mut self) -> Option; +} diff --git a/tests/rustdoc-js/assoc-type.js b/tests/rustdoc-js/assoc-type.js index eec4e7a8258fb..0edf10e794ed7 100644 --- a/tests/rustdoc-js/assoc-type.js +++ b/tests/rustdoc-js/assoc-type.js @@ -7,16 +7,40 @@ const EXPECTED = [ 'query': 'iterator -> 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`', + }, ], }, { 'query': 'iterator', '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`', + }, ], }, { @@ -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', + }, ], }, // if I write an explicit binding, only it shows up diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js index a71393b5e0502..8da9c67050e62 100644 --- a/tests/rustdoc-js/generics-trait.js +++ b/tests/rustdoc-js/generics-trait.js @@ -5,10 +5,22 @@ const EXPECTED = [ 'query': 'Result', '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`', + }, ], }, ]; From 12dc24f46007f82b93ed85614347a42d47580afa Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Sep 2024 18:18:01 -0700 Subject: [PATCH 6/7] rustdoc-search: simplify rules for generics and type params This commit is a response to feedback on the displayed type signatures results, by making generics act stricter. 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 | 27 +- compiler/rustc_passes/src/errors.rs | 7 + compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/boxed.rs | 1 + library/alloc/src/rc.rs | 1 + library/alloc/src/sync.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 | 352 ++++++++++++++---- .../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 | 12 + tests/rustdoc-js/assoc-type-backtrack.js | 48 ++- 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 | 16 +- tests/rustdoc-js/hof.js | 25 +- tests/rustdoc-js/looks-like-rustc-interner.js | 18 +- 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 | 3 + .../feature-gate-rustdoc_internals.stderr | 12 +- 40 files changed, 630 insertions(+), 217 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 d646150a620cd..396aac3451560 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 e5e70ba203384..3536b3fc4cd60 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 0a2926c040447..4cf523b05568a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -16,8 +16,8 @@ use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, B use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, self, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, Item, ItemKind, - MethodKind, Safety, Target, TraitItem, + self as hir, self, AssocItemKind, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, + Item, ItemKind, MethodKind, Safety, Target, TraitItem, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -940,6 +940,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_doc_search_unbox(&self, meta: &MetaItemInner, hir_id: HirId) { + let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else { + self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() }); + return; + }; + match item.kind { + ItemKind::Enum(_, generics) | ItemKind::Struct(_, generics) + if generics.params.len() != 0 => {} + ItemKind::Trait(_, _, generics, _, items) + if generics.params.len() != 0 + || items.iter().any(|item| matches!(item.kind, AssocItemKind::Type)) => {} + _ => { + self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() }); + } + } + } + /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. /// /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or @@ -1152,6 +1169,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + sym::search_unbox => { + if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { + self.check_doc_search_unbox(meta, hir_id); + } + } + sym::test => { if self.check_attr_crate_level(attr, meta, hir_id) { self.check_test_attr(meta, hir_id); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 8bd767c1243d9..494be13b86a45 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -244,6 +244,13 @@ pub(crate) struct DocKeywordOnlyImpl { pub span: Span, } +#[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] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 890c4fdafef89..b2e8adb060436 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1762,6 +1762,7 @@ symbols! { saturating_add, saturating_div, saturating_sub, + search_unbox, select_unpredictable, self_in_typedefs, self_struct_ctor, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index e4956c7c53c8d..c5024a05ed631 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 fc8646e96d948..06addb4edb2b4 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 98a2fe242570f..e6a2cf009ce7a 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 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 2aa4f1723680f..461879386225a 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 330d1eb14edb0..b450123c5aa90 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 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 ee2fc1f44b345..f91fdfa1fb5f5 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, 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. @@ -132,10 +132,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()), @@ -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, 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, @@ -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, 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 { @@ -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, 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`. @@ -450,6 +481,7 @@ pub(crate) fn build_index<'tcx>( name: Symbol, path: Option, exact_path: Option, + 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::)?; + } + if self.exact_path.is_none() { + seq.serialize_element(&None::)?; + } + seq.serialize_element(&1)?; + } seq.end() } } @@ -489,9 +530,15 @@ pub(crate) fn build_index<'tcx>( mod_paths.insert(&item.path, index); } let mut paths = Vec::with_capacity(self.paths.len()); - for (ty, path, exact) in &self.paths { + for &(ty, ref path, ref exact, search_unbox) in &self.paths { if path.len() < 2 { - paths.push(Paths { ty: *ty, name: path[0], path: None, exact_path: None }); + paths.push(Paths { + ty, + name: path[0], + path: None, + exact_path: None, + search_unbox, + }); continue; } let full_path = join_with_double_colon(&path[..path.len() - 1]); @@ -517,10 +564,11 @@ pub(crate) fn build_index<'tcx>( }); if let Some(index) = mod_paths.get(&full_path) { paths.push(Paths { - ty: *ty, + ty, name: *path.last().unwrap(), path: Some(*index), exact_path, + search_unbox, }); continue; } @@ -532,10 +580,11 @@ pub(crate) fn build_index<'tcx>( match extra_paths.entry(full_path.clone()) { Entry::Occupied(entry) => { paths.push(Paths { - ty: *ty, + ty, name: *path.last().unwrap(), path: Some(*entry.get()), exact_path, + search_unbox, }); } Entry::Vacant(entry) => { @@ -544,10 +593,11 @@ pub(crate) fn build_index<'tcx>( revert_extra_paths.insert(index, full_path); } paths.push(Paths { - ty: *ty, + ty, name: *path.last().unwrap(), path: Some(index), exact_path, + search_unbox, }); } } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 0458b81d35257..d269c9322a317 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1327,6 +1327,7 @@ class DocSearch { exactPath: null, generics, bindings, + unboxFlag: true, }; } else if (pathIndex === 0) { // `0` is used as a sentinel because it's fewer bytes than `null` @@ -1338,6 +1339,7 @@ class DocSearch { exactPath: null, generics, bindings, + unboxFlag: true, }; } else { const item = lowercasePaths[pathIndex - 1]; @@ -1353,6 +1355,7 @@ class DocSearch { exactPath: item.exactPath, generics, bindings, + unboxFlag: item.unboxFlag, }; } const cr = this.TYPES_POOL.get(result.id); @@ -1390,6 +1393,7 @@ class DocSearch { if (cr.ty === result.ty && cr.path === result.path && cr.bindings === result.bindings && cr.generics === result.generics && cr.ty === result.ty && cr.name === result.name + && cr.unboxFlag === result.unboxFlag ) { return cr; } @@ -1681,14 +1685,17 @@ class DocSearch { const ty = elem[0]; const name = elem[1]; let path = null; - if (elem.length > 2) { + if (elem.length > 2 && elem[2] !== null) { path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; lastPath = path; } - const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path; + const exactPath = elem.length > 3 && elem[3] !== null ? + itemPaths.get(elem[3]) : + path; + const unboxFlag = elem.length > 4 && !!elem[4]; - lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath }); - paths[i] = { ty, name, path, exactPath }; + lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag }); + paths[i] = { ty, name, path, exactPath, unboxFlag }; } // convert `item*` into an object form, and construct word indices. @@ -2376,17 +2383,20 @@ class DocSearch { */ const writeFn = (fnType, result) => { if (fnType.id < 0) { - const queryId = mgens && mgens.has(fnType.id) ? mgens.get(fnType.id) : null; if (fnParamNames[-1 - fnType.id] === "") { for (const nested of fnType.generics) { writeFn(nested, result); } return; - } else if (queryId < 0) { - mappedNames.set( - fnParamNames[-1 - fnType.id], - queryParamNames[-1 - queryId], - ); + } else if (mgens) { + for (const [queryId, fnId] of mgens) { + if (fnId === fnType.id) { + mappedNames.set( + queryParamNames[-1 - queryId], + fnParamNames[-1 - fnType.id], + ); + } + } } pushText({ name: fnParamNames[-1 - fnType.id], @@ -2436,15 +2446,15 @@ class DocSearch { } pushText({ name, highlighted: false }, result); pushText({ - name: values.length > 1 ? "=(" : "=", + name: values.length !== 1 ? "=(" : "=", highlighted: false, }, result); onEachBtwn( - values, + values || [], value => writeFn(value, result), () => pushText({ name: " + ", highlighted: false }, result), ); - if (values.length > 1) { + if (values.length !== 1) { pushText({ name: ")", highlighted: false }, result); } }, @@ -2616,8 +2626,8 @@ class DocSearch { * @param {Array} 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. + * - Map query generics to function generics (never modified). + * @param {Map -> bool} solutionCb - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> @@ -2640,7 +2650,7 @@ class DocSearch { */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { - return (!solutionCb || solutionCb(mgens)) ? fnTypesIn : null; + return solutionCb(mgens) ? fnTypesIn : null; } if (!fnTypesIn || fnTypesIn.length === 0) { return null; @@ -2657,12 +2667,12 @@ class DocSearch { continue; } if (fnType.id < 0 && queryElem.id < 0) { - if (mgens && mgens.has(fnType.id) && - mgens.get(fnType.id) !== queryElem.id) { + if (mgens && mgens.has(queryElem.id) && + mgens.get(queryElem.id) !== fnType.id) { continue; } const mgensScratch = new Map(mgens); - mgensScratch.set(fnType.id, queryElem.id); + mgensScratch.set(queryElem.id, fnType.id); if (!solutionCb || solutionCb(mgensScratch)) { const highlighted = [...fnTypesIn]; highlighted[i] = Object.assign({ @@ -2672,13 +2682,13 @@ class DocSearch { }); return highlighted; } - } else if (!solutionCb || solutionCb(mgens ? new Map(mgens) : null)) { + } else if (solutionCb(mgens ? new Map(mgens) : null)) { // unifyFunctionTypeIsMatchCandidate already checks that ids match const highlighted = [...fnTypesIn]; highlighted[i] = Object.assign({ highlighted: true, }, fnType, { - generics: unifyFunctionTypes( + generics: unifyGenericTypes( fnType.generics, queryElem.generics, whereClause, @@ -2701,17 +2711,11 @@ class DocSearch { continue; } if (fnType.id < 0) { - if (mgens && mgens.has(fnType.id) && - mgens.get(fnType.id) !== 0) { - continue; - } - const mgensScratch = new Map(mgens); - mgensScratch.set(fnType.id, 0); const highlightedGenerics = unifyFunctionTypes( whereClause[(-fnType.id) - 1], queryElems, whereClause, - mgensScratch, + mgens, solutionCb, unboxingDepth + 1, ); @@ -2782,11 +2786,11 @@ class DocSearch { let mgensScratch; if (fnType.id < 0) { mgensScratch = new Map(mgens); - if (mgensScratch.has(fnType.id) - && mgensScratch.get(fnType.id) !== queryElem.id) { + if (mgensScratch.has(queryElem.id) + && mgensScratch.get(queryElem.id) !== fnType.id) { continue; } - mgensScratch.set(fnType.id, queryElem.id); + mgensScratch.set(queryElem.id, fnType.id); } else { mgensScratch = mgens; } @@ -2809,7 +2813,7 @@ class DocSearch { mgensScratch => { if (fnType.generics.length === 0 && queryElem.generics.length === 0 && fnType.bindings.size === 0 && queryElem.bindings.size === 0) { - return !solutionCb || solutionCb(mgensScratch); + return solutionCb(mgensScratch); } const solution = unifyFunctionTypeCheckBindings( fnType, @@ -2823,7 +2827,7 @@ class DocSearch { } const simplifiedGenerics = solution.simplifiedGenerics; for (const simplifiedMgens of solution.mgens) { - unifiedGenerics = unifyFunctionTypes( + unifiedGenerics = unifyGenericTypes( simplifiedGenerics, queryElem.generics, whereClause, @@ -2831,7 +2835,7 @@ class DocSearch { solutionCb, unboxingDepth, ); - if (unifiedGenerics) { + if (unifiedGenerics !== null) { unifiedGenericsMgens = simplifiedMgens; return true; } @@ -2875,16 +2879,6 @@ class DocSearch { )) { continue; } - let mgensScratch; - if (fnType.id < 0) { - mgensScratch = new Map(mgens); - if (mgensScratch.has(fnType.id) && mgensScratch.get(fnType.id) !== 0) { - continue; - } - mgensScratch.set(fnType.id, 0); - } else { - mgensScratch = mgens; - } const generics = fnType.id < 0 ? whereClause[(-fnType.id) - 1] : fnType.generics; @@ -2895,7 +2889,7 @@ class DocSearch { fnTypes.toSpliced(i, 1, ...bindings, ...generics), queryElems, whereClause, - mgensScratch, + mgens, solutionCb, unboxingDepth + 1, ); @@ -2919,6 +2913,199 @@ class DocSearch { } return null; } + /** + * This function compares two lists of generics. + * + * This function behaves very similarly to `unifyFunctionTypes`, except that it + * doesn't skip or reorder anything. This is intended to match the behavior of + * the ordinary Rust type system, so that `Vec` 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 {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(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. * @@ -2930,7 +3117,7 @@ class DocSearch { * * @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} */ const unifyFunctionTypeIsMatchCandidate = (fnType, queryElem, mgensIn) => { @@ -2940,22 +3127,13 @@ class DocSearch { } // fnType.id < 0 means generic // queryElem.id < 0 does too - // mgensIn[fnType.id] = queryElem.id - // or, if mgensIn[fnType.id] = 0, then we've matched this generic with a bare trait - // and should make that same decision everywhere it appears + // mgensIn[queryElem.id] = fnType.id if (fnType.id < 0 && queryElem.id < 0) { - if (mgensIn) { - if (mgensIn.has(fnType.id) && mgensIn.get(fnType.id) !== queryElem.id) { - return false; - } - for (const [fid, qid] of mgensIn.entries()) { - if (fnType.id !== fid && queryElem.id === qid) { - return false; - } - if (fnType.id === fid && queryElem.id !== qid) { - return false; - } - } + if ( + mgensIn && mgensIn.has(queryElem.id) && + mgensIn.get(queryElem.id) !== fnType.id + ) { + return false; } return true; } else { @@ -3032,7 +3210,7 @@ class DocSearch { * @param {FunctionType} fnType * @param {QueryElement} queryElem * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map} 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]}} @@ -3100,7 +3278,7 @@ class DocSearch { * @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} */ @@ -3114,19 +3292,10 @@ class DocSearch { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (fnType.id < 0 && queryElem.id >= 0) { + if (fnType.id < 0) { if (!whereClause) { return false; } - // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic - // mgens[fnType.id] === null indicates that we haven't decided yet - if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { - return false; - } - // Where clauses can represent cyclical data. - // `null` prevents it from trying to unbox in an infinite loop - const mgensTmp = new Map(mgens); - mgensTmp.set(fnType.id, null); // This is only a potential unbox if the search query appears in the where clause // for example, searching `Read -> usize` should find // `fn read_all(R) -> Result` @@ -3135,10 +3304,11 @@ class DocSearch { whereClause[(-fnType.id) - 1], queryElem, whereClause, - mgensTmp, + mgens, unboxingDepth, ); - } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { + } else if (fnType.unboxFlag && + (fnType.generics.length > 0 || fnType.bindings.size > 0)) { const simplifiedGenerics = [ ...fnType.generics, ...Array.from(fnType.bindings.values()).flat(), @@ -3182,7 +3352,7 @@ class DocSearch { * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. + * @param {Map|null} mgens - Map query generics to function generics. * * @return {boolean} - Returns true if the type matches, false otherwise. */ @@ -3200,8 +3370,32 @@ class DocSearch { ) { return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty); } else { - return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); + return unifyFunctionTypes( + [row], + [elem], + whereClause, + mgens, + () => true, + unboxingDepth, + ); + } + }; + + /** + * Check a query solution for conflicting generics. + */ + const checkTypeMgensForConflict = mgens => { + if (!mgens) { + return true; } + const fnTypes = new Set(); + for (const [_qid, fid] of mgens) { + if (fnTypes.has(fid)) { + return false; + } + fnTypes.add(fid); + } + return true; }; /** @@ -3523,7 +3717,7 @@ class DocSearch { parsedQuery.returned, row.type.where_clause, mgens, - null, + checkTypeMgensForConflict, 0, // unboxing depth ); }, @@ -3973,7 +4167,7 @@ ${item.displayPath}${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; 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 18cf9d6efd0fd..3c2ff30a83330 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 -> 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 493e1a9910d18..ccf5c063c8c50 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,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, 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 must match up { 'query': 'myintofuture>, myintofuture> -> myfuture', 'correction': null, 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..a6d20538efee5 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"', 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..d6d2764c3aede 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 -> 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..57d6b59128703 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 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..f3c00a2156bf9 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 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`. From 9900ea48b566656fb12b5fcbd0a1b20aaa96e5ca Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 31 Oct 2024 13:09:37 -0700 Subject: [PATCH 7/7] Adjust ranking so that duplicates count against rank --- src/librustdoc/html/static/js/search.js | 25 +++++++++----------- tests/rustdoc-js-std/simd-type-signatures.js | 20 ++++++++-------- tests/rustdoc-js-std/transmute-fail.js | 13 ++++++++++ tests/rustdoc-js-std/transmute.js | 12 ++++++++++ tests/rustdoc-js/impl-trait.js | 6 ++--- tests/rustdoc-js/type-parameters.js | 6 ++--- 6 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 tests/rustdoc-js-std/transmute-fail.js create mode 100644 tests/rustdoc-js-std/transmute.js diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index d269c9322a317..4e1bbbbf59d89 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1412,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 @@ -1420,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} 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 @@ -1468,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, @@ -1482,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; } /** @@ -1734,16 +1733,15 @@ class DocSearch { 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); } } } @@ -3885,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) { diff --git a/tests/rustdoc-js-std/simd-type-signatures.js b/tests/rustdoc-js-std/simd-type-signatures.js index c07f15dcbe8b1..4fc14e65ac424 100644 --- a/tests/rustdoc-js-std/simd-type-signatures.js +++ b/tests/rustdoc-js-std/simd-type-signatures.js @@ -20,11 +20,6 @@ const EXPECTED = [ 'name': 'simd_min', 'href': '../std/simd/prelude/struct.Simd.html#impl-SimdOrd-for-Simd%3Ci16,+N%3E/method.simd_min' }, - { - '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', @@ -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' + }, ], }, { @@ -50,11 +50,6 @@ const EXPECTED = [ 'name': 'simd_min', 'href': '../std/simd/prelude/struct.Simd.html#impl-SimdOrd-for-Simd%3Ci8,+N%3E/method.simd_min' }, - { - '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', @@ -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 0000000000000..c4dddf3cf3cee --- /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 0000000000000..0e52e21e0dead --- /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/impl-trait.js b/tests/rustdoc-js/impl-trait.js index 8bb3f2d3e99a5..3d7d0ca5bcda9 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/type-parameters.js b/tests/rustdoc-js/type-parameters.js index e045409e507e5..fa2b8d2ebfdb6 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' }, ],