diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index e94c6beabea39..91a58fab86eff 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -6,8 +6,10 @@ declare global { /** Map from crate name to directory structure, for source view */ declare var srcIndex: Map; - /** Defined and documented in `main.js` */ + /** Defined and documented in `storage.js` */ declare function nonnull(x: T|null, msg: string|undefined); + /** Defined and documented in `storage.js` */ + declare function nonundef(x: T|undefined, msg: string|undefined); interface Window { /** Make the current theme easy to find */ currentTheme: HTMLLinkElement|null; @@ -255,12 +257,20 @@ declare namespace rustdoc { ty: number, type?: FunctionSearchType, paramNames?: string[], - displayType: Promise>>|null, - displayTypeMappedNames: Promise]>>|null, + displayTypeSignature: Promise | null, item: Row, dontValidate?: boolean, } + /** + * output of `formatDisplayTypeSignature` + */ + interface DisplayTypeSignature { + type: Array, + mappedNames: Map, + whereClause: Map>, + } + /** * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. * The JavaScript deserializes this into FunctionSearchType. diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index c275127997ab1..dce5fddb3177e 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -33,6 +33,20 @@ function onEachBtwn(arr, func, funcBtwn) { } } +/** + * Convert any `undefined` to `null`. + * + * @template T + * @param {T|undefined} x + * @returns {T|null} + */ +function undef2null(x) { + if (x !== undefined) { + return x; + } + return null; +} + // ==================== Core search logic begin ==================== // This mapping table should match the discriminants of // `rustdoc::formats::item_type::ItemType` type in Rust. @@ -338,7 +352,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); - // typeFilterElem is not null. If it was, the elems.length check would have fired. + // typeFilterElem is not undefined. If it was, the elems.length check would have fired. // @ts-expect-error parserState.typeFilter = typeFilterElem.normalizedPathLast; parserState.pos += 1; @@ -1311,10 +1325,9 @@ class NameTrie { let sste; if (substart >= 2) { const tail = name.substring(substart - 2, substart + 1); - if (tailTable.has(tail)) { - // it's not undefined - // @ts-expect-error - sste = tailTable.get(tail); + const entry = tailTable.get(tail); + if (entry !== undefined) { + sste = entry; } else { sste = []; tailTable.set(tail, sste); @@ -1338,10 +1351,9 @@ class NameTrie { new Lev1TParametricDescription(name.length); this.searchLev(name, 0, levParams, results); const tail = name.substring(0, 3); - if (tailTable.has(tail)) { - // it's not undefined - // @ts-expect-error - for (const entry of tailTable.get(tail)) { + const list = tailTable.get(tail); + if (list !== undefined) { + for (const entry of list) { entry.searchSubstringPrefix(name, 3, results); } } @@ -1600,10 +1612,8 @@ class DocSearch { return null; } - if (this.typeNameIdMap.has(name)) { - /** @type {{id: number, assocOnly: boolean}} */ - // @ts-expect-error - const obj = this.typeNameIdMap.get(name); + const obj = this.typeNameIdMap.get(name); + if (obj !== undefined) { obj.assocOnly = !!(isAssocType && obj.assocOnly); return obj.id; } else { @@ -2082,7 +2092,8 @@ class DocSearch { descIndex += 1; } - // a String of one character item type codes + // see `RawSearchIndexCrate` in `rustdoc.d.ts` for a more + // up to date description of these fields const itemTypes = crateCorpus.t; // an array of (String) item names const itemNames = crateCorpus.n; @@ -2099,8 +2110,6 @@ class DocSearch { const itemParentIdxDecoder = new VlqHexDecoder(crateCorpus.i, noop => noop); // a map Number, string for impl disambiguators const implDisambiguator = new Map(crateCorpus.b); - // an array of [(Number) item type, - // (String) name] const rawPaths = crateCorpus.p; const aliases = crateCorpus.a; // an array of [(Number) item index, @@ -2139,30 +2148,31 @@ class DocSearch { // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = rawPaths.length; - let lastPath = itemPaths.get(0); + let lastPath = undef2null(itemPaths.get(0)); for (let i = 0; i < len; ++i) { const elem = rawPaths[i]; const ty = elem[0]; const name = elem[1]; - let path = null; - if (elem.length > 2 && elem[2] !== null) { - // @ts-expect-error - path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; - lastPath = path; - } - let exactPath = elem.length > 3 && elem[3] !== null ? - // @ts-expect-error - itemPaths.get(elem[3]) : - path; + /** + * @param {2|3} idx + * @param {string|null} if_null + * @param {string|null} if_not_found + * @returns {string|null} + */ + const elemPath = (idx, if_null, if_not_found) => { + if (elem.length > idx && elem[idx] !== undefined) { + const p = itemPaths.get(elem[idx]); + if (p !== undefined) { + return p; + } + return if_not_found; + } + return if_null; + }; + const path = elemPath(2, lastPath, null); + const exactPath = elemPath(3, path, path); const unboxFlag = elem.length > 4 && !!elem[4]; - if (path === undefined) { - path = null; - } - if (exactPath === undefined) { - exactPath = null; - } - lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag }); paths[i] = { ty, name, path, exactPath, unboxFlag }; } @@ -2196,12 +2206,11 @@ class DocSearch { } const name = itemNames[i] === "" ? lastName : itemNames[i]; const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); - /** @type {string} */ - // @ts-expect-error - const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - const paramNames = itemParamNames.has(i) ? - // @ts-expect-error - itemParamNames.get(i).split(",") : + const pathU = itemPaths.get(i); + const path = pathU !== undefined ? pathU : lastPath; + const paramNameString = itemParamNames.get(i); + const paramNames = paramNameString !== undefined ? + paramNameString.split(",") : lastParamNames; const type = itemFunctionDecoder.next(); if (type !== null) { @@ -2243,8 +2252,7 @@ class DocSearch { word, normalizedName, bitIndex, - implDisambiguator: implDisambiguator.has(i) ? - implDisambiguator.get(i) : null, + implDisambiguator: undef2null(implDisambiguator.get(i)), }; this.nameTrie.insert(normalizedName, id, this.tailTable); id += 1; @@ -2482,9 +2490,13 @@ class DocSearch { } } catch (err) { query = newParsedQuery(userQuery); - // is string list - // @ts-expect-error - query.error = err; + if (Array.isArray(err) && err.every(elem => typeof elem === "string")) { + query.error = err; + } else { + // rethrow the error if it isn't a string array + throw err; + } + return query; } if (!query.literalSearch) { @@ -2572,9 +2584,9 @@ 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.normalizedPathLast)) { - // @ts-expect-error - elem.id = genericSymbols.get(elem.normalizedPathLast); + const id = genericSymbols.get(elem.normalizedPathLast); + if (id !== undefined) { + elem.id = id; } else { elem.id = -(genericSymbols.size + 1); genericSymbols.set(elem.normalizedPathLast, elem.id); @@ -2766,15 +2778,28 @@ class DocSearch { for (const result of results) { if (result.id !== -1) { const res = buildHrefAndPath(this.searchIndex[result.id]); + // many of these properties don't strictly need to be + // copied over, but copying them over satisfies tsc, + // and hopefully plays nice with the shape optimization + // of the browser engine. + /** @type {rustdoc.ResultObject} */ const obj = Object.assign({ + parent: result.parent, + type: result.type, dist: result.dist, + path_dist: result.path_dist, + index: result.index, + desc: result.desc, + item: result.item, displayPath: pathSplitter(res[0]), + fullPath: "", + href: "", + displayTypeSignature: null, }, this.searchIndex[result.id]); // To be sure than it some items aren't considered as duplicate. - // @ts-expect-error obj.fullPath = res[2] + "|" + obj.ty; - // @ts-expect-error + if (duplicates.has(obj.fullPath)) { continue; } @@ -2787,18 +2812,15 @@ class DocSearch { if (duplicates.has(res[2] + "|" + TY_IMPORT)) { continue; } - // @ts-expect-error duplicates.add(obj.fullPath); duplicates.add(res[2]); if (typeInfo !== null) { - // @ts-expect-error obj.displayTypeSignature = // @ts-expect-error this.formatDisplayTypeSignature(obj, typeInfo); } - // @ts-expect-error obj.href = res[1]; out.push(obj); if (out.length >= MAX_RESULTS) { @@ -2806,7 +2828,6 @@ class DocSearch { } } } - // @ts-expect-error return out; }; @@ -2819,11 +2840,7 @@ class DocSearch { * * @param {rustdoc.ResultObject} obj * @param {"sig"|"elems"|"returned"|null} typeInfo - * @returns {Promise<{ - * "type": Array, - * "mappedNames": Map, - * "whereClause": Map>, - * }>} + * @returns {Promise} */ this.formatDisplayTypeSignature = async(obj, typeInfo) => { const objType = obj.type; @@ -2885,7 +2902,7 @@ class DocSearch { const whereClause = new Map(); const fnParamNames = obj.paramNames || []; - // @ts-expect-error + /** @type {string[]} */ const queryParamNames = []; /** * Recursively writes a map of IDs to query generic names, @@ -2918,7 +2935,7 @@ class DocSearch { * index 2 is not highlighted, etc. * * @param {{name?: string, highlighted?: boolean}} fnType - input - * @param {[string]} result + * @param {string[]} result */ const pushText = (fnType, result) => { // If !!(result.length % 2) == false, then pushing a new slot starts an even @@ -2930,7 +2947,6 @@ class DocSearch { // needs coerced to a boolean. if (!!(result.length % 2) === !!fnType.highlighted) { result.push(""); - // @ts-expect-error } else if (result.length === 0 && !!fnType.highlighted) { result.push(""); result.push(""); @@ -2944,7 +2960,7 @@ class DocSearch { * or a trait bound on Fn, FnMut, or FnOnce. * * @param {rustdoc.HighlightedFunctionType} fnType - input - * @param {[string]} result + * @param {string[]} result */ const writeHof = (fnType, result) => { const hofOutput = fnType.bindings.get(this.typeNameIdOfOutput) || []; @@ -2984,7 +3000,7 @@ class DocSearch { * Returns `false` if the supplied type isn't special. * * @param {rustdoc.HighlightedFunctionType} fnType - * @param {[string]} result + * @param {string[]} result */ const writeSpecialPrimitive = (fnType, result) => { if (fnType.id === this.typeNameIdOfArray || fnType.id === this.typeNameIdOfSlice || @@ -3031,7 +3047,7 @@ class DocSearch { * updating the where clause and generic type param map. * * @param {rustdoc.HighlightedFunctionType} fnType - * @param {[string]} result + * @param {string[]} result */ const writeFn = (fnType, result) => { if (fnType.id !== null && fnType.id < 0) { @@ -3049,7 +3065,6 @@ class DocSearch { for (const [queryId, fnId] of mgens) { if (fnId === fnType.id) { mappedNames.set( - // @ts-expect-error queryParamNames[-1 - queryId], fnParamNames[-1 - fnType.id], ); @@ -3064,7 +3079,6 @@ class DocSearch { const where = []; onEachBtwn( fnType.generics, - // @ts-expect-error nested => writeFn(nested, where), // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, where), @@ -3099,7 +3113,6 @@ class DocSearch { // shown in the where clause and name mapping output, but is // redundant in this spot for (const value of values) { - // @ts-expect-error writeFn(value, []); } return true; @@ -3141,26 +3154,22 @@ class DocSearch { } } }; - // @ts-expect-error + /** @type {string[]} */ const type = []; onEachBtwn( fnInputs, - // @ts-expect-error fnType => writeFn(fnType, type), // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); - // @ts-expect-error pushText({ name: " -> ", highlighted: false }, type); onEachBtwn( fnOutput, - // @ts-expect-error fnType => writeFn(fnType, type), // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); - // @ts-expect-error return {type, mappedNames, whereClause}; }; @@ -3475,8 +3484,7 @@ class DocSearch { } } } - // @ts-expect-error - return false; + return null; } // Multiple element recursive case @@ -3532,7 +3540,7 @@ class DocSearch { } /** @type {rustdoc.HighlightedFunctionType[]|null} */ let unifiedGenerics = []; - // @ts-expect-error + /** @type {null|Map} */ let unifiedGenericsMgens = null; /** @type {rustdoc.HighlightedFunctionType[]|null} */ const passesUnification = unifyFunctionTypes( @@ -3586,7 +3594,6 @@ class DocSearch { // @ts-expect-error queryElem.bindings.get(k), whereClause, - // @ts-expect-error unifiedGenericsMgens, solutionCb, unboxingDepth, @@ -3984,7 +3991,7 @@ class DocSearch { } const fnTypeBindings = fnType.bindings.get(name); mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { - // @ts-expect-error + /** @type{Array | null>} */ const newSolutions = []; unifyFunctionTypes( // @ts-expect-error @@ -4000,7 +4007,6 @@ class DocSearch { }, unboxingDepth, ); - // @ts-expect-error return newSolutions; }); } @@ -4248,6 +4254,7 @@ class DocSearch { return false; } + // this does not yet have a type in `rustdoc.d.ts`. // @ts-expect-error function createAliasFromItem(item) { return { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 748d2ef33c32c..7611372689478 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -43,6 +43,28 @@ function nonnull(x, msg) { } } +/** + * Assert that the passed value is not undefined, then return it. + * + * Takes an optional error message argument. + * + * Must be defined in this file, as it is loaded before all others. + * + * @template T + * @param {T|undefined} x + * @param {string=} msg + * @returns T + */ +// used in other files, not yet used in this one. +// eslint-disable-next-line no-unused-vars +function nonundef(x, msg) { + if (x === undefined) { + throw (msg || "unexpected null value!"); + } else { + return x; + } +} + /** * Get a configuration value. If it's not set, get the default. * diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index ea575f27799a9..f70fc917770c6 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -451,7 +451,7 @@ function loadSearchJS(doc_folder, resource_suffix) { if (!Object.prototype.hasOwnProperty.call(entry, key)) { continue; } - if (key === "displayTypeSignature") { + if (key === "displayTypeSignature" && entry.displayTypeSignature !== null) { const {type, mappedNames, whereClause} = await entry.displayTypeSignature; entry.displayType = arrayToCode(type);