} generics - List of generics of this query element.
+ *
+ * @return {QueryElement} - The newly created `QueryElement`.
+ */
+function createQueryElement(query, parserState, name, generics, isInGenerics) {
+ const path = name.trim();
+ if (path.length === 0 && generics.length === 0) {
+ throw ["Unexpected ", parserState.userQuery[parserState.pos]];
+ }
+ if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) {
+ throw ["Cannot have more than one element if you use quotes"];
+ }
+ const typeFilter = parserState.typeFilter;
+ parserState.typeFilter = null;
+ if (name === "!") {
+ if (typeFilter !== null && typeFilter !== "primitive") {
+ throw [
+ "Invalid search type: primitive never type ",
+ "!",
+ " and ",
+ typeFilter,
+ " both specified",
+ ];
+ }
+ if (generics.length !== 0) {
+ throw [
+ "Never type ",
+ "!",
+ " does not accept generic parameters",
+ ];
+ }
+ const bindingName = parserState.isInBinding;
+ parserState.isInBinding = null;
+ return makePrimitiveElement("never", { bindingName });
+ }
+ const quadcolon = /::\s*::/.exec(path);
+ if (path.startsWith("::")) {
+ throw ["Paths cannot start with ", "::"];
+ } else if (path.endsWith("::")) {
+ throw ["Paths cannot end with ", "::"];
+ } else if (quadcolon !== null) {
+ throw ["Unexpected ", quadcolon[0]];
+ }
+ const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);
+ // In case we only have something like ``, there is no name.
+ if (pathSegments.length === 0
+ || (pathSegments.length === 1 && pathSegments[0] === "")) {
+ if (generics.length > 0 || prevIs(parserState, ">")) {
+ throw ["Found generics without a path"];
+ } else {
+ throw ["Unexpected ", parserState.userQuery[parserState.pos]];
+ }
+ }
+ for (const [i, pathSegment] of pathSegments.entries()) {
+ if (pathSegment === "!") {
+ if (i !== 0) {
+ throw ["Never type ", "!", " is not associated item"];
+ }
+ pathSegments[i] = "never";
+ }
+ }
+ parserState.totalElems += 1;
+ if (isInGenerics) {
+ parserState.genericsElems += 1;
+ }
+ const bindingName = parserState.isInBinding;
+ parserState.isInBinding = null;
+ const bindings = new Map();
+ const pathLast = pathSegments[pathSegments.length - 1];
+ return {
+ name: name.trim(),
+ id: null,
+ fullPath: pathSegments,
+ pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
+ pathLast,
+ normalizedPathLast: pathLast.replace(/_/g, ""),
+ generics: generics.filter(gen => {
+ // Syntactically, bindings are parsed as generics,
+ // but the query engine treats them differently.
+ if (gen.bindingName !== null) {
+ if (gen.name !== null) {
+ gen.bindingName.generics.unshift(gen);
+ }
+ bindings.set(gen.bindingName.name, gen.bindingName.generics);
+ return false;
+ }
+ return true;
+ }),
+ bindings,
+ typeFilter,
+ bindingName,
+ };
+}
+
+function makePrimitiveElement(name, extra) {
+ return Object.assign({
+ name,
+ id: null,
+ fullPath: [name],
+ pathWithoutLast: [],
+ pathLast: name,
+ normalizedPathLast: name,
+ generics: [],
+ bindings: new Map(),
+ typeFilter: "primitive",
+ bindingName: null,
+ }, extra);
+}
+
+/**
+ * If we encounter a `"`, then we try to extract the string
+ * from it until we find another `"`.
+ *
+ * This function will throw an error in the following cases:
+ * * There is already another string element.
+ * * We are parsing a generic argument.
+ * * There is more than one element.
+ * * There is no closing `"`.
+ *
+ * @param {ParsedQuery} query
+ * @param {ParserState} parserState
+ * @param {boolean} isInGenerics
+ */
+function getStringElem(query, parserState, isInGenerics) {
+ if (isInGenerics) {
+ throw ["Unexpected ", "\"", " in generics"];
+ } else if (query.literalSearch) {
+ throw ["Cannot have more than one literal search element"];
+ } else if (parserState.totalElems - parserState.genericsElems > 0) {
+ throw ["Cannot use literal search when there is more than one element"];
+ }
+ parserState.pos += 1;
+ const start = parserState.pos;
+ const end = getIdentEndPosition(parserState);
+ if (parserState.pos >= parserState.length) {
+ throw ["Unclosed ", "\""];
+ } else if (parserState.userQuery[end] !== "\"") {
+ throw ["Unexpected ", parserState.userQuery[end], " in a string element"];
+ } else if (start === end) {
+ throw ["Cannot have empty string element"];
+ }
+ // To skip the quote at the end.
+ parserState.pos += 1;
+ query.literalSearch = true;
+}
+
+/**
+ * This function goes through all characters until it reaches an invalid ident
+ * character or the end of the query. It returns the position of the last
+ * character of the ident.
+ *
+ * @param {ParserState} parserState
+ *
+ * @return {integer}
+ */
+function getIdentEndPosition(parserState) {
+ let afterIdent = consumeIdent(parserState);
+ let end = parserState.pos;
+ let macroExclamation = -1;
+ while (parserState.pos < parserState.length) {
+ const c = parserState.userQuery[parserState.pos];
+ if (c === "!") {
+ if (macroExclamation !== -1) {
+ throw ["Cannot have more than one ", "!", " in an ident"];
+ } else if (parserState.pos + 1 < parserState.length) {
+ const pos = parserState.pos;
+ parserState.pos++;
+ const beforeIdent = consumeIdent(parserState);
+ parserState.pos = pos;
+ if (beforeIdent) {
+ throw ["Unexpected ", "!", ": it can only be at the end of an ident"];
+ }
+ }
+ if (afterIdent) macroExclamation = parserState.pos;
+ } else if (isPathSeparator(c)) {
+ if (c === ":") {
+ if (!isPathStart(parserState)) {
+ break;
+ }
+ // Skip current ":".
+ parserState.pos += 1;
+ } else {
+ while (parserState.pos + 1 < parserState.length) {
+ const next_c = parserState.userQuery[parserState.pos + 1];
+ if (next_c !== " ") {
+ break;
+ }
+ parserState.pos += 1;
+ }
+ }
+ if (macroExclamation !== -1) {
+ throw ["Cannot have associated items in macros"];
+ }
+ } else if (
+ c === "[" ||
+ c === "(" ||
+ isEndCharacter(c) ||
+ isSpecialStartCharacter(c) ||
+ isSeparatorCharacter(c)
+ ) {
+ break;
+ } else if (parserState.pos > 0) {
+ throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1],
+ " (not a valid identifier)"];
+ } else {
+ throw ["Unexpected ", c, " (not a valid identifier)"];
+ }
+ parserState.pos += 1;
+ afterIdent = consumeIdent(parserState);
+ end = parserState.pos;
+ }
+ if (macroExclamation !== -1) {
+ if (parserState.typeFilter === null) {
+ parserState.typeFilter = "macro";
+ } else if (parserState.typeFilter !== "macro") {
+ throw [
+ "Invalid search type: macro ",
+ "!",
+ " and ",
+ parserState.typeFilter,
+ " both specified",
+ ];
+ }
+ end = macroExclamation;
+ }
+ return end;
+}
+
+function isSpecialStartCharacter(c) {
+ return "<\"".indexOf(c) !== -1;
+}
+
+/**
+ * Returns `true` if the current parser position is starting with "::".
+ *
+ * @param {ParserState} parserState
+ *
+ * @return {boolean}
+ */
+function isPathStart(parserState) {
+ return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "::";
+}
+
+/**
+ * If the current parser position is at the beginning of an identifier,
+ * move the position to the end of it and return `true`. Otherwise, return `false`.
+ *
+ * @param {ParserState} parserState
+ *
+ * @return {boolean}
+ */
+function consumeIdent(parserState) {
+ REGEX_IDENT.lastIndex = parserState.pos;
+ const match = parserState.userQuery.match(REGEX_IDENT);
+ if (match) {
+ parserState.pos += match[0].length;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Returns `true` if the given `c` character is a path separator. For example
+ * `:` in `a::b` or a whitespace in `a b`.
+ *
+ * @param {string} c
+ *
+ * @return {boolean}
+ */
+function isPathSeparator(c) {
+ return c === ":" || c === " ";
+}
+
+class VlqHexDecoder {
+ constructor(string, cons) {
+ this.string = string;
+ this.cons = cons;
+ this.offset = 0;
+ this.backrefQueue = [];
+ }
+ // call after consuming `{`
+ decodeList() {
+ let c = this.string.charCodeAt(this.offset);
+ const ret = [];
+ while (c !== 125) { // 125 = "}"
+ ret.push(this.decode());
+ c = this.string.charCodeAt(this.offset);
+ }
+ this.offset += 1; // eat cb
+ return ret;
+ }
+ // consumes and returns a list or integer
+ decode() {
+ let n = 0;
+ let c = this.string.charCodeAt(this.offset);
+ if (c === 123) { // 123 = "{"
+ this.offset += 1;
+ return this.decodeList();
+ }
+ while (c < 96) { // 96 = "`"
+ n = (n << 4) | (c & 0xF);
+ this.offset += 1;
+ c = this.string.charCodeAt(this.offset);
+ }
+ // last character >= la
+ n = (n << 4) | (c & 0xF);
+ const [sign, value] = [n & 1, n >> 1];
+ this.offset += 1;
+ return sign ? -value : value;
+ }
+ next() {
+ const c = this.string.charCodeAt(this.offset);
+ // sixteen characters after "0" are backref
+ if (c >= 48 && c < 64) { // 48 = "0", 64 = "@"
+ this.offset += 1;
+ return this.backrefQueue[c - 48];
+ }
+ // special exception: 0 doesn't use backref encoding
+ // it's already one character, and it's always nullish
+ if (c === 96) { // 96 = "`"
+ this.offset += 1;
+ return this.cons(0);
+ }
+ const result = this.cons(this.decode());
+ this.backrefQueue.unshift(result);
+ if (this.backrefQueue.length > 16) {
+ this.backrefQueue.pop();
+ }
+ return result;
+ }
+}
+class RoaringBitmap {
+ constructor(str) {
+ const strdecoded = atob(str);
+ const u8array = new Uint8Array(strdecoded.length);
+ for (let j = 0; j < strdecoded.length; ++j) {
+ u8array[j] = strdecoded.charCodeAt(j);
+ }
+ const has_runs = u8array[0] === 0x3b;
+ const size = has_runs ?
+ ((u8array[2] | (u8array[3] << 8)) + 1) :
+ ((u8array[4] | (u8array[5] << 8) | (u8array[6] << 16) | (u8array[7] << 24)));
+ let i = has_runs ? 4 : 8;
+ let is_run;
+ if (has_runs) {
+ const is_run_len = Math.floor((size + 7) / 8);
+ is_run = u8array.slice(i, i + is_run_len);
+ i += is_run_len;
+ } else {
+ is_run = new Uint8Array();
+ }
+ this.keys = [];
+ this.cardinalities = [];
+ for (let j = 0; j < size; ++j) {
+ this.keys.push(u8array[i] | (u8array[i + 1] << 8));
+ i += 2;
+ this.cardinalities.push((u8array[i] | (u8array[i + 1] << 8)) + 1);
+ i += 2;
+ }
+ this.containers = [];
+ let offsets = null;
+ if (!has_runs || this.keys.length >= 4) {
+ offsets = [];
+ for (let j = 0; j < size; ++j) {
+ offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) |
+ (u8array[i + 3] << 24));
+ i += 4;
+ }
+ }
+ for (let j = 0; j < size; ++j) {
+ if (offsets && offsets[j] !== i) {
+ console.log(this.containers);
+ throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`);
+ }
+ if (is_run[j >> 3] & (1 << (j & 0x7))) {
+ const runcount = (u8array[i] | (u8array[i + 1] << 8));
+ i += 2;
+ this.containers.push(new RoaringBitmapRun(
+ runcount,
+ u8array.slice(i, i + (runcount * 4)),
+ ));
+ i += runcount * 4;
+ } else if (this.cardinalities[j] >= 4096) {
+ this.containers.push(new RoaringBitmapBits(u8array.slice(i, i + 8192)));
+ i += 8192;
+ } else {
+ const end = this.cardinalities[j] * 2;
+ this.containers.push(new RoaringBitmapArray(
+ this.cardinalities[j],
+ u8array.slice(i, i + end),
+ ));
+ i += end;
+ }
+ }
+ }
+ contains(keyvalue) {
+ const key = keyvalue >> 16;
+ const value = keyvalue & 0xFFFF;
+ for (let i = 0; i < this.keys.length; ++i) {
+ if (this.keys[i] === key) {
+ return this.containers[i].contains(value);
+ }
+ }
+ return false;
+ }
+}
+
+class RoaringBitmapRun {
+ constructor(runcount, array) {
+ this.runcount = runcount;
+ this.array = array;
+ }
+ contains(value) {
+ const l = this.runcount * 4;
+ for (let i = 0; i < l; i += 4) {
+ const start = this.array[i] | (this.array[i + 1] << 8);
+ const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8);
+ if (value >= start && value <= (start + lenm1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+class RoaringBitmapArray {
+ constructor(cardinality, array) {
+ this.cardinality = cardinality;
+ this.array = array;
+ }
+ contains(value) {
+ const l = this.cardinality * 2;
+ for (let i = 0; i < l; i += 2) {
+ const start = this.array[i] | (this.array[i + 1] << 8);
+ if (value === start) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+class RoaringBitmapBits {
+ constructor(array) {
+ this.array = array;
+ }
+ contains(value) {
+ return !!(this.array[value >> 3] & (1 << (value & 7)));
+ }
+}
+
+
+class DocSearch {
+ constructor(rawSearchIndex, rootPath, searchState) {
+ /**
+ * @type {Map}
+ */
+ this.searchIndexDeprecated = new Map();
+ /**
+ * @type {Map}
+ */
+ this.searchIndexEmptyDesc = new Map();
+ /**
+ * @type {Uint32Array}
+ */
+ this.functionTypeFingerprint = null;
+ /**
+ * Map from normalized type names to integers. Used to make type search
+ * more efficient.
+ *
+ * @type {Map}
+ */
+ this.typeNameIdMap = new Map();
+ this.ALIASES = new Map();
+ this.rootPath = rootPath;
+ this.searchState = searchState;
+
+ /**
+ * Special type name IDs for searching by array.
+ */
+ this.typeNameIdOfArray = this.buildTypeMapIndex("array");
+ /**
+ * Special type name IDs for searching by slice.
+ */
+ this.typeNameIdOfSlice = this.buildTypeMapIndex("slice");
+ /**
+ * Special type name IDs for searching by both array and slice (`[]` syntax).
+ */
+ this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]");
+ /**
+ * Special type name IDs for searching by tuple.
+ */
+ this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple");
+ /**
+ * Special type name IDs for searching by unit.
+ */
+ this.typeNameIdOfUnit = this.buildTypeMapIndex("unit");
+ /**
+ * Special type name IDs for searching by both tuple and unit (`()` syntax).
+ */
+ this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()");
+ /**
+ * Special type name IDs for searching `fn`.
+ */
+ this.typeNameIdOfFn = this.buildTypeMapIndex("fn");
+ /**
+ * Special type name IDs for searching `fnmut`.
+ */
+ this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut");
+ /**
+ * Special type name IDs for searching `fnonce`.
+ */
+ this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce");
+ /**
+ * Special type name IDs for searching higher order functions (`->` syntax).
+ */
+ this.typeNameIdOfHof = this.buildTypeMapIndex("->");
+
+ /**
+ * Empty, immutable map used in item search types with no bindings.
+ *
+ * @type {Map>}
+ */
+ this.EMPTY_BINDINGS_MAP = new Map();
+
+ /**
+ * Empty, immutable map used in item search types with no bindings.
+ *
+ * @type {Array}
+ */
+ this.EMPTY_GENERICS_ARRAY = [];
+
+ /**
+ * Object pool for function types with no bindings or generics.
+ * This is reset after loading the index.
+ *
+ * @type {Map}
+ */
+ this.TYPES_POOL = new Map();
+
+ /**
+ * @type {Array}
+ */
+ this.searchIndex = this.buildIndex(rawSearchIndex);
+ }
/**
* Add an item to the type Name->ID map, or, if one already exists, use it.
@@ -323,918 +1200,540 @@ function initSearch(rawSearchIndex) {
*
* @returns {integer}
*/
- function buildTypeMapIndex(name, isAssocType) {
+ buildTypeMapIndex(name, isAssocType) {
if (name === "" || name === null) {
return null;
}
- if (typeNameIdMap.has(name)) {
- const obj = typeNameIdMap.get(name);
+ if (this.typeNameIdMap.has(name)) {
+ const obj = this.typeNameIdMap.get(name);
obj.assocOnly = isAssocType && obj.assocOnly;
return obj.id;
} else {
- const id = typeNameIdMap.size;
- typeNameIdMap.set(name, {id, assocOnly: isAssocType});
+ const id = this.typeNameIdMap.size;
+ this.typeNameIdMap.set(name, { id, assocOnly: isAssocType });
return id;
}
}
- function isSpecialStartCharacter(c) {
- return "<\"".indexOf(c) !== -1;
- }
-
- function isEndCharacter(c) {
- return "=,>-])".indexOf(c) !== -1;
- }
-
- function itemTypeFromName(typename) {
- const index = itemTypes.findIndex(i => i === typename);
- if (index < 0) {
- throw ["Unknown type filter ", typename];
- }
- return index;
+ /**
+ * Convert a list of RawFunctionType / ID to object-based FunctionType.
+ *
+ * Crates often have lots of functions in them, and it's common to have a large number of
+ * functions that operate on a small set of data types, so the search index compresses them
+ * by encoding function parameter and return types as indexes into an array of names.
+ *
+ * Even when a general-purpose compression algorithm is used, this is still a win.
+ * I checked. https://github.com/rust-lang/rust/pull/98475#issue-1284395985
+ *
+ * The format for individual function types is encoded in
+ * librustdoc/html/render/mod.rs: impl Serialize for RenderType
+ *
+ * @param {null|Array} types
+ * @param {Array<{name: string, ty: number}>} lowercasePaths
+ *
+ * @return {Array}
+ */
+ buildItemSearchTypeAll(types, lowercasePaths) {
+ return types.length > 0 ?
+ types.map(type => this.buildItemSearchType(type, lowercasePaths)) :
+ this.EMPTY_GENERICS_ARRAY;
}
/**
- * If we encounter a `"`, then we try to extract the string from it until we find another `"`.
+ * Converts a single type.
*
- * This function will throw an error in the following cases:
- * * There is already another string element.
- * * We are parsing a generic argument.
- * * There is more than one element.
- * * There is no closing `"`.
- *
- * @param {ParsedQuery} query
- * @param {ParserState} parserState
- * @param {boolean} isInGenerics
+ * @param {RawFunctionType} type
*/
- function getStringElem(query, parserState, isInGenerics) {
- if (isInGenerics) {
- throw ["Unexpected ", "\"", " in generics"];
- } else if (query.literalSearch) {
- throw ["Cannot have more than one literal search element"];
- } else if (parserState.totalElems - parserState.genericsElems > 0) {
- throw ["Cannot use literal search when there is more than one element"];
- }
- parserState.pos += 1;
- const start = parserState.pos;
- const end = getIdentEndPosition(parserState);
- if (parserState.pos >= parserState.length) {
- throw ["Unclosed ", "\""];
- } else if (parserState.userQuery[end] !== "\"") {
- throw ["Unexpected ", parserState.userQuery[end], " in a string element"];
- } else if (start === end) {
- throw ["Cannot have empty string element"];
- }
- // To skip the quote at the end.
- parserState.pos += 1;
- query.literalSearch = true;
- }
-
- /**
- * Returns `true` if the current parser position is starting with "::".
- *
- * @param {ParserState} parserState
- *
- * @return {boolean}
- */
- function isPathStart(parserState) {
- return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "::";
- }
-
- /**
- * Returns `true` if the current parser position is starting with "->".
- *
- * @param {ParserState} parserState
- *
- * @return {boolean}
- */
- function isReturnArrow(parserState) {
- return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "->";
- }
-
- /**
- * If the current parser position is at the beginning of an identifier,
- * move the position to the end of it and return `true`. Otherwise, return `false`.
- *
- * @param {ParserState} parserState
- *
- * @return {boolean}
- */
- function consumeIdent(parserState) {
- REGEX_IDENT.lastIndex = parserState.pos;
- const match = parserState.userQuery.match(REGEX_IDENT);
- if (match) {
- parserState.pos += match[0].length;
- return true;
- }
- return false;
- }
-
- /**
- * Returns `true` if the given `c` character is a separator.
- *
- * @param {string} c
- *
- * @return {boolean}
- */
- function isSeparatorCharacter(c) {
- return c === "," || c === "=";
- }
-
- /**
- * Returns `true` if the given `c` character is a path separator. For example
- * `:` in `a::b` or a whitespace in `a b`.
- *
- * @param {string} c
- *
- * @return {boolean}
- */
- function isPathSeparator(c) {
- return c === ":" || c === " ";
- }
-
- /**
- * Returns `true` if the previous character is `lookingFor`.
- *
- * @param {ParserState} parserState
- * @param {String} lookingFor
- *
- * @return {boolean}
- */
- function prevIs(parserState, lookingFor) {
- let pos = parserState.pos;
- while (pos > 0) {
- const c = parserState.userQuery[pos - 1];
- if (c === lookingFor) {
- return true;
- } else if (c !== " ") {
- break;
- }
- pos -= 1;
- }
- return false;
- }
-
- /**
- * Returns `true` if the last element in the `elems` argument has generics.
- *
- * @param {Array} elems
- * @param {ParserState} parserState
- *
- * @return {boolean}
- */
- function isLastElemGeneric(elems, parserState) {
- return (elems.length > 0 && elems[elems.length - 1].generics.length > 0) ||
- prevIs(parserState, ">");
- }
-
- /**
- * Increase current parser position until it doesn't find a whitespace anymore.
- *
- * @param {ParserState} parserState
- */
- function skipWhitespace(parserState) {
- while (parserState.pos < parserState.userQuery.length) {
- const c = parserState.userQuery[parserState.pos];
- if (c !== " ") {
- break;
- }
- parserState.pos += 1;
- }
- }
-
- function makePrimitiveElement(name, extra) {
- return Object.assign({
- name,
- id: null,
- fullPath: [name],
- pathWithoutLast: [],
- pathLast: name,
- normalizedPathLast: name,
- generics: [],
- bindings: new Map(),
- typeFilter: "primitive",
- bindingName: null,
- }, extra);
- }
-
- /**
- * @param {ParsedQuery} query
- * @param {ParserState} parserState
- * @param {string} name - Name of the query element.
- * @param {Array} generics - List of generics of this query element.
- *
- * @return {QueryElement} - The newly created `QueryElement`.
- */
- function createQueryElement(query, parserState, name, generics, isInGenerics) {
- const path = name.trim();
- if (path.length === 0 && generics.length === 0) {
- throw ["Unexpected ", parserState.userQuery[parserState.pos]];
- }
- if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) {
- throw ["Cannot have more than one element if you use quotes"];
- }
- const typeFilter = parserState.typeFilter;
- parserState.typeFilter = null;
- if (name === "!") {
- if (typeFilter !== null && typeFilter !== "primitive") {
- throw [
- "Invalid search type: primitive never type ",
- "!",
- " and ",
- typeFilter,
- " both specified",
- ];
- }
- if (generics.length !== 0) {
- throw [
- "Never type ",
- "!",
- " does not accept generic parameters",
- ];
- }
- const bindingName = parserState.isInBinding;
- parserState.isInBinding = null;
- return makePrimitiveElement("never", { bindingName });
- }
- const quadcolon = /::\s*::/.exec(path);
- if (path.startsWith("::")) {
- throw ["Paths cannot start with ", "::"];
- } else if (path.endsWith("::")) {
- throw ["Paths cannot end with ", "::"];
- } else if (quadcolon !== null) {
- throw ["Unexpected ", quadcolon[0]];
- }
- const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);
- // In case we only have something like ``, there is no name.
- if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) {
- if (generics.length > 0 || prevIs(parserState, ">")) {
- throw ["Found generics without a path"];
+ buildItemSearchType(type, lowercasePaths, isAssocType) {
+ const PATH_INDEX_DATA = 0;
+ const GENERICS_DATA = 1;
+ const BINDINGS_DATA = 2;
+ let pathIndex, generics, bindings;
+ if (typeof type === "number") {
+ pathIndex = type;
+ generics = this.EMPTY_GENERICS_ARRAY;
+ bindings = this.EMPTY_BINDINGS_MAP;
+ } else {
+ pathIndex = type[PATH_INDEX_DATA];
+ generics = this.buildItemSearchTypeAll(
+ type[GENERICS_DATA],
+ lowercasePaths,
+ );
+ if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) {
+ bindings = new Map(type[BINDINGS_DATA].map(binding => {
+ const [assocType, constraints] = binding;
+ // Associated type constructors are represented sloppily in rustdoc's
+ // type search, to make the engine simpler.
+ //
+ // MyType