about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/html/static/js/search.js115
-rw-r--r--src/test/rustdoc-js-std/parser-errors.js139
-rw-r--r--src/test/rustdoc-js-std/parser-filter.js14
-rw-r--r--src/test/rustdoc-js-std/parser-generics.js6
-rw-r--r--src/test/rustdoc-js-std/parser-invalid.js72
-rw-r--r--src/test/rustdoc-js-std/parser-paths.js30
-rw-r--r--src/tools/rustdoc-js/tester.js7
7 files changed, 242 insertions, 141 deletions
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 + "`");
     }
 
     /**
@@ -190,22 +186,6 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     /**
-     * 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 "::".
      *
      * @param {ParserState} parserState
@@ -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]) +
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<f>:foo",
         returned: [],
-        typeFilter: 14,
+        typeFilter: -1,
         userQuery: "macro<f>: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 = ['<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,
     },
     {
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 = ['-> <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: [
-            {
-                name: "",
-                fullPath: [""],
-                pathWithoutLast: [],
-                pathLast: "",
-                generics: [
-                    {
-                        name: "p",
-                        fullPath: ["p"],
-                        pathWithoutLast: [],
-                        pathLast: "p",
-                        generics: [],
-                    },
-                ],
-            },
-            {
-                name: "p2",
-                fullPath: ["p2"],
-                pathWithoutLast: [],
-                pathLast: "p2",
-                generics: [],
-            },
-        ],
-        typeFilter: -1,
-        userQuery: "-> <p> (p2)",
-        error: null,
-    },
-    {
-        args: [
+        elems: [
             {
-                name: "p",
-                fullPath: ["p"],
+                name: "a",
+                fullPath: ["a"],
                 pathWithoutLast: [],
-                pathLast: "p",
+                pathLast: "a",
                 generics: [],
             },
             {
-                name: "p2",
-                fullPath: ["p2"],
+                name: "b",
+                fullPath: ["b"],
                 pathWithoutLast: [],
-                pathLast: "p2",
+                pathLast: "b",
                 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",
+                generics: [],
+            },
+            {
+                name: "b",
+                fullPath: ["b"],
                 pathWithoutLast: [],
-                pathLast: "a b",
+                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::<f>,C'];
+const QUERY = ['A::B', 'A::B,C',  'A::B<f>,C'];
 
 const PARSED = [
     {
@@ -19,25 +19,9 @@ const PARSED = [
     },
     {
         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,
     },
 ];
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 = {};