diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 62afe40bb31..984358396ab 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -208,6 +208,46 @@ function initSearch(rawSearchIndex) {
let typeNameIdMap;
const ALIASES = new Map();
+ /**
+ * Special type name IDs for searching by array.
+ */
+ let typeNameIdOfArray;
+ /**
+ * Special type name IDs for searching by slice.
+ */
+ let typeNameIdOfSlice;
+ /**
+ * Special type name IDs for searching by both array and slice (`[]` syntax).
+ */
+ let typeNameIdOfArrayOrSlice;
+
+ /**
+ * Add an item to the type Name->ID map, or, if one already exists, use it.
+ * Returns the number. If name is "" or null, return -1 (pure generic).
+ *
+ * This is effectively string interning, so that function matching can be
+ * done more quickly. Two types with the same name but different item kinds
+ * get the same ID.
+ *
+ * @param {string} name
+ *
+ * @returns {integer}
+ */
+ function buildTypeMapIndex(name) {
+
+ if (name === "" || name === null) {
+ return -1;
+ }
+
+ if (typeNameIdMap.has(name)) {
+ return typeNameIdMap.get(name);
+ } else {
+ const id = typeNameIdMap.size;
+ typeNameIdMap.set(name, id);
+ return id;
+ }
+ }
+
function isWhitespace(c) {
return " \t\n\r".indexOf(c) !== -1;
}
@@ -217,7 +257,7 @@ function initSearch(rawSearchIndex) {
}
function isEndCharacter(c) {
- return ",>-".indexOf(c) !== -1;
+ return ",>-]".indexOf(c) !== -1;
}
function isStopCharacter(c) {
@@ -466,35 +506,64 @@ function initSearch(rawSearchIndex) {
let start = parserState.pos;
let end;
- // We handle the strings on their own mostly to make code easier to follow.
- if (parserState.userQuery[parserState.pos] === "\"") {
- start += 1;
- getStringElem(query, parserState, isInGenerics);
- end = parserState.pos - 1;
- } else {
- end = getIdentEndPosition(parserState);
- }
- if (parserState.pos < parserState.length &&
- parserState.userQuery[parserState.pos] === "<"
- ) {
- if (start >= end) {
- throw ["Found generics without a path"];
- }
+ if (parserState.userQuery[parserState.pos] === "[") {
parserState.pos += 1;
- getItemsBefore(query, parserState, generics, ">");
- }
- if (start >= end && generics.length === 0) {
- return;
- }
- elems.push(
- createQueryElement(
- query,
- parserState,
- parserState.userQuery.slice(start, end),
+ getItemsBefore(query, parserState, generics, "]");
+ const typeFilter = parserState.typeFilter;
+ if (typeFilter !== null && typeFilter !== "primitive") {
+ throw [
+ "Invalid search type: primitive ",
+ "[]",
+ " and ",
+ typeFilter,
+ " both specified",
+ ];
+ }
+ parserState.typeFilter = null;
+ parserState.totalElems += 1;
+ if (isInGenerics) {
+ parserState.genericsElems += 1;
+ }
+ elems.push({
+ name: "[]",
+ id: -1,
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
generics,
- isInGenerics
- )
- );
+ typeFilter: "primitive",
+ });
+ } else {
+ // We handle the strings on their own mostly to make code easier to follow.
+ if (parserState.userQuery[parserState.pos] === "\"") {
+ start += 1;
+ getStringElem(query, parserState, isInGenerics);
+ end = parserState.pos - 1;
+ } else {
+ end = getIdentEndPosition(parserState);
+ }
+ if (parserState.pos < parserState.length &&
+ parserState.userQuery[parserState.pos] === "<"
+ ) {
+ if (start >= end) {
+ throw ["Found generics without a path"];
+ }
+ parserState.pos += 1;
+ getItemsBefore(query, parserState, generics, ">");
+ }
+ if (start >= end && generics.length === 0) {
+ return;
+ }
+ elems.push(
+ createQueryElement(
+ query,
+ parserState,
+ parserState.userQuery.slice(start, end),
+ generics,
+ isInGenerics
+ )
+ );
+ }
}
/**
@@ -518,6 +587,17 @@ function initSearch(rawSearchIndex) {
const oldTypeFilter = parserState.typeFilter;
parserState.typeFilter = null;
+ let extra = "";
+ if (endChar === ">") {
+ extra = "<";
+ } else if (endChar === "]") {
+ extra = "[";
+ } else if (endChar === "") {
+ extra = "->";
+ } else {
+ extra = endChar;
+ }
+
while (parserState.pos < parserState.length) {
const c = parserState.userQuery[parserState.pos];
if (c === endChar) {
@@ -547,14 +627,6 @@ function initSearch(rawSearchIndex) {
foundStopChar = true;
continue;
} else if (isEndCharacter(c)) {
- let extra = "";
- if (endChar === ">") {
- extra = "<";
- } else if (endChar === "") {
- extra = "->";
- } else {
- extra = endChar;
- }
throw ["Unexpected ", c, " after ", extra];
}
if (!foundStopChar) {
@@ -581,9 +653,9 @@ function initSearch(rawSearchIndex) {
}
const posBefore = parserState.pos;
start = parserState.pos;
- getNextElem(query, parserState, elems, endChar === ">");
+ getNextElem(query, parserState, elems, endChar !== "");
if (endChar !== "" && parserState.pos >= parserState.length) {
- throw ["Unclosed ", "<"];
+ throw ["Unclosed ", extra];
}
// This case can be encountered if `getNextElem` encountered a "stop character" right
// from the start. For example if you have `,,` or `<>`. In this case, we simply move up
@@ -594,7 +666,7 @@ function initSearch(rawSearchIndex) {
foundStopChar = false;
}
if (parserState.pos >= parserState.length && endChar !== "") {
- throw ["Unclosed ", "<"];
+ throw ["Unclosed ", extra];
}
// We are either at the end of the string or on the `endChar` character, let's move forward
// in any case.
@@ -779,7 +851,8 @@ function initSearch(rawSearchIndex) {
*
* ident = *(ALPHA / DIGIT / "_")
* path = ident *(DOUBLE-COLON ident) [!]
- * arg = [type-filter *WS COLON *WS] path [generics]
+ * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
+ * arg = [type-filter *WS COLON *WS] (path [generics] / slice)
* type-sep = COMMA/WS *(COMMA/WS)
* nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
* generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
@@ -821,6 +894,8 @@ function initSearch(rawSearchIndex) {
*
* OPEN-ANGLE-BRACKET = "<"
* CLOSE-ANGLE-BRACKET = ">"
+ * OPEN-SQUARE-BRACKET = "["
+ * CLOSE-SQUARE-BRACKET = "]"
* COLON = ":"
* DOUBLE-COLON = "::"
* QUOTE = %x22
@@ -1170,7 +1245,22 @@ function initSearch(rawSearchIndex) {
// ones with no type filter, which can match any entry regardless of its
// own type.
for (const generic of elem.generics) {
- if (generic.typeFilter !== -1 && !handleGeneric(generic)) {
+ if (generic.typeFilter === TY_PRIMITIVE &&
+ generic.id === typeNameIdOfArrayOrSlice) {
+ const genericArray = {
+ id: typeNameIdOfArray,
+ typeFilter: TY_PRIMITIVE,
+ generics: generic.generics,
+ };
+ const genericSlice = {
+ id: typeNameIdOfSlice,
+ typeFilter: TY_PRIMITIVE,
+ generics: generic.generics,
+ };
+ if (!handleGeneric(genericArray) && !handleGeneric(genericSlice)) {
+ return false;
+ }
+ } else if (generic.typeFilter !== -1 && !handleGeneric(generic)) {
return false;
}
}
@@ -1217,7 +1307,12 @@ function initSearch(rawSearchIndex) {
return row.generics.length > 0 ? checkIfInGenerics(row, elem) : false;
}
- if (row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty)) {
+ const matchesExact = row.id === elem.id;
+ const matchesArrayOrSlice = elem.id === typeNameIdOfArrayOrSlice &&
+ (row.id === typeNameIdOfSlice || row.id === typeNameIdOfArray);
+
+ if ((matchesExact || matchesArrayOrSlice) &&
+ typePassesFilter(elem.typeFilter, row.ty)) {
if (elem.generics.length > 0) {
return checkGenerics(row, elem);
}
@@ -2082,34 +2177,6 @@ function initSearch(rawSearchIndex) {
filterCrates);
}
- /**
- * Add an item to the type Name->ID map, or, if one already exists, use it.
- * Returns the number. If name is "" or null, return -1 (pure generic).
- *
- * This is effectively string interning, so that function matching can be
- * done more quickly. Two types with the same name but different item kinds
- * get the same ID.
- *
- * @param {Map} typeNameIdMap
- * @param {string} name
- *
- * @returns {integer}
- */
- function buildTypeMapIndex(typeNameIdMap, name) {
-
- if (name === "" || name === null) {
- return -1;
- }
-
- if (typeNameIdMap.has(name)) {
- return typeNameIdMap.get(name);
- } else {
- const id = typeNameIdMap.size;
- typeNameIdMap.set(name, id);
- return id;
- }
- }
-
/**
* Convert a list of RawFunctionType / ID to object-based FunctionType.
*
@@ -2128,7 +2195,7 @@ function initSearch(rawSearchIndex) {
*
* @return {Array}
*/
- function buildItemSearchTypeAll(types, lowercasePaths, typeNameIdMap) {
+ function buildItemSearchTypeAll(types, lowercasePaths) {
const PATH_INDEX_DATA = 0;
const GENERICS_DATA = 1;
return types.map(type => {
@@ -2140,15 +2207,14 @@ function initSearch(rawSearchIndex) {
pathIndex = type[PATH_INDEX_DATA];
generics = buildItemSearchTypeAll(
type[GENERICS_DATA],
- lowercasePaths,
- typeNameIdMap
+ lowercasePaths
);
}
return {
// `0` is used as a sentinel because it's fewer bytes than `null`
id: pathIndex === 0
? -1
- : buildTypeMapIndex(typeNameIdMap, lowercasePaths[pathIndex - 1].name),
+ : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
generics: generics,
};
@@ -2171,7 +2237,7 @@ function initSearch(rawSearchIndex) {
*
* @return {null|FunctionSearchType}
*/
- function buildFunctionSearchType(functionSearchType, lowercasePaths, typeNameIdMap) {
+ function buildFunctionSearchType(functionSearchType, lowercasePaths) {
const INPUTS_DATA = 0;
const OUTPUT_DATA = 1;
// `0` is used as a sentinel because it's fewer bytes than `null`
@@ -2184,15 +2250,14 @@ function initSearch(rawSearchIndex) {
inputs = [{
id: pathIndex === 0
? -1
- : buildTypeMapIndex(typeNameIdMap, lowercasePaths[pathIndex - 1].name),
+ : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
generics: [],
}];
} else {
inputs = buildItemSearchTypeAll(
functionSearchType[INPUTS_DATA],
- lowercasePaths,
- typeNameIdMap
+ lowercasePaths
);
}
if (functionSearchType.length > 1) {
@@ -2201,15 +2266,14 @@ function initSearch(rawSearchIndex) {
output = [{
id: pathIndex === 0
? -1
- : buildTypeMapIndex(typeNameIdMap, lowercasePaths[pathIndex - 1].name),
+ : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
generics: [],
}];
} else {
output = buildItemSearchTypeAll(
functionSearchType[OUTPUT_DATA],
- lowercasePaths,
- typeNameIdMap
+ lowercasePaths
);
}
} else {
@@ -2233,6 +2297,12 @@ function initSearch(rawSearchIndex) {
let currentIndex = 0;
let id = 0;
+ // Initialize type map indexes for primitive list types
+ // that can be searched using `[]` syntax.
+ typeNameIdOfArray = buildTypeMapIndex("array");
+ typeNameIdOfSlice = buildTypeMapIndex("slice");
+ typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]");
+
for (const crate in rawSearchIndex) {
if (!hasOwnPropertyRustdoc(rawSearchIndex, crate)) {
continue;
@@ -2363,8 +2433,7 @@ function initSearch(rawSearchIndex) {
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
type: buildFunctionSearchType(
itemFunctionSearchTypes[i],
- lowercasePaths,
- typeNameIdMap
+ lowercasePaths
),
id: id,
normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js
index 8f6b0450dd3..25997850661 100644
--- a/tests/rustdoc-js-std/option-type-signatures.js
+++ b/tests/rustdoc-js-std/option-type-signatures.js
@@ -12,4 +12,11 @@ const EXPECTED = [
{ 'path': 'std::option::Option', 'name': 'get_or_insert_default' },
],
},
+ {
+ 'query': 'option -> []',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'as_slice' },
+ { 'path': 'std::option::Option', 'name': 'as_mut_slice' },
+ ],
+ },
];
diff --git a/tests/rustdoc-js-std/parser-slice-array.js b/tests/rustdoc-js-std/parser-slice-array.js
new file mode 100644
index 00000000000..4c0a1defe71
--- /dev/null
+++ b/tests/rustdoc-js-std/parser-slice-array.js
@@ -0,0 +1,308 @@
+const QUERY = [
+ '[[[D, []]]',
+ '[[[D, []]]]',
+ '[] u8',
+ '[u8]',
+ '[u8,u8]',
+ '[u8]',
+ '[]',
+ '[>',
+ '[<',
+ '[a>',
+ '[a<',
+ '[a',
+ '[',
+ ']',
+ 'primitive:[u8]',
+ 'macro:[u8]',
+];
+
+const PARSED = [
+ {
+ elems: [],
+ foundElems: 0,
+ original: '[[[D, []]]',
+ returned: [],
+ userQuery: '[[[d, []]]',
+ error: 'Unclosed `[`',
+ },
+ {
+ elems: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [
+ {
+ name: "d",
+ fullPath: ["d"],
+ pathWithoutLast: [],
+ pathLast: "d",
+ generics: [],
+ typeFilter: -1,
+ },
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [],
+ typeFilter: 15,
+ },
+ ],
+ typeFilter: 15,
+ },
+ ],
+ typeFilter: 15,
+ },
+ ],
+ typeFilter: 15,
+ },
+ ],
+ foundElems: 1,
+ original: '[[[D, []]]]',
+ returned: [],
+ userQuery: '[[[d, []]]]',
+ error: null,
+ },
+ {
+ elems: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [],
+ typeFilter: 15,
+ },
+ {
+ name: "u8",
+ fullPath: ["u8"],
+ pathWithoutLast: [],
+ pathLast: "u8",
+ generics: [],
+ typeFilter: -1,
+ },
+ ],
+ foundElems: 2,
+ original: "[] u8",
+ returned: [],
+ userQuery: "[] u8",
+ error: null,
+ },
+ {
+ elems: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [
+ {
+ name: "u8",
+ fullPath: ["u8"],
+ pathWithoutLast: [],
+ pathLast: "u8",
+ generics: [],
+ typeFilter: -1,
+ },
+ ],
+ typeFilter: 15,
+ },
+ ],
+ foundElems: 1,
+ original: "[u8]",
+ returned: [],
+ userQuery: "[u8]",
+ error: null,
+ },
+ {
+ elems: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [
+ {
+ name: "u8",
+ fullPath: ["u8"],
+ pathWithoutLast: [],
+ pathLast: "u8",
+ generics: [],
+ typeFilter: -1,
+ },
+ {
+ name: "u8",
+ fullPath: ["u8"],
+ pathWithoutLast: [],
+ pathLast: "u8",
+ generics: [],
+ typeFilter: -1,
+ },
+ ],
+ typeFilter: 15,
+ },
+ ],
+ foundElems: 1,
+ original: "[u8,u8]",
+ returned: [],
+ userQuery: "[u8,u8]",
+ error: null,
+ },
+ {
+ elems: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [
+ {
+ name: "u8",
+ fullPath: ["u8"],
+ pathWithoutLast: [],
+ pathLast: "u8",
+ generics: [
+ {
+ name: "u8",
+ fullPath: ["u8"],
+ pathWithoutLast: [],
+ pathLast: "u8",
+ generics: [],
+ typeFilter: -1,
+ },
+ ],
+ typeFilter: -1,
+ },
+ ],
+ typeFilter: 15,
+ },
+ ],
+ foundElems: 1,
+ original: "[u8]",
+ returned: [],
+ userQuery: "[u8]",
+ error: null,
+ },
+ {
+ elems: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [],
+ typeFilter: 15,
+ },
+ ],
+ foundElems: 1,
+ original: "[]",
+ returned: [],
+ userQuery: "[]",
+ error: null,
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "[>",
+ returned: [],
+ userQuery: "[>",
+ error: "Unexpected `>` after `[`",
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "[<",
+ returned: [],
+ userQuery: "[<",
+ error: "Found generics without a path",
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "[a>",
+ returned: [],
+ userQuery: "[a>",
+ error: "Unexpected `>` after `[`",
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "[a<",
+ returned: [],
+ userQuery: "[a<",
+ error: "Unclosed `<`",
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "[a",
+ returned: [],
+ userQuery: "[a",
+ error: "Unclosed `[`",
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "[",
+ returned: [],
+ userQuery: "[",
+ error: "Unclosed `[`",
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "]",
+ returned: [],
+ userQuery: "]",
+ error: "Unexpected `]`",
+ },
+ {
+ elems: [
+ {
+ name: "[]",
+ fullPath: ["[]"],
+ pathWithoutLast: [],
+ pathLast: "[]",
+ generics: [
+ {
+ name: "u8",
+ fullPath: ["u8"],
+ pathWithoutLast: [],
+ pathLast: "u8",
+ generics: [],
+ typeFilter: -1,
+ },
+ ],
+ typeFilter: 15,
+ },
+ ],
+ foundElems: 1,
+ original: "primitive:[u8]",
+ returned: [],
+ userQuery: "primitive:[u8]",
+ error: null,
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "macro:[u8]",
+ returned: [],
+ userQuery: "macro:[u8]",
+ error: "Invalid search type: primitive `[]` and `macro` both specified",
+ },
+];
diff --git a/tests/rustdoc-js/slice-array.js b/tests/rustdoc-js/slice-array.js
index 1c06566920c..83b9f69d03e 100644
--- a/tests/rustdoc-js/slice-array.js
+++ b/tests/rustdoc-js/slice-array.js
@@ -51,4 +51,23 @@ const EXPECTED = [
{ 'path': 'slice_array', 'name': 'gamma' },
],
},
+ {
+ 'query': '[TraitDog]',
+ 'in_args': [
+ { 'path': 'slice_array', 'name': 'gamma' },
+ { 'path': 'slice_array', 'name': 'epsilon' },
+ ],
+ },
+ {
+ 'query': 'R<[Q]>',
+ 'returned': [
+ { 'path': 'slice_array', 'name': 'bet' },
+ ],
+ },
+ {
+ 'query': 'R<[P]>',
+ 'in_args': [
+ { 'path': 'slice_array', 'name': 'alpha' },
+ ],
+ },
];
diff --git a/tests/rustdoc-js/slice-array.rs b/tests/rustdoc-js/slice-array.rs
index 2523b21cfaa..15ac4294f3d 100644
--- a/tests/rustdoc-js/slice-array.rs
+++ b/tests/rustdoc-js/slice-array.rs
@@ -14,3 +14,5 @@ pub trait TraitCat {}
pub trait TraitDog {}
pub fn gamma(t: [T; 32]) {}
+
+pub fn epsilon(t: &[T]) {}