diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 7f21fa8bea6..861bf1a42dd 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -132,17 +132,12 @@ window.initSearch = function(rawSearchIndex) { return "(<\"".indexOf(c) !== -1; } - function isStopCharacter(c) { - return isWhitespace(c) || "),>-=".indexOf(c) !== -1; + function isEndCharacter(c) { + return "),>-".indexOf(c) !== -1; } - function removeEmptyStringsFromArray(arr) { - for (var i = 0, len = arr.length; i < len; ++i) { - if (arr[i] === "") { - arr.splice(i, 1); - i -= 1; - } - } + function isStopCharacter(c) { + return isWhitespace(c) || isEndCharacter(c); } function itemTypeFromName(typename) { @@ -151,7 +146,8 @@ window.initSearch = function(rawSearchIndex) { return i; } } - return NO_TYPE_FILTER; + + throw new Error("Unknown type filter `" + typename + "`"); } /** @@ -189,22 +185,6 @@ window.initSearch = function(rawSearchIndex) { query.literalSearch = true; } - /** - * Increase the parser position as long as the character is a whitespace. This check is - * performed with the `isWhitespace` function. - * - * @param {ParserState} parserState - */ - function skipWhitespaces(parserState) { - while (parserState.pos < parserState.length) { - var c = parserState.userQuery[parserState.pos]; - if (!isWhitespace(c)) { - break; - } - parserState.pos += 1; - } - } - /** * Returns `true` if the current parser position is starting with "::". * @@ -233,7 +213,6 @@ window.initSearch = function(rawSearchIndex) { * @param {Array} generics - List of generics of this query element. */ function createQueryElement(query, parserState, elems, name, generics) { - removeEmptyStringsFromArray(generics); if (name === '*' || (name.length === 0 && generics.length === 0)) { return; } @@ -241,7 +220,20 @@ window.initSearch = function(rawSearchIndex) { throw new Error("You cannot have more than one element if you use quotes"); } var pathSegments = name.split("::"); - removeEmptyStringsFromArray(pathSegments); + if (pathSegments.length > 1) { + for (var i = 0, len = pathSegments.length; i < len; ++i) { + var pathSegment = pathSegments[i]; + + if (pathSegment.length === 0) { + if (i === 0) { + throw new Error("Paths cannot start with `::`"); + } else if (i + 1 === len) { + throw new Error("Paths cannot end with `::`"); + } + throw new Error("Unexpected `::::`"); + } + } + } // In case we only have something like `

`, there is no name but it remains valid. if (pathSegments.length === 0) { pathSegments = [""]; @@ -272,7 +264,6 @@ window.initSearch = function(rawSearchIndex) { start += 1; getStringElem(query, parserState, isInGenerics); end = parserState.pos - 1; - skipWhitespaces(parserState); } else { while (parserState.pos < parserState.length) { var c = parserState.userQuery[parserState.pos]; @@ -289,7 +280,6 @@ window.initSearch = function(rawSearchIndex) { } parserState.pos += 1; end = parserState.pos; - skipWhitespaces(parserState); } } if (parserState.pos < parserState.length && @@ -317,22 +307,36 @@ window.initSearch = function(rawSearchIndex) { * character. */ function getItemsBefore(query, parserState, elems, limit) { + var turns = 0; while (parserState.pos < parserState.length) { var c = parserState.userQuery[parserState.pos]; if (c === limit) { break; - } else if (c === '(' || c === ":") { - // Something weird is going on in here. Ignoring it! + } else if (c === "," && limit !== "" && turns > 0) { parserState.pos += 1; continue; + } else if (c === ":" && isPathStart(parserState)) { + throw new Error("Unexpected `::`: paths cannot start with `::`"); + } else if (c === "(" || c === ":" || isEndCharacter(c)) { + var extra = ""; + if (limit === ">") { + extra = "`<`"; + } else if (limit === ")") { + extra = "`(`"; + } else if (limit === "") { + extra = "`->`"; + } + throw new Error("Unexpected `" + c + "` after " + extra); } var posBefore = parserState.pos; getNextElem(query, parserState, elems, limit === ">"); + turns += 1; if (posBefore === parserState.pos) { parserState.pos += 1; } } - // We skip the "limit". + // We are either at the end of the string or on the "limit" character, let's move forward + // in any case. parserState.pos += 1; } @@ -356,9 +360,13 @@ window.initSearch = function(rawSearchIndex) { break; } else if (c === ":" && parserState.typeFilter === null && - !isPathStart(parserState) && - query.elems.length === 1) + !isPathStart(parserState)) { + if (query.elems.length === 0) { + throw new Error("Expected type filter before `:`"); + } else if (query.elems.length !== 1 || parserState.totalElems !== 1) { + throw new Error("Unexpected `:`"); + } if (query.literalSearch) { throw new Error("You cannot use quotes on type filter"); } @@ -531,6 +539,10 @@ window.initSearch = function(rawSearchIndex) { try { parseInput(query, parserState); + if (parserState.typeFilter !== null) { + var typeFilter = parserState.typeFilter.replace(/^const$/, "constant"); + query.typeFilter = itemTypeFromName(typeFilter); + } } catch (err) { query = newParsedQuery(userQuery); query.error = err.message; @@ -548,10 +560,6 @@ window.initSearch = function(rawSearchIndex) { createQueryElement(query, parserState, query.elems, userQuery, []); query.foundElems += 1; } - if (parserState.typeFilter !== null) { - var typeFilter = parserState.typeFilter.replace(/^const$/, "constant"); - query.typeFilter = itemTypeFromName(typeFilter); - } return query; } @@ -582,9 +590,6 @@ window.initSearch = function(rawSearchIndex) { * @return {ResultsTable} */ function execQuery(parsedQuery, searchWords, filterCrates) { - if (parsedQuery.error !== null) { - createQueryResults([], [], [], parsedQuery); - } var results_others = {}, results_in_args = {}, results_returned = {}; function transformResults(results) { @@ -1267,7 +1272,10 @@ window.initSearch = function(rawSearchIndex) { } } } - innerRunQuery(); + + if (parsedQuery.error === null) { + innerRunQuery(); + } var ret = createQueryResults( sortResults(results_in_args, true), @@ -1275,6 +1283,10 @@ window.initSearch = function(rawSearchIndex) { sortResults(results_others, false), parsedQuery); handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates); + if (parsedQuery.error !== null && ret.others.length !== 0) { + // It means some doc aliases were found so let's "remove" the error! + ret.query.error = null; + } return ret; } @@ -1413,7 +1425,7 @@ window.initSearch = function(rawSearchIndex) { var output = document.createElement("div"); var length = 0; - if (array.length > 0 && query.error === null) { + if (array.length > 0) { output.className = "search-results " + extraClass; array.forEach(function(item) { @@ -1466,10 +1478,7 @@ window.initSearch = function(rawSearchIndex) { link.appendChild(wrapper); output.appendChild(link); }); - } else if (query.error !== null) { - output.className = "search-failed" + extraClass; - output.innerHTML = "Syntax error: " + query.error; - } else { + } else if (query.error === null) { output.className = "search-failed" + extraClass; output.innerHTML = "No results :(
" + "Try on `; } + var typeFilter = ""; if (results.query.typeFilter !== NO_TYPE_FILTER) { - typeFilter = " (type: " + escape(results.query.typeFilter) + ")"; + typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")"; } var output = `

` + `

Results for ${escape(results.query.userQuery)}` + - `${typeFilter}

in ${crates}
` + - `
` + + `${typeFilter} in ${crates}
`; + if (results.query.error !== null) { + output += `

Query parser error: "${results.query.error}".

`; + } + output += `
` + makeTabHeader(0, "In Names", ret_others[1]) + makeTabHeader(1, "In Parameters", ret_in_args[1]) + makeTabHeader(2, "In Return Types", ret_returned[1]) + diff --git a/src/test/rustdoc-js-std/parser-errors.js b/src/test/rustdoc-js-std/parser-errors.js index 887ac38f417..369b8c89a66 100644 --- a/src/test/rustdoc-js-std/parser-errors.js +++ b/src/test/rustdoc-js-std/parser-errors.js @@ -1,4 +1,21 @@ -const QUERY = ['<"P">', '"P" "P"', 'P "P"', '"p" p', '"const": p']; +const QUERY = [ + '<"P">', + '"P" "P"', + 'P "P"', + '"p" p', + '"const": p', + "<:a>", "<::a>", + "((a))", + "->,a", + "(p -> p", + "::a::b", + "a::::b", + "a::b::", + ":a", + "a b:", + "a (b:", + "{:", +]; const PARSED = [ { @@ -51,4 +68,124 @@ const PARSED = [ userQuery: "\"const\": p", error: "You cannot use quotes on type filter", }, + { + args: [], + elems: [], + foundElems: 0, + original: "<:a>", + returned: [], + typeFilter: -1, + userQuery: "<:a>", + error: "Unexpected `:` after `<`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "<::a>", + returned: [], + typeFilter: -1, + userQuery: "<::a>", + error: "Unexpected `::`: paths cannot start with `::`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "((a))", + returned: [], + typeFilter: -1, + userQuery: "((a))", + error: "Unexpected `(` after `(`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "->,a", + returned: [], + typeFilter: -1, + userQuery: "->,a", + error: "Unexpected `,` after `->`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "(p -> p", + returned: [], + typeFilter: -1, + userQuery: "(p -> p", + error: "Unexpected `-` after `(`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "::a::b", + returned: [], + typeFilter: -1, + userQuery: "::a::b", + error: "Paths cannot start with `::`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "a::::b", + returned: [], + typeFilter: -1, + userQuery: "a::::b", + error: "Unexpected `::::`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "a::b::", + returned: [], + typeFilter: -1, + userQuery: "a::b::", + error: "Paths cannot end with `::`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: ":a", + returned: [], + typeFilter: -1, + userQuery: ":a", + error: "Expected type filter before `:`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "a b:", + returned: [], + typeFilter: -1, + userQuery: "a b:", + error: "Unexpected `:`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "a (b:", + returned: [], + typeFilter: -1, + userQuery: "a (b:", + error: "Unexpected `:` after `(`", + }, + { + args: [], + elems: [], + foundElems: 0, + original: "{:", + returned: [], + typeFilter: -1, + userQuery: "{:", + error: "Unknown type filter `{`", + }, ]; diff --git a/src/test/rustdoc-js-std/parser-filter.js b/src/test/rustdoc-js-std/parser-filter.js index 49fa66b5bb3..2b474849318 100644 --- a/src/test/rustdoc-js-std/parser-filter.js +++ b/src/test/rustdoc-js-std/parser-filter.js @@ -35,18 +35,12 @@ const PARSED = [ }, { args: [], - elems: [{ - name: "foo", - fullPath: ["foo"], - pathWithoutLast: [], - pathLast: "foo", - generics: [], - }], - foundElems: 1, + elems: [], + foundElems: 0, original: "macro:foo", returned: [], - typeFilter: 14, + typeFilter: -1, userQuery: "macro:foo", - error: null, + error: "Unexpected `:`", }, ]; diff --git a/src/test/rustdoc-js-std/parser-generics.js b/src/test/rustdoc-js-std/parser-generics.js index 34d33ed4d67..d0a0a904353 100644 --- a/src/test/rustdoc-js-std/parser-generics.js +++ b/src/test/rustdoc-js-std/parser-generics.js @@ -1,4 +1,4 @@ -const QUERY = ['

', 'A, E>', 'p<> u8']; +const QUERY = ['

', 'A, E>', 'p<> u8']; const PARSED = [ { @@ -66,10 +66,10 @@ const PARSED = [ ], }], foundElems: 1, - original: 'A, E>', + original: 'A, E>', returned: [], typeFilter: -1, - userQuery: 'a, e>', + userQuery: 'a, e>', error: null, }, { diff --git a/src/test/rustdoc-js-std/parser-invalid.js b/src/test/rustdoc-js-std/parser-invalid.js index 5e7be3c73b0..024372ef8d5 100644 --- a/src/test/rustdoc-js-std/parser-invalid.js +++ b/src/test/rustdoc-js-std/parser-invalid.js @@ -1,83 +1,57 @@ // This test is mostly to check that the parser still kinda outputs something // (and doesn't enter an infinite loop!) even though the query is completely // invalid. -const QUERY = ['->

(p2)', '(p -> p2', 'a b', 'a,b(c)']; +const QUERY = ['a b', 'a b', 'a,b(c)']; const PARSED = [ { args: [], - elems: [], - foundElems: 2, - original: "->

(p2)", - returned: [ + elems: [ { - name: "", - fullPath: [""], + name: "a", + fullPath: ["a"], pathWithoutLast: [], - pathLast: "", - generics: [ - { - name: "p", - fullPath: ["p"], - pathWithoutLast: [], - pathLast: "p", - generics: [], - }, - ], + pathLast: "a", + generics: [], }, { - name: "p2", - fullPath: ["p2"], + name: "b", + fullPath: ["b"], pathWithoutLast: [], - pathLast: "p2", + pathLast: "b", generics: [], }, ], - typeFilter: -1, - userQuery: "->

(p2)", - error: null, - }, - { - args: [ - { - name: "p", - fullPath: ["p"], - pathWithoutLast: [], - pathLast: "p", - generics: [], - }, - { - name: "p2", - fullPath: ["p2"], - pathWithoutLast: [], - pathLast: "p2", - generics: [], - }, - ], - elems: [], foundElems: 2, - original: "(p -> p2", + original: "a b", returned: [], typeFilter: -1, - userQuery: "(p -> p2", + userQuery: "a b", error: null, }, { args: [], elems: [ { - name: "a b", - fullPath: ["a b"], + name: "a", + fullPath: ["a"], pathWithoutLast: [], - pathLast: "a b", + pathLast: "a", + generics: [], + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", generics: [], }, ], - foundElems: 1, - original: "a b", + foundElems: 2, + original: "a b", returned: [], typeFilter: -1, - userQuery: "a b", + userQuery: "a b", error: null, }, { diff --git a/src/test/rustdoc-js-std/parser-paths.js b/src/test/rustdoc-js-std/parser-paths.js index 1bd3bb61dc9..12b7f922b14 100644 --- a/src/test/rustdoc-js-std/parser-paths.js +++ b/src/test/rustdoc-js-std/parser-paths.js @@ -1,4 +1,4 @@ -const QUERY = ['A::B', '::A::B', 'A::B::,C', 'A::B::,C']; +const QUERY = ['A::B', 'A::B,C', 'A::B,C']; const PARSED = [ { @@ -17,27 +17,11 @@ const PARSED = [ userQuery: "a::b", error: null, }, - { - args: [], - elems: [{ - name: "::a::b", - fullPath: ["a", "b"], - pathWithoutLast: ["a"], - pathLast: "b", - generics: [], - }], - foundElems: 1, - original: '::A::B', - returned: [], - typeFilter: -1, - userQuery: '::a::b', - error: null, - }, { args: [], elems: [ { - name: "a::b::", + name: "a::b", fullPath: ["a", "b"], pathWithoutLast: ["a"], pathLast: "b", @@ -52,17 +36,17 @@ const PARSED = [ }, ], foundElems: 2, - original: 'A::B::,C', + original: 'A::B,C', returned: [], typeFilter: -1, - userQuery: 'a::b::,c', + userQuery: 'a::b,c', error: null, }, { args: [], elems: [ { - name: "a::b::", + name: "a::b", fullPath: ["a", "b"], pathWithoutLast: ["a"], pathLast: "b", @@ -85,10 +69,10 @@ const PARSED = [ }, ], foundElems: 2, - original: 'A::B::,C', + original: 'A::B,C', returned: [], typeFilter: -1, - userQuery: 'a::b::,c', + userQuery: 'a::b,c', error: null, }, ]; diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 4ef08f53dab..934f35f2c41 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -272,10 +272,9 @@ function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) { var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult", "buildIndex", "execQuery", "parseQuery", "createQueryResults", "isWhitespace", "isSpecialStartCharacter", "isStopCharacter", - "removeEmptyStringsFromArray", "parseInput", "getItemsBefore", - "getNextElem", "createQueryElement", "isReturnArrow", "isPathStart", - "skipWhitespaces", "getStringElem", "itemTypeFromName", - "newParsedQuery"]; + "parseInput", "getItemsBefore", "getNextElem", "createQueryElement", + "isReturnArrow", "isPathStart", "getStringElem", "newParsedQuery", + "itemTypeFromName", "isEndCharacter"]; const functions = ["hasOwnPropertyRustdoc", "onEach"]; ALIASES = {};