mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 14:01:51 +00:00
Make query parser more strict and improve display of errors
This commit is contained in:
parent
264064df36
commit
99d552092c
@ -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<QueryElement>} 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 `<p>`, 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 :(<br/>" +
|
||||
"Try on <a href=\"https://duckduckgo.com/?q=" +
|
||||
@ -1552,15 +1561,19 @@ window.initSearch = function(rawSearchIndex) {
|
||||
}
|
||||
crates += `</select>`;
|
||||
}
|
||||
|
||||
var typeFilter = "";
|
||||
if (results.query.typeFilter !== NO_TYPE_FILTER) {
|
||||
typeFilter = " (type: " + escape(results.query.typeFilter) + ")";
|
||||
typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")";
|
||||
}
|
||||
|
||||
var output = `<div id="search-settings">` +
|
||||
`<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` +
|
||||
`${typeFilter}</h1> in ${crates} </div>` +
|
||||
`<div id="titles">` +
|
||||
`${typeFilter}</h1> in ${crates} </div>`;
|
||||
if (results.query.error !== null) {
|
||||
output += `<h3>Query parser error: "${results.query.error}".</h3>`;
|
||||
}
|
||||
output += `<div id="titles">` +
|
||||
makeTabHeader(0, "In Names", ret_others[1]) +
|
||||
makeTabHeader(1, "In Parameters", ret_in_args[1]) +
|
||||
makeTabHeader(2, "In Return Types", ret_returned[1]) +
|
||||
|
@ -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 `{`",
|
||||
},
|
||||
];
|
||||
|
@ -35,18 +35,12 @@ const PARSED = [
|
||||
},
|
||||
{
|
||||
args: [],
|
||||
elems: [{
|
||||
name: "foo",
|
||||
fullPath: ["foo"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "foo",
|
||||
generics: [],
|
||||
}],
|
||||
foundElems: 1,
|
||||
elems: [],
|
||||
foundElems: 0,
|
||||
original: "macro<f>:foo",
|
||||
returned: [],
|
||||
typeFilter: 14,
|
||||
typeFilter: -1,
|
||||
userQuery: "macro<f>:foo",
|
||||
error: null,
|
||||
error: "Unexpected `:`",
|
||||
},
|
||||
];
|
||||
|
@ -1,4 +1,4 @@
|
||||
const QUERY = ['<P>', 'A<B<C<D>, E>', 'p<> u8'];
|
||||
const QUERY = ['<P>', 'A<B<C<D>, E>', 'p<> u8'];
|
||||
|
||||
const PARSED = [
|
||||
{
|
||||
@ -66,10 +66,10 @@ const PARSED = [
|
||||
],
|
||||
}],
|
||||
foundElems: 1,
|
||||
original: 'A<B<C<D>, E>',
|
||||
original: 'A<B<C<D>, E>',
|
||||
returned: [],
|
||||
typeFilter: -1,
|
||||
userQuery: 'a<b<c<d>, e>',
|
||||
userQuery: 'a<b<c<d>, e>',
|
||||
error: null,
|
||||
},
|
||||
{
|
||||
|
@ -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 = ['-> <P> (p2)', '(p -> p2', 'a b', 'a,b(c)'];
|
||||
const QUERY = ['a b', 'a b', 'a,b(c)'];
|
||||
|
||||
const PARSED = [
|
||||
{
|
||||
args: [],
|
||||
elems: [],
|
||||
foundElems: 2,
|
||||
original: "-> <P> (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: "-> <p> (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,
|
||||
},
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
const QUERY = ['A::B', '::A::B', 'A::B::,C', 'A::B::<f>,C'];
|
||||
const QUERY = ['A::B', 'A::B,C', 'A::B<f>,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::<f>,C',
|
||||
original: 'A::B<f>,C',
|
||||
returned: [],
|
||||
typeFilter: -1,
|
||||
userQuery: 'a::b::<f>,c',
|
||||
userQuery: 'a::b<f>,c',
|
||||
error: null,
|
||||
},
|
||||
];
|
||||
|
@ -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 = {};
|
||||
|
Loading…
Reference in New Issue
Block a user