about summary refs log tree commit diff
path: root/src/librustdoc/html/static
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/static')
-rw-r--r--src/librustdoc/html/static/COPYRIGHT.txt10
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css27
-rw-r--r--src/librustdoc/html/static/fonts/FiraSans-Medium.woffbin186824 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/FiraSans-Regular.woffbin183268 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woffbin677868 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woffbin58444 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woffbin68152 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woffbin68080 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woffbin110552 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woffbin78108 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woffbin103604 -> 0 bytes
-rw-r--r--src/librustdoc/html/static/js/externs.js68
-rw-r--r--src/librustdoc/html/static/js/main.js269
-rw-r--r--src/librustdoc/html/static/js/scrape-examples.js45
-rw-r--r--src/librustdoc/html/static/js/search.js1665
-rw-r--r--src/librustdoc/html/static/js/settings.js15
-rw-r--r--src/librustdoc/html/static/js/source-script.js89
-rw-r--r--src/librustdoc/html/static/js/storage.js47
18 files changed, 1399 insertions, 836 deletions
diff --git a/src/librustdoc/html/static/COPYRIGHT.txt b/src/librustdoc/html/static/COPYRIGHT.txt
index c2629a83f70..34e48134cc3 100644
--- a/src/librustdoc/html/static/COPYRIGHT.txt
+++ b/src/librustdoc/html/static/COPYRIGHT.txt
@@ -2,8 +2,7 @@ These documentation pages include resources by third parties. This copyright
 file applies only to those resources. The following third party resources are
 included, and carry their own copyright notices and license terms:
 
-* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2,
-    FiraSans-Regular.woff, FiraSans-Medium.woff):
+* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2):
 
     Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
     with Reserved Font Name Fira Sans.
@@ -25,9 +24,7 @@ included, and carry their own copyright notices and license terms:
     Licensed under the MIT license (see LICENSE-MIT.txt).
 
 * Source Code Pro (SourceCodePro-Regular.ttf.woff2,
-    SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2,
-    SourceCodePro-Regular.ttf.woff, SourceCodePro-Semibold.ttf.woff,
-    SourceCodePro-It.ttf.woff):
+    SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2):
 
     Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
     with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark
@@ -37,8 +34,7 @@ included, and carry their own copyright notices and license terms:
     See SourceCodePro-LICENSE.txt.
 
 * Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2,
-    SourceSerif4-It.ttf.woff2, SourceSerif4-Regular.ttf.woff,
-    SourceSerif4-Bold.ttf.woff, SourceSerif4-It.ttf.woff):
+    SourceSerif4-It.ttf.woff2):
 
     Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name
     'Source'. All Rights Reserved. Source is a trademark of Adobe in the United
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 48cb0a46ad6..81c12be8e83 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -4,8 +4,7 @@
 	font-style: normal;
 	font-weight: 400;
 	src: local('Fira Sans'),
-		url("FiraSans-Regular.woff2") format("woff2"),
-		url("FiraSans-Regular.woff") format('woff');
+		url("FiraSans-Regular.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -13,8 +12,7 @@
 	font-style: normal;
 	font-weight: 500;
 	src: local('Fira Sans Medium'),
-		url("FiraSans-Medium.woff2") format("woff2"),
-		url("FiraSans-Medium.woff") format('woff');
+		url("FiraSans-Medium.woff2") format("woff2");
 	font-display: swap;
 }
 
@@ -24,8 +22,7 @@
 	font-style: normal;
 	font-weight: 400;
 	src: local('Source Serif 4'),
-		url("SourceSerif4-Regular.ttf.woff2") format("woff2"),
-		url("SourceSerif4-Regular.ttf.woff") format("woff");
+		url("SourceSerif4-Regular.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -33,8 +30,7 @@
 	font-style: italic;
 	font-weight: 400;
 	src: local('Source Serif 4 Italic'),
-		url("SourceSerif4-It.ttf.woff2") format("woff2"),
-		url("SourceSerif4-It.ttf.woff") format("woff");
+		url("SourceSerif4-It.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
@@ -42,8 +38,7 @@
 	font-style: normal;
 	font-weight: 700;
 	src: local('Source Serif 4 Bold'),
-		url("SourceSerif4-Bold.ttf.woff2") format("woff2"),
-		url("SourceSerif4-Bold.ttf.woff") format("woff");
+		url("SourceSerif4-Bold.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 
@@ -54,32 +49,28 @@
 	font-weight: 400;
 	/* Avoid using locally installed font because bad versions are in circulation:
 	 * see https://github.com/rust-lang/rust/issues/24355 */
-	src: url("SourceCodePro-Regular.ttf.woff2") format("woff2"),
-		url("SourceCodePro-Regular.ttf.woff") format("woff");
+	src: url("SourceCodePro-Regular.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
 	font-family: 'Source Code Pro';
 	font-style: italic;
 	font-weight: 400;
-	src: url("SourceCodePro-It.ttf.woff2") format("woff2"),
-		url("SourceCodePro-It.ttf.woff") format("woff");
+	src: url("SourceCodePro-It.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 @font-face {
 	font-family: 'Source Code Pro';
 	font-style: normal;
 	font-weight: 600;
-	src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2"),
-		url("SourceCodePro-Semibold.ttf.woff") format("woff");
+	src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2");
 	font-display: swap;
 }
 
 /* Avoid using legacy CJK serif fonts in Windows like Batang. */
 @font-face {
 	font-family: 'NanumBarunGothic';
-	src: url("NanumBarunGothic.ttf.woff2") format("woff2"),
-		url("NanumBarunGothic.ttf.woff") format("woff");
+	src: url("NanumBarunGothic.ttf.woff2") format("woff2");
 	font-display: swap;
 	unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF;
 }
diff --git a/src/librustdoc/html/static/fonts/FiraSans-Medium.woff b/src/librustdoc/html/static/fonts/FiraSans-Medium.woff
deleted file mode 100644
index 7d742c5fb7d..00000000000
--- a/src/librustdoc/html/static/fonts/FiraSans-Medium.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/FiraSans-Regular.woff b/src/librustdoc/html/static/fonts/FiraSans-Regular.woff
deleted file mode 100644
index d8e0363f4e1..00000000000
--- a/src/librustdoc/html/static/fonts/FiraSans-Regular.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff b/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff
deleted file mode 100644
index fb063e8fb7d..00000000000
--- a/src/librustdoc/html/static/fonts/NanumBarunGothic.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff b/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff
deleted file mode 100644
index 8d68f2febdd..00000000000
--- a/src/librustdoc/html/static/fonts/SourceCodePro-It.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff b/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff
deleted file mode 100644
index 7be076e1fca..00000000000
--- a/src/librustdoc/html/static/fonts/SourceCodePro-Regular.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff b/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff
deleted file mode 100644
index 61bc67b8025..00000000000
--- a/src/librustdoc/html/static/fonts/SourceCodePro-Semibold.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff b/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff
deleted file mode 100644
index 8ad41888e6e..00000000000
--- a/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff b/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff
deleted file mode 100644
index 2a34b5c42a8..00000000000
--- a/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff b/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff
deleted file mode 100644
index 45a5521ab0c..00000000000
--- a/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff
+++ /dev/null
Binary files differdiff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index 629f90728d2..de881dbd081 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -1,20 +1,47 @@
 // This file contains type definitions that are processed by the Closure Compiler but are
 // not put into the JavaScript we include as part of the documentation. It is used for
 // type checking. See README.md in this directory for more info.
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 
 /* eslint-disable */
-var searchState;
+let searchState;
 function initSearch(searchIndex){}
 
 /**
  * @typedef {{
- *   raw: string,
- *   query: string,
- *   type: string,
- *   id: string,
+ *     name: string,
+ *     fullPath: Array<string>,
+ *     pathWithoutLast: Array<string>,
+ *     pathLast: string,
+ *     generics: Array<QueryElement>,
  * }}
  */
-var ParsedQuery;
+let QueryElement;
+
+/**
+ * @typedef {{
+ *      pos: number,
+ *      totalElems: number,
+ *      typeFilter: (null|string),
+ *      userQuery: string,
+ * }}
+ */
+let ParserState;
+
+/**
+ * @typedef {{
+ *     original: string,
+ *     userQuery: string,
+ *     typeFilter: number,
+ *     elems: Array<QueryElement>,
+ *     args: Array<QueryElement>,
+ *     returned: Array<QueryElement>,
+ *     foundElems: number,
+ * }}
+ */
+let ParsedQuery;
 
 /**
  * @typedef {{
@@ -29,4 +56,31 @@ var ParsedQuery;
  *    type: (Array<?>|null)
  * }}
  */
-var Row;
+let Row;
+
+/**
+ * @typedef {{
+ *    in_args: Array<Object>,
+ *    returned: Array<Object>,
+ *    others: Array<Object>,
+ *    query: ParsedQuery,
+ * }}
+ */
+let ResultsTable;
+
+/**
+ * @typedef {{
+ *     desc: string,
+ *     displayPath: string,
+ *     fullPath: string,
+ *     href: string,
+ *     id: number,
+ *     lev: number,
+ *     name: string,
+ *     normalizedName: string,
+ *     parent: (Object|undefined),
+ *     path: string,
+ *     ty: number,
+ * }}
+ */
+let Results;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 90592335d5d..9e5de9a843a 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1,3 +1,6 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 // Local js definitions:
 /* global addClass, getSettingValue, hasClass, searchState */
 /* global onEach, onEachLazy, removeClass */
@@ -11,7 +14,7 @@ if (!String.prototype.startsWith) {
 }
 if (!String.prototype.endsWith) {
     String.prototype.endsWith = function(suffix, length) {
-        var l = length || this.length;
+        const l = length || this.length;
         return this.indexOf(suffix, l - suffix.length) !== -1;
     };
 }
@@ -40,7 +43,7 @@ if (!DOMTokenList.prototype.remove) {
 // Get a value from the rustdoc-vars div, which is used to convey data from
 // Rust to the JS. If there is no such element, return null.
 function getVar(name) {
-    var el = document.getElementById("rustdoc-vars");
+    const el = document.getElementById("rustdoc-vars");
     if (el) {
         return el.attributes["data-" + name].value;
     } else {
@@ -59,7 +62,7 @@ function resourcePath(basename, extension) {
     window.currentCrate = getVar("current-crate");
     window.searchJS =  resourcePath("search", ".js");
     window.searchIndexJS = resourcePath("search-index", ".js");
-    var sidebarVars = document.getElementById("sidebar-vars");
+    const sidebarVars = document.getElementById("sidebar-vars");
     if (sidebarVars) {
         window.sidebarCurrent = {
             name: sidebarVars.attributes["data-name"].value,
@@ -68,8 +71,8 @@ function resourcePath(basename, extension) {
         };
         // FIXME: It would be nicer to generate this text content directly in HTML,
         // but with the current code it's hard to get the right information in the right place.
-        var mobileLocationTitle = document.querySelector(".mobile-topbar h2.location");
-        var locationTitle = document.querySelector(".sidebar h2.location");
+        const mobileLocationTitle = document.querySelector(".mobile-topbar h2.location");
+        const locationTitle = document.querySelector(".sidebar h2.location");
         if (mobileLocationTitle && locationTitle) {
             mobileLocationTitle.innerHTML = locationTitle.innerHTML;
         }
@@ -91,16 +94,16 @@ function getVirtualKey(ev) {
         return ev.key;
     }
 
-    var c = ev.charCode || ev.keyCode;
+    const c = ev.charCode || ev.keyCode;
     if (c == 27) {
         return "Escape";
     }
     return String.fromCharCode(c);
 }
 
-var THEME_PICKER_ELEMENT_ID = "theme-picker";
-var THEMES_ELEMENT_ID = "theme-choices";
-var MAIN_ID = "main-content";
+const THEME_PICKER_ELEMENT_ID = "theme-picker";
+const THEMES_ELEMENT_ID = "theme-choices";
+const MAIN_ID = "main-content";
 
 function getThemesElement() {
     return document.getElementById(THEMES_ELEMENT_ID);
@@ -116,8 +119,8 @@ function getNakedUrl() {
 }
 
 function showThemeButtonState() {
-    var themePicker = getThemePickerElement();
-    var themeChoices = getThemesElement();
+    const themePicker = getThemePickerElement();
+    const themeChoices = getThemesElement();
 
     themeChoices.style.display = "block";
     themePicker.style.borderBottomRightRadius = "0";
@@ -125,8 +128,8 @@ function showThemeButtonState() {
 }
 
 function hideThemeButtonState() {
-    var themePicker = getThemePickerElement();
-    var themeChoices = getThemesElement();
+    const themePicker = getThemePickerElement();
+    const themeChoices = getThemesElement();
 
     themeChoices.style.display = "none";
     themePicker.style.borderBottomRightRadius = "3px";
@@ -138,9 +141,9 @@ function hideThemeButtonState() {
     if (!document.location.href.startsWith("file:///")) {
         return;
     }
-    var themeChoices = getThemesElement();
-    var themePicker = getThemePickerElement();
-    var availableThemes = getVar("themes").split(",");
+    const themeChoices = getThemesElement();
+    const themePicker = getThemePickerElement();
+    const availableThemes = getVar("themes").split(",");
 
     removeClass(themeChoices.parentElement, "hidden");
 
@@ -153,8 +156,8 @@ function hideThemeButtonState() {
     }
 
     function handleThemeButtonsBlur(e) {
-        var active = document.activeElement;
-        var related = e.relatedTarget;
+        const active = document.activeElement;
+        const related = e.relatedTarget;
 
         if (active.id !== THEME_PICKER_ELEMENT_ID &&
             (!active.parentNode || active.parentNode.id !== THEMES_ELEMENT_ID) &&
@@ -168,7 +171,7 @@ function hideThemeButtonState() {
     themePicker.onclick = switchThemeButtonState;
     themePicker.onblur = handleThemeButtonsBlur;
     availableThemes.forEach(function(item) {
-        var but = document.createElement("button");
+        const but = document.createElement("button");
         but.textContent = item;
         but.onclick = function() {
             switchTheme(window.currentTheme, window.mainTheme, item, true);
@@ -236,10 +239,10 @@ function hideThemeButtonState() {
             }
         },
         getQueryStringParams: function() {
-            var params = {};
+            const params = {};
             window.location.search.substring(1).split("&").
                 map(function(s) {
-                    var pair = s.split("=");
+                    const pair = s.split("=");
                     params[decodeURIComponent(pair[0])] =
                         typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
                 });
@@ -249,17 +252,17 @@ function hideThemeButtonState() {
             return window.history && typeof window.history.pushState === "function";
         },
         setup: function() {
-            var search_input = searchState.input;
+            const search_input = searchState.input;
             if (!searchState.input) {
                 return;
             }
             function loadScript(url) {
-                var script = document.createElement('script');
+                const script = document.createElement('script');
                 script.src = url;
                 document.head.append(script);
             }
 
-            var searchLoaded = false;
+            let searchLoaded = false;
             function loadSearch() {
                 if (!searchLoaded) {
                     searchLoaded = true;
@@ -278,9 +281,9 @@ function hideThemeButtonState() {
                 loadSearch();
             }
 
-            var params = searchState.getQueryStringParams();
+            const params = searchState.getQueryStringParams();
             if (params.search !== undefined) {
-                var search = searchState.outputElement();
+                const search = searchState.outputElement();
                 search.innerHTML = "<h3 class=\"search-loading\">" +
                     searchState.loadingText + "</h3>";
                 searchState.showResults(search);
@@ -291,7 +294,7 @@ function hideThemeButtonState() {
 
     function getPageId() {
         if (window.location.hash) {
-            var tmp = window.location.hash.replace(/^#/, "");
+            const tmp = window.location.hash.replace(/^#/, "");
             if (tmp.length > 0) {
                 return tmp;
             }
@@ -299,18 +302,18 @@ function hideThemeButtonState() {
         return null;
     }
 
-    var toggleAllDocsId = "toggle-all-docs";
-    var main = document.getElementById(MAIN_ID);
-    var savedHash = "";
+    const toggleAllDocsId = "toggle-all-docs";
+    const main = document.getElementById(MAIN_ID);
+    let savedHash = "";
 
     function handleHashes(ev) {
-        var elem;
-        var search = searchState.outputElement();
+        let elem;
+        const search = searchState.outputElement();
         if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
             // This block occurs when clicking on an element in the navbar while
             // in a search.
             searchState.hideResults(search);
-            var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
+            const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
             if (searchState.browserSupportsHistoryApi()) {
                 // `window.location.search`` contains all the query parameters, not just `search`.
                 history.replaceState(null, "",
@@ -333,7 +336,7 @@ function hideThemeButtonState() {
 
     function onHashChange(ev) {
         // If we're in mobile mode, we should hide the sidebar in any case.
-        var sidebar = document.getElementsByClassName("sidebar")[0];
+        const sidebar = document.getElementsByClassName("sidebar")[0];
         removeClass(sidebar, "shown");
         handleHashes(ev);
     }
@@ -386,8 +389,8 @@ function hideThemeButtonState() {
     }
 
     function handleEscape(ev) {
-        var help = getHelpElement(false);
-        var search = searchState.outputElement();
+        const help = getHelpElement(false);
+        const search = searchState.outputElement();
         if (help && !hasClass(help, "hidden")) {
             displayHelp(false, ev, help);
         } else if (search && !hasClass(search, "hidden")) {
@@ -399,13 +402,15 @@ function hideThemeButtonState() {
         hideThemeButtonState();
     }
 
-    var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
+    const disableShortcuts = getSettingValue("disable-shortcuts") === "true";
     function handleShortcut(ev) {
         // Don't interfere with browser shortcuts
         if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
             return;
         }
 
+        let themePicker;
+
         if (document.activeElement.tagName === "INPUT") {
             switch (getVirtualKey(ev)) {
             case "Escape":
@@ -439,7 +444,7 @@ function hideThemeButtonState() {
             case "T":
                 displayHelp(false, ev);
                 ev.preventDefault();
-                var themePicker = getThemePickerElement();
+                themePicker = getThemePickerElement();
                 themePicker.click();
                 themePicker.focus();
                 break;
@@ -453,8 +458,8 @@ function hideThemeButtonState() {
     }
 
     function handleThemeKeyDown(ev) {
-        var active = document.activeElement;
-        var themes = getThemesElement();
+        const active = document.activeElement;
+        const themes = getThemesElement();
         switch (getVirtualKey(ev)) {
         case "ArrowUp":
             ev.preventDefault();
@@ -499,36 +504,11 @@ function hideThemeButtonState() {
     document.addEventListener("keypress", handleShortcut);
     document.addEventListener("keydown", handleShortcut);
 
-    (function() {
-        var x = document.getElementsByClassName("version-selector");
-        if (x.length > 0) {
-            x[0].onchange = function() {
-                var i, match,
-                    url = document.location.href,
-                    stripped = "",
-                    len = window.rootPath.match(/\.\.\//g).length + 1;
-
-                for (i = 0; i < len; ++i) {
-                    match = url.match(/\/[^/]*$/);
-                    if (i < len - 1) {
-                        stripped = match[0] + stripped;
-                    }
-                    url = url.substring(0, url.length - match[0].length);
-                }
-
-                var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
-                url += "/" + selectedVersion + stripped;
-
-                document.location.href = url;
-            };
-        }
-    }());
-
     // delayed sidebar rendering.
     window.initSidebarItems = function(items) {
-        var sidebar = document.getElementsByClassName("sidebar-elems")[0];
-        var others;
-        var current = window.sidebarCurrent;
+        const sidebar = document.getElementsByClassName("sidebar-elems")[0];
+        let others;
+        const current = window.sidebarCurrent;
 
         function addSidebarCrates(crates) {
             if (!hasClass(document.body, "crate")) {
@@ -536,23 +516,23 @@ function hideThemeButtonState() {
                 return;
             }
             // Draw a convenient sidebar of known crates if we have a listing
-            var div = document.createElement("div");
+            const div = document.createElement("div");
             div.className = "block crate";
             div.innerHTML = "<h3>Crates</h3>";
-            var ul = document.createElement("ul");
+            const ul = document.createElement("ul");
             div.appendChild(ul);
 
-            for (var i = 0; i < crates.length; ++i) {
-                var klass = "crate";
-                if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
+            for (const crate of crates) {
+                let klass = "crate";
+                if (window.rootPath !== "./" && crate === window.currentCrate) {
                     klass += " current";
                 }
-                var link = document.createElement("a");
-                link.href = window.rootPath + crates[i] + "/index.html";
+                const link = document.createElement("a");
+                link.href = window.rootPath + crate + "/index.html";
                 link.className = klass;
-                link.textContent = crates[i];
+                link.textContent = crate;
 
-                var li = document.createElement("li");
+                const li = document.createElement("li");
                 li.appendChild(link);
                 ul.appendChild(li);
             }
@@ -568,39 +548,38 @@ function hideThemeButtonState() {
          *                          "Modules", or "Macros".
          */
         function block(shortty, id, longty) {
-            var filtered = items[shortty];
+            const filtered = items[shortty];
             if (!filtered) {
                 return;
             }
 
-            var div = document.createElement("div");
+            const div = document.createElement("div");
             div.className = "block " + shortty;
-            var h3 = document.createElement("h3");
+            const h3 = document.createElement("h3");
             h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
             div.appendChild(h3);
-            var ul = document.createElement("ul");
+            const ul = document.createElement("ul");
 
-            for (var i = 0, len = filtered.length; i < len; ++i) {
-                var item = filtered[i];
-                var name = item[0];
-                var desc = item[1]; // can be null
+            for (const item of filtered) {
+                const name = item[0];
+                const desc = item[1]; // can be null
 
-                var klass = shortty;
+                let klass = shortty;
                 if (name === current.name && shortty === current.ty) {
                     klass += " current";
                 }
-                var path;
+                let path;
                 if (shortty === "mod") {
                     path = name + "/index.html";
                 } else {
                     path = shortty + "." + name + ".html";
                 }
-                var link = document.createElement("a");
+                const link = document.createElement("a");
                 link.href = current.relpath + path;
                 link.title = desc;
                 link.className = klass;
                 link.textContent = name;
-                var li = document.createElement("li");
+                const li = document.createElement("li");
                 li.appendChild(link);
                 ul.appendChild(li);
             }
@@ -613,7 +592,7 @@ function hideThemeButtonState() {
             others.className = "others";
             sidebar.appendChild(others);
 
-            var isModule = hasClass(document.body, "mod");
+            const isModule = hasClass(document.body, "mod");
             if (!isModule) {
                 block("primitive", "primitives", "Primitive Types");
                 block("mod", "modules", "Modules");
@@ -638,8 +617,9 @@ function hideThemeButtonState() {
     };
 
     window.register_implementors = function(imp) {
-        var implementors = document.getElementById("implementors-list");
-        var synthetic_implementors = document.getElementById("synthetic-implementors-list");
+        const implementors = document.getElementById("implementors-list");
+        const synthetic_implementors = document.getElementById("synthetic-implementors-list");
+        const inlined_types = new Set();
 
         if (synthetic_implementors) {
             // This `inlined_types` variable is used to avoid having the same implementation
@@ -647,9 +627,8 @@ function hideThemeButtonState() {
             //
             // By the way, this is only used by and useful for traits implemented automatically
             // (like "Send" and "Sync").
-            var inlined_types = new Set();
             onEachLazy(synthetic_implementors.getElementsByClassName("impl"), function(el) {
-                var aliases = el.getAttribute("data-aliases");
+                const aliases = el.getAttribute("data-aliases");
                 if (!aliases) {
                     return;
                 }
@@ -659,48 +638,48 @@ function hideThemeButtonState() {
             });
         }
 
-        var currentNbImpls = implementors.getElementsByClassName("impl").length;
-        var traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
-        var baseIdName = "impl-" + traitName + "-";
-        var libs = Object.getOwnPropertyNames(imp);
-        for (var i = 0, llength = libs.length; i < llength; ++i) {
-            if (libs[i] === window.currentCrate) { continue; }
-            var structs = imp[libs[i]];
+        let currentNbImpls = implementors.getElementsByClassName("impl").length;
+        const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
+        const baseIdName = "impl-" + traitName + "-";
+        const libs = Object.getOwnPropertyNames(imp);
+        for (const lib of libs) {
+            if (lib === window.currentCrate) {
+                continue;
+            }
+            const structs = imp[lib];
 
             struct_loop:
-            for (var j = 0, slength = structs.length; j < slength; ++j) {
-                var struct = structs[j];
-
-                var list = struct.synthetic ? synthetic_implementors : implementors;
+            for (const struct of structs) {
+                const list = struct.synthetic ? synthetic_implementors : implementors;
 
                 if (struct.synthetic) {
-                    for (var k = 0, stlength = struct.types.length; k < stlength; k++) {
-                        if (inlined_types.has(struct.types[k])) {
+                    for (const struct_type of struct.types) {
+                        if (inlined_types.has(struct_type)) {
                             continue struct_loop;
                         }
-                        inlined_types.add(struct.types[k]);
+                        inlined_types.add(struct_type);
                     }
                 }
 
-                var code = document.createElement("h3");
+                const code = document.createElement("h3");
                 code.innerHTML = struct.text;
                 addClass(code, "code-header");
                 addClass(code, "in-band");
 
                 onEachLazy(code.getElementsByTagName("a"), function(elem) {
-                    var href = elem.getAttribute("href");
+                    const href = elem.getAttribute("href");
 
                     if (href && href.indexOf("http") !== 0) {
                         elem.setAttribute("href", window.rootPath + href);
                     }
                 });
 
-                var currentId = baseIdName + currentNbImpls;
-                var anchor = document.createElement("a");
+                const currentId = baseIdName + currentNbImpls;
+                const anchor = document.createElement("a");
                 anchor.href = "#" + currentId;
                 addClass(anchor, "anchor");
 
-                var display = document.createElement("div");
+                const display = document.createElement("div");
                 display.id = currentId;
                 addClass(display, "impl");
                 display.appendChild(anchor);
@@ -725,11 +704,11 @@ function hideThemeButtonState() {
     }
 
     function toggleAllDocs() {
-        var innerToggle = document.getElementById(toggleAllDocsId);
+        const innerToggle = document.getElementById(toggleAllDocsId);
         if (!innerToggle) {
             return;
         }
-        var sectionIsCollapsed = false;
+        let sectionIsCollapsed = false;
         if (hasClass(innerToggle, "will-expand")) {
             removeClass(innerToggle, "will-expand");
             onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
@@ -759,17 +738,17 @@ function hideThemeButtonState() {
     }
 
     (function() {
-        var toggles = document.getElementById(toggleAllDocsId);
+        const toggles = document.getElementById(toggleAllDocsId);
         if (toggles) {
             toggles.onclick = toggleAllDocs;
         }
 
-        var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
-        var hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
-        var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
+        const hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
+        const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
+        const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
 
         function setImplementorsTogglesOpen(id, open) {
-            var list = document.getElementById(id);
+            const list = document.getElementById(id);
             if (list !== null) {
                 onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) {
                     e.open = open;
@@ -792,7 +771,7 @@ function hideThemeButtonState() {
 
         });
 
-        var pageId = getPageId();
+        const pageId = getPageId();
         if (pageId !== null) {
             expandSection(pageId);
         }
@@ -800,15 +779,15 @@ function hideThemeButtonState() {
 
     (function() {
         // To avoid checking on "rustdoc-line-numbers" value on every loop...
-        var lineNumbersFunc = function() {};
+        let lineNumbersFunc = function() {};
         if (getSettingValue("line-numbers") === "true") {
             lineNumbersFunc = function(x) {
-                var count = x.textContent.split("\n").length;
-                var elems = [];
-                for (var i = 0; i < count; ++i) {
+                const count = x.textContent.split("\n").length;
+                const elems = [];
+                for (let i = 0; i < count; ++i) {
                     elems.push(i + 1);
                 }
-                var node = document.createElement("pre");
+                const node = document.createElement("pre");
                 addClass(node, "line-number");
                 node.innerHTML = elems.join("\n");
                 x.parentNode.insertBefore(node, x);
@@ -835,12 +814,12 @@ function hideThemeButtonState() {
     }());
 
     function hideSidebar() {
-        var sidebar = document.getElementsByClassName("sidebar")[0];
+        const sidebar = document.getElementsByClassName("sidebar")[0];
         removeClass(sidebar, "shown");
     }
 
     function handleClick(id, f) {
-        var elem = document.getElementById(id);
+        const elem = document.getElementById(id);
         if (elem) {
             elem.addEventListener("click", f);
         }
@@ -879,10 +858,10 @@ function hideThemeButtonState() {
         };
     });
 
-    var sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0];
+    const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0];
     if (sidebar_menu_toggle) {
         sidebar_menu_toggle.addEventListener("click", function() {
-            var sidebar = document.getElementsByClassName("sidebar")[0];
+            const sidebar = document.getElementsByClassName("sidebar")[0];
             if (!hasClass(sidebar, "shown")) {
                 addClass(sidebar, "shown");
             } else {
@@ -891,8 +870,8 @@ function hideThemeButtonState() {
         });
     }
 
-    var buildHelperPopup = function() {
-        var popup = document.createElement("aside");
+    let buildHelperPopup = function() {
+        const popup = document.createElement("aside");
         addClass(popup, "hidden");
         popup.id = "help";
 
@@ -903,13 +882,13 @@ function hideThemeButtonState() {
             }
         });
 
-        var book_info = document.createElement("span");
+        const book_info = document.createElement("span");
         book_info.className = "top";
         book_info.innerHTML = "You can find more information in \
             <a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>.";
 
-        var container = document.createElement("div");
-        var shortcuts = [
+        const container = document.createElement("div");
+        const shortcuts = [
             ["?", "Show this help dialog"],
             ["S", "Focus the search field"],
             ["T", "Focus the theme picker menu"],
@@ -927,11 +906,11 @@ function hideThemeButtonState() {
                     })
                     .join("") + "</dt><dd>" + x[1] + "</dd>";
         }).join("");
-        var div_shortcuts = document.createElement("div");
+        const div_shortcuts = document.createElement("div");
         addClass(div_shortcuts, "shortcuts");
         div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";
 
-        var infos = [
+        const infos = [
             "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
              restrict the search to a given item kind.",
             "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
@@ -947,7 +926,7 @@ function hideThemeButtonState() {
         ].map(function(x) {
             return "<p>" + x + "</p>";
         }).join("");
-        var div_infos = document.createElement("div");
+        const div_infos = document.createElement("div");
         addClass(div_infos, "infos");
         div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos;
 
@@ -955,9 +934,9 @@ function hideThemeButtonState() {
         container.appendChild(div_shortcuts);
         container.appendChild(div_infos);
 
-        var rustdoc_version = document.createElement("span");
+        const rustdoc_version = document.createElement("span");
         rustdoc_version.className = "bottom";
-        var rustdoc_version_code = document.createElement("code");
+        const rustdoc_version_code = document.createElement("code");
         rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version");
         rustdoc_version.appendChild(rustdoc_version_code);
 
@@ -975,11 +954,11 @@ function hideThemeButtonState() {
 }());
 
 (function () {
-    var reset_button_timeout = null;
+    let reset_button_timeout = null;
 
     window.copy_path = function(but) {
-        var parent = but.parentElement;
-        var path = [];
+        const parent = but.parentElement;
+        const path = [];
 
         onEach(parent.childNodes, function(child) {
             if (child.tagName === 'A') {
@@ -987,7 +966,7 @@ function hideThemeButtonState() {
             }
         });
 
-        var el = document.createElement('textarea');
+        const el = document.createElement('textarea');
         el.value = path.join('::');
         el.setAttribute('readonly', '');
         // To not make it appear on the screen.
@@ -1002,7 +981,7 @@ function hideThemeButtonState() {
         // There is always one children, but multiple childNodes.
         but.children[0].style.display = 'none';
 
-        var tmp;
+        let tmp;
         if (but.childNodes.length < 2) {
             tmp = document.createTextNode('✓');
             but.appendChild(tmp);
diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js
index a28fb461729..865ed7190f3 100644
--- a/src/librustdoc/html/static/js/scrape-examples.js
+++ b/src/librustdoc/html/static/js/scrape-examples.js
@@ -1,4 +1,7 @@
-/* global addClass, hasClass, removeClass, onEach */
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+/* global addClass, hasClass, removeClass, onEachLazy */
 
 (function () {
     // Number of lines shown when code viewer is not expanded
@@ -6,19 +9,19 @@
 
     // Scroll code block to the given code location
     function scrollToLoc(elt, loc) {
-        var lines = elt.querySelector('.line-numbers');
-        var scrollOffset;
+        const lines = elt.querySelector('.line-numbers');
+        let scrollOffset;
 
         // If the block is greater than the size of the viewer,
         // then scroll to the top of the block. Otherwise scroll
         // to the middle of the block.
         if (loc[1] - loc[0] > MAX_LINES) {
-            var line = Math.max(0, loc[0] - 1);
+            const line = Math.max(0, loc[0] - 1);
             scrollOffset = lines.children[line].offsetTop;
         } else {
-            var wrapper = elt.querySelector(".code-wrapper");
-            var halfHeight = wrapper.offsetHeight / 2;
-            var offsetMid = (lines.children[loc[0]].offsetTop
+            const wrapper = elt.querySelector(".code-wrapper");
+            const halfHeight = wrapper.offsetHeight / 2;
+            const offsetMid = (lines.children[loc[0]].offsetTop
                              + lines.children[loc[1]].offsetTop) / 2;
             scrollOffset = offsetMid - halfHeight;
         }
@@ -28,21 +31,21 @@
     }
 
     function updateScrapedExample(example) {
-        var locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
-        var locIndex = 0;
-        var highlights = example.querySelectorAll('.highlight');
-        var link = example.querySelector('.scraped-example-title a');
+        const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
+        let locIndex = 0;
+        const highlights = Array.prototype.slice.call(example.querySelectorAll('.highlight'));
+        const link = example.querySelector('.scraped-example-title a');
 
         if (locs.length > 1) {
             // Toggle through list of examples in a given file
-            var onChangeLoc = function(changeIndex) {
+            const onChangeLoc = function(changeIndex) {
                 removeClass(highlights[locIndex], 'focus');
                 changeIndex();
                 scrollToLoc(example, locs[locIndex][0]);
                 addClass(highlights[locIndex], 'focus');
 
-                var url = locs[locIndex][1];
-                var title = locs[locIndex][2];
+                const url = locs[locIndex][1];
+                const title = locs[locIndex][2];
 
                 link.href = url;
                 link.innerHTML = title;
@@ -63,7 +66,7 @@
                 });
         }
 
-        var expandButton = example.querySelector('.expand');
+        const expandButton = example.querySelector('.expand');
         if (expandButton) {
             expandButton.addEventListener('click', function () {
                 if (hasClass(example, "expanded")) {
@@ -79,24 +82,24 @@
         scrollToLoc(example, locs[0][0]);
     }
 
-    var firstExamples = document.querySelectorAll('.scraped-example-list > .scraped-example');
-    onEach(firstExamples, updateScrapedExample);
-    onEach(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
+    const firstExamples = document.querySelectorAll('.scraped-example-list > .scraped-example');
+    onEachLazy(firstExamples, updateScrapedExample);
+    onEachLazy(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
         // Allow users to click the left border of the <details> section to close it,
         // since the section can be large and finding the [+] button is annoying.
-        toggle.querySelectorAll('.toggle-line, .hide-more').forEach(button => {
+        onEachLazy(toggle.querySelectorAll('.toggle-line, .hide-more'), button => {
             button.addEventListener('click', function() {
                 toggle.open = false;
             });
         });
 
-        var moreExamples = toggle.querySelectorAll('.scraped-example');
+        const moreExamples = toggle.querySelectorAll('.scraped-example');
         toggle.querySelector('summary').addEventListener('click', function() {
             // Wrapping in setTimeout ensures the update happens after the elements are actually
             // visible. This is necessary since updateScrapedExample calls scrollToLoc which
             // depends on offsetHeight, a property that requires an element to be visible to
             // compute correctly.
-            setTimeout(function() { onEach(moreExamples, updateScrapedExample); });
+            setTimeout(function() { onEachLazy(moreExamples, updateScrapedExample); });
         }, {once: true});
     });
 })();
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index ab52304491a..a6f7dd74af6 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1,10 +1,13 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 /* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
 /* global onEachLazy, removeClass, searchState, hasClass */
 
 (function() {
 // This mapping table should match the discriminants of
 // `rustdoc::formats::item_type::ItemType` type in Rust.
-var itemTypes = [
+const itemTypes = [
     "mod",
     "externcrate",
     "import",
@@ -34,15 +37,15 @@ var itemTypes = [
 ];
 
 // used for special search precedence
-var TY_PRIMITIVE = itemTypes.indexOf("primitive");
-var TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_PRIMITIVE = itemTypes.indexOf("primitive");
+const TY_KEYWORD = itemTypes.indexOf("keyword");
 
 // In the search display, allows to switch between tabs.
 function printTab(nb) {
     if (nb === 0 || nb === 1 || nb === 2) {
         searchState.currentTab = nb;
     }
-    var nb_copy = nb;
+    let nb_copy = nb;
     onEachLazy(document.getElementById("titles").childNodes, function(elem) {
         if (nb_copy === 0) {
             addClass(elem, "selected");
@@ -61,15 +64,6 @@ function printTab(nb) {
     });
 }
 
-function removeEmptyStringsFromArray(x) {
-    for (var i = 0, len = x.length; i < len; ++i) {
-        if (x[i] === "") {
-            x.splice(i, 1);
-            i -= 1;
-        }
-    }
-}
-
 /**
  * A function to compute the Levenshtein distance between two strings
  * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
@@ -77,14 +71,15 @@ function removeEmptyStringsFromArray(x) {
  * This code is an unmodified version of the code written by Marco de Wit
  * and was found at https://stackoverflow.com/a/18514751/745719
  */
-var levenshtein_row2 = [];
+const levenshtein_row2 = [];
 function levenshtein(s1, s2) {
     if (s1 === s2) {
         return 0;
     }
-    var s1_len = s1.length, s2_len = s2.length;
+    const s1_len = s1.length, s2_len = s2.length;
     if (s1_len && s2_len) {
-        var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
+        let i1 = 0, i2 = 0, a, b, c, c2;
+        const row = levenshtein_row2;
         while (i1 < s1_len) {
             row[i1] = ++i1;
         }
@@ -106,24 +101,20 @@ function levenshtein(s1, s2) {
 }
 
 window.initSearch = function(rawSearchIndex) {
-    var MAX_LEV_DISTANCE = 3;
-    var MAX_RESULTS = 200;
-    var GENERICS_DATA = 2;
-    var NAME = 0;
-    var INPUTS_DATA = 0;
-    var OUTPUT_DATA = 1;
-    var NO_TYPE_FILTER = -1;
+    const MAX_LEV_DISTANCE = 3;
+    const MAX_RESULTS = 200;
+    const GENERICS_DATA = 2;
+    const NAME = 0;
+    const INPUTS_DATA = 0;
+    const OUTPUT_DATA = 1;
+    const NO_TYPE_FILTER = -1;
     /**
      *  @type {Array<Row>}
      */
-    var searchIndex;
-    /**
-     *  @type {Array<string>}
-     */
-    var searchWords;
-    var currentResults;
-    var ALIASES = {};
-    var params = searchState.getQueryStringParams();
+    let searchIndex;
+    let currentResults;
+    const ALIASES = {};
+    const params = searchState.getQueryStringParams();
 
     // Populate search bar with query string search term when provided,
     // but only if the input bar is empty. This avoid the obnoxious issue
@@ -133,15 +124,451 @@ window.initSearch = function(rawSearchIndex) {
         searchState.input.value = params.search || "";
     }
 
+    function isWhitespace(c) {
+        return " \t\n\r".indexOf(c) !== -1;
+    }
+
+    function isSpecialStartCharacter(c) {
+        return "<\"".indexOf(c) !== -1;
+    }
+
+    function isEndCharacter(c) {
+        return ",>-".indexOf(c) !== -1;
+    }
+
+    function isStopCharacter(c) {
+        return isWhitespace(c) || isEndCharacter(c);
+    }
+
+    function isErrorCharacter(c) {
+        return "()".indexOf(c) !== -1;
+    }
+
+    function itemTypeFromName(typename) {
+        for (let i = 0, len = itemTypes.length; i < len; ++i) {
+            if (itemTypes[i] === typename) {
+                return i;
+            }
+        }
+
+        throw new Error("Unknown type filter `" + typename + "`");
+    }
+
+    /**
+     * 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 new Error("`\"` cannot be used in generics");
+        } else if (query.literalSearch) {
+            throw new Error("Cannot have more than one literal search element");
+        } else if (parserState.totalElems - parserState.genericsElems > 0) {
+            throw new Error("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 new Error("Unclosed `\"`");
+        } else if (parserState.userQuery[end] !== "\"") {
+            throw new Error(`Unexpected \`${parserState.userQuery[end]}\` in a string element`);
+        } else if (start === end) {
+            throw new Error("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) == '->';
+    }
+
+    /**
+     * Returns `true` if the given `c` character is valid for an ident.
+     *
+     * @param {string} c
+     *
+     * @return {boolean}
+     */
+    function isIdentCharacter(c) {
+        return (
+            c === '_' ||
+            (c >= '0' && c <= '9') ||
+            (c >= 'a' && c <= 'z') ||
+            (c >= 'A' && c <= 'Z'));
+    }
+
+    /**
+     * Returns `true` if the given `c` character is a separator.
+     *
+     * @param {string} c
+     *
+     * @return {boolean}
+     */
+    function isSeparatorCharacter(c) {
+        return c === "," || isWhitespaceCharacter(c);
+    }
+
+    /**
+     * Returns `true` if the given `c` character is a whitespace.
+     *
+     * @param {string} c
+     *
+     * @return {boolean}
+     */
+    function isWhitespaceCharacter(c) {
+        return c === " " || c === "\t";
+    }
+
+    /**
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     * @param {string} name                  - Name of the query element.
+     * @param {Array<QueryElement>} generics - List of generics of this query element.
+     *
+     * @return {QueryElement}                - The newly created `QueryElement`.
+     */
+    function createQueryElement(query, parserState, name, generics, isInGenerics) {
+        if (name === '*' || (name.length === 0 && generics.length === 0)) {
+            return;
+        }
+        if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) {
+            throw new Error("You cannot have more than one element if you use quotes");
+        }
+        const pathSegments = name.split("::");
+        if (pathSegments.length > 1) {
+            for (let i = 0, len = pathSegments.length; i < len; ++i) {
+                const 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.
+        if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) {
+            throw new Error("Found generics without a path");
+        }
+        parserState.totalElems += 1;
+        if (isInGenerics) {
+            parserState.genericsElems += 1;
+        }
+        return {
+            name: name,
+            fullPath: pathSegments,
+            pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
+            pathLast: pathSegments[pathSegments.length - 1],
+            generics: generics,
+        };
+    }
+
+    /**
+     * 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 end = parserState.pos;
+        let foundExclamation = false;
+        while (parserState.pos < parserState.length) {
+            const c = parserState.userQuery[parserState.pos];
+            if (!isIdentCharacter(c)) {
+                if (c === "!") {
+                    if (foundExclamation) {
+                        throw new Error("Cannot have more than one `!` in an ident");
+                    } else if (parserState.pos + 1 < parserState.length &&
+                        isIdentCharacter(parserState.userQuery[parserState.pos + 1]))
+                    {
+                        throw new Error("`!` can only be at the end of an ident");
+                    }
+                    foundExclamation = true;
+                } else if (isErrorCharacter(c)) {
+                    throw new Error(`Unexpected \`${c}\``);
+                } else if (
+                    isStopCharacter(c) ||
+                    isSpecialStartCharacter(c) ||
+                    isSeparatorCharacter(c))
+                {
+                    break;
+                }
+                // If we allow paths ("str::string" for example).
+                else if (c === ":") {
+                    if (!isPathStart(parserState)) {
+                        break;
+                    }
+                    // Skip current ":".
+                    parserState.pos += 1;
+                    foundExclamation = false;
+                } else {
+                    throw new Error(`Unexpected \`${c}\``);
+                }
+            }
+            parserState.pos += 1;
+            end = parserState.pos;
+        }
+        return end;
+    }
+
+    /**
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     * @param {Array<QueryElement>} elems - This is where the new {QueryElement} will be added.
+     * @param {boolean} isInGenerics
+     */
+    function getNextElem(query, parserState, elems, isInGenerics) {
+        const generics = [];
+
+        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 (isInGenerics) {
+                throw new Error("Unexpected `<` after `<`");
+            } else if (start >= end) {
+                throw new Error("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
+            )
+        );
+    }
+
+    /**
+     * This function parses the next query element until it finds `endChar`, calling `getNextElem`
+     * to collect each element.
+     *
+     * If there is no `endChar`, this function will implicitly stop at the end without raising an
+     * error.
+     *
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     * @param {Array<QueryElement>} elems - This is where the new {QueryElement} will be added.
+     * @param {string} endChar            - This function will stop when it'll encounter this
+     *                                      character.
+     */
+    function getItemsBefore(query, parserState, elems, endChar) {
+        let foundStopChar = true;
+
+        while (parserState.pos < parserState.length) {
+            const c = parserState.userQuery[parserState.pos];
+            if (c === endChar) {
+                break;
+            } else if (isSeparatorCharacter(c)) {
+                parserState.pos += 1;
+                foundStopChar = true;
+                continue;
+            } else if (c === ":" && isPathStart(parserState)) {
+                throw new Error("Unexpected `::`: paths cannot start with `::`");
+            } else if (c === ":" || isEndCharacter(c)) {
+                let extra = "";
+                if (endChar === ">") {
+                    extra = "`<`";
+                } else if (endChar === "") {
+                    extra = "`->`";
+                }
+                throw new Error("Unexpected `" + c + "` after " + extra);
+            }
+            if (!foundStopChar) {
+                if (endChar !== "") {
+                    throw new Error(`Expected \`,\`, \` \` or \`${endChar}\`, found \`${c}\``);
+                }
+                throw new Error(`Expected \`,\` or \` \`, found \`${c}\``);
+            }
+            const posBefore = parserState.pos;
+            getNextElem(query, parserState, elems, endChar === ">");
+            // This case can be encountered if `getNextElem` encounted a "stop character" right from
+            // the start. For example if you have `,,` or `<>`. In this case, we simply move up the
+            // current position to continue the parsing.
+            if (posBefore === parserState.pos) {
+                parserState.pos += 1;
+            }
+            foundStopChar = false;
+        }
+        // We are either at the end of the string or on the `endChar`` character, let's move forward
+        // in any case.
+        parserState.pos += 1;
+    }
+
+    /**
+     * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored
+     * if empty).
+     *
+     * @param {ParserState} parserState
+     */
+    function checkExtraTypeFilterCharacters(parserState) {
+        const query = parserState.userQuery;
+
+        for (let pos = 0; pos < parserState.pos; ++pos) {
+            if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) {
+                throw new Error(`Unexpected \`${query[pos]}\` in type filter`);
+            }
+        }
+    }
+
+    /**
+     * Parses the provided `query` input to fill `parserState`. If it encounters an error while
+     * parsing `query`, it'll throw an error.
+     *
+     * @param {ParsedQuery} query
+     * @param {ParserState} parserState
+     */
+    function parseInput(query, parserState) {
+        let c, before;
+        let foundStopChar = true;
+
+        while (parserState.pos < parserState.length) {
+            c = parserState.userQuery[parserState.pos];
+            if (isStopCharacter(c)) {
+                foundStopChar = true;
+                if (isSeparatorCharacter(c)) {
+                    parserState.pos += 1;
+                    continue;
+                } else if (c === "-" || c === ">") {
+                    if (isReturnArrow(parserState)) {
+                        break;
+                    }
+                    throw new Error(`Unexpected \`${c}\` (did you mean \`->\`?)`);
+                }
+                throw new Error(`Unexpected \`${c}\``);
+            } else if (c === ":" && !isPathStart(parserState)) {
+                if (parserState.typeFilter !== null) {
+                    throw new Error("Unexpected `:`");
+                }
+                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 `:`");
+                } else if (query.literalSearch) {
+                    throw new Error("You cannot use quotes on type filter");
+                }
+                checkExtraTypeFilterCharacters(parserState);
+                // The type filter doesn't count as an element since it's a modifier.
+                parserState.typeFilter = query.elems.pop().name;
+                parserState.pos += 1;
+                parserState.totalElems = 0;
+                query.literalSearch = false;
+                foundStopChar = true;
+                continue;
+            }
+            if (!foundStopChar) {
+                if (parserState.typeFilter !== null) {
+                    throw new Error(`Expected \`,\`, \` \` or \`->\`, found \`${c}\``);
+                }
+                throw new Error(`Expected \`,\`, \` \`, \`:\` or \`->\`, found \`${c}\``);
+            }
+            before = query.elems.length;
+            getNextElem(query, parserState, query.elems, false);
+            if (query.elems.length === before) {
+                // Nothing was added, weird... Let's increase the position to not remain stuck.
+                parserState.pos += 1;
+            }
+            foundStopChar = false;
+        }
+        while (parserState.pos < parserState.length) {
+            c = parserState.userQuery[parserState.pos];
+            if (isReturnArrow(parserState)) {
+                parserState.pos += 2;
+                // Get returned elements.
+                getItemsBefore(query, parserState, query.returned, "");
+                // Nothing can come afterward!
+                if (query.returned.length === 0) {
+                    throw new Error("Expected at least one item after `->`");
+                }
+                break;
+            } else {
+                parserState.pos += 1;
+            }
+        }
+    }
+
+    /**
+     * Takes the user search input and returns an empty `ParsedQuery`.
+     *
+     * @param {string} userQuery
+     *
+     * @return {ParsedQuery}
+     */
+    function newParsedQuery(userQuery) {
+        return {
+            original: userQuery,
+            userQuery: userQuery.toLowerCase(),
+            typeFilter: NO_TYPE_FILTER,
+            elems: [],
+            returned: [],
+            // Total number of "top" elements (does not include generics).
+            foundElems: 0,
+            literalSearch: false,
+            error: null,
+        };
+    }
+
     /**
      * Build an URL with search parameters.
      *
      * @param {string} search            - The current search being performed.
      * @param {string|null} filterCrates - The current filtering crate (if any).
+     *
      * @return {string}
      */
     function buildUrl(search, filterCrates) {
-        var extra = "?search=" + encodeURIComponent(search);
+        let extra = "?search=" + encodeURIComponent(search);
 
         if (filterCrates !== null) {
             extra += "&filter-crate=" + encodeURIComponent(filterCrates);
@@ -155,7 +582,7 @@ window.initSearch = function(rawSearchIndex) {
      * @return {string|null}
      */
     function getFilterCrates() {
-        var elem = document.getElementById("crate-search");
+        const elem = document.getElementById("crate-search");
 
         if (elem &&
             elem.value !== "All crates" &&
@@ -167,45 +594,149 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     /**
-     * Executes the query and returns a list of results for each results tab.
-     * @param  {Object}        query          - The user query
-     * @param  {Array<string>} searchWords    - The list of search words to query against
-     * @param  {string}        [filterCrates] - Crate to search in
-     * @return {{
-     *   in_args: Array<?>,
-     *   returned: Array<?>,
-     *   others: Array<?>
-     * }}
+     * Parses the query.
+     *
+     * The supported syntax by this parser is as follow:
+     *
+     * ident = *(ALPHA / DIGIT / "_") [!]
+     * path = ident *(DOUBLE-COLON ident)
+     * arg = path [generics]
+     * arg-without-generic = path
+     * type-sep = COMMA/WS *(COMMA/WS)
+     * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
+     * nonempty-arg-list-without-generics = *(type-sep) arg-without-generic
+     *                                      *(type-sep arg-without-generic) *(type-sep)
+     * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list-without-generics ] *(type-sep)
+     *            CLOSE-ANGLE-BRACKET/EOF
+     * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
+     *
+     * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
+     * type-search = [type-filter *WS COLON] [ nonempty-arg-list ] [ return-args ]
+     *
+     * query = *WS (exact-search / type-search) *WS
+     *
+     * type-filter = (
+     *     "mod" /
+     *     "externcrate" /
+     *     "import" /
+     *     "struct" /
+     *     "enum" /
+     *     "fn" /
+     *     "type" /
+     *     "static" /
+     *     "trait" /
+     *     "impl" /
+     *     "tymethod" /
+     *     "method" /
+     *     "structfield" /
+     *     "variant" /
+     *     "macro" /
+     *     "primitive" /
+     *     "associatedtype" /
+     *     "constant" /
+     *     "associatedconstant" /
+     *     "union" /
+     *     "foreigntype" /
+     *     "keyword" /
+     *     "existential" /
+     *     "attr" /
+     *     "derive" /
+     *     "traitalias")
+     *
+     * OPEN-ANGLE-BRACKET = "<"
+     * CLOSE-ANGLE-BRACKET = ">"
+     * COLON = ":"
+     * DOUBLE-COLON = "::"
+     * QUOTE = %x22
+     * COMMA = ","
+     * RETURN-ARROW = "->"
+     *
+     * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+     * DIGIT = %x30-39
+     * WS = %x09 / " "
+     *
+     * @param  {string} val     - The user query
+     *
+     * @return {ParsedQuery}    - The parsed query
      */
-    function execQuery(query, searchWords, filterCrates) {
-        function itemTypeFromName(typename) {
-            for (var i = 0, len = itemTypes.length; i < len; ++i) {
-                if (itemTypes[i] === typename) {
-                    return i;
+    function parseQuery(userQuery) {
+        userQuery = userQuery.trim();
+        const parserState = {
+            length: userQuery.length,
+            pos: 0,
+            // Total number of elements (includes generics).
+            totalElems: 0,
+            genericsElems: 0,
+            typeFilter: null,
+            userQuery: userQuery.toLowerCase(),
+        };
+        let query = newParsedQuery(userQuery);
+
+        try {
+            parseInput(query, parserState);
+            if (parserState.typeFilter !== null) {
+                let typeFilter = parserState.typeFilter;
+                if (typeFilter === "const") {
+                    typeFilter = "constant";
                 }
+                query.typeFilter = itemTypeFromName(typeFilter);
             }
-            return NO_TYPE_FILTER;
+        } catch (err) {
+            query = newParsedQuery(userQuery);
+            query.error = err.message;
+            query.typeFilter = -1;
+            return query;
         }
 
-        var valLower = query.query.toLowerCase(),
-            val = valLower,
-            typeFilter = itemTypeFromName(query.type),
-            results = {}, results_in_args = {}, results_returned = {},
-            split = valLower.split("::");
+        if (!query.literalSearch) {
+            // If there is more than one element in the query, we switch to literalSearch in any
+            // case.
+            query.literalSearch = parserState.totalElems > 1;
+        }
+        query.foundElems = query.elems.length + query.returned.length;
+        return query;
+    }
+
+    /**
+     * Creates the query results.
+     *
+     * @param {Array<Result>} results_in_args
+     * @param {Array<Result>} results_returned
+     * @param {Array<Result>} results_in_args
+     * @param {ParsedQuery} parsedQuery
+     *
+     * @return {ResultsTable}
+     */
+    function createQueryResults(results_in_args, results_returned, results_others, parsedQuery) {
+        return {
+            "in_args": results_in_args,
+            "returned": results_returned,
+            "others": results_others,
+            "query": parsedQuery,
+        };
+    }
 
-        removeEmptyStringsFromArray(split);
+    /**
+     * Executes the parsed query and builds a {ResultsTable}.
+     *
+     * @param  {ParsedQuery} parsedQuery - The parsed user query
+     * @param  {Object} searchWords      - The list of search words to query against
+     * @param  {Object} [filterCrates]   - Crate to search in if defined
+     *
+     * @return {ResultsTable}
+     */
+    function execQuery(parsedQuery, searchWords, filterCrates) {
+        const results_others = {}, results_in_args = {}, results_returned = {};
 
         function transformResults(results) {
-            var duplicates = {};
-            var out = [];
-
-            for (var i = 0, len = results.length; i < len; ++i) {
-                var result = results[i];
+            const duplicates = {};
+            const out = [];
 
+            for (const result of results) {
                 if (result.id > -1) {
-                    var obj = searchIndex[result.id];
+                    const obj = searchIndex[result.id];
                     obj.lev = result.lev;
-                    var res = buildHrefAndPath(obj);
+                    const res = buildHrefAndPath(obj);
                     obj.displayPath = pathSplitter(res[0]);
                     obj.fullPath = obj.displayPath + obj.name;
                     // To be sure than it some items aren't considered as duplicate.
@@ -227,10 +758,11 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         function sortResults(results, isType) {
-            var ar = [];
-            for (var entry in results) {
+            const userQuery = parsedQuery.userQuery;
+            const ar = [];
+            for (const entry in results) {
                 if (hasOwnPropertyRustdoc(results, entry)) {
-                    var result = results[entry];
+                    const result = results[entry];
                     result.word = searchWords[result.id];
                     result.item = searchIndex[result.id] || {};
                     ar.push(result);
@@ -243,11 +775,11 @@ window.initSearch = function(rawSearchIndex) {
             }
 
             results.sort(function(aaa, bbb) {
-                var a, b;
+                let a, b;
 
                 // sort by exact match with regard to the last word (mismatch goes later)
-                a = (aaa.word !== val);
-                b = (bbb.word !== val);
+                a = (aaa.word !== userQuery);
+                b = (bbb.word !== userQuery);
                 if (a !== b) { return a - b; }
 
                 // Sort by non levenshtein results and then levenshtein results by the distance
@@ -309,270 +841,273 @@ window.initSearch = function(rawSearchIndex) {
                 return 0;
             });
 
-            for (var i = 0, len = results.length; i < len; ++i) {
-                result = results[i];
+            let nameSplit = null;
+            if (parsedQuery.elems.length === 1) {
+                const hasPath = typeof parsedQuery.elems[0].path === "undefined";
+                nameSplit = hasPath ? null : parsedQuery.elems[0].path;
+            }
 
+            for (const result of results) {
                 // this validation does not make sense when searching by types
                 if (result.dontValidate) {
                     continue;
                 }
-                var name = result.item.name.toLowerCase(),
+                const name = result.item.name.toLowerCase(),
                     path = result.item.path.toLowerCase(),
                     parent = result.item.parent;
 
-                if (!isType && !validateResult(name, path, split, parent)) {
+                if (!isType && !validateResult(name, path, nameSplit, parent)) {
                     result.id = -1;
                 }
             }
             return transformResults(results);
         }
 
-        function extractGenerics(val) {
-            val = val.toLowerCase();
-            if (val.indexOf("<") !== -1) {
-                var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
-                return {
-                    name: val.substring(0, val.indexOf("<")),
-                    generics: values.split(/\s*,\s*/),
-                };
+        /**
+         * This function checks if the object (`row`) generics match the given type (`elem`)
+         * generics. If there are no generics on `row`, `defaultLev` is returned.
+         *
+         * @param {Row} row            - The object to check.
+         * @param {QueryElement} elem  - The element from the parsed query.
+         * @param {integer} defaultLev - This is the value to return in case there are no generics.
+         *
+         * @return {integer}           - Returns the best match (if any) or `MAX_LEV_DISTANCE + 1`.
+         */
+        function checkGenerics(row, elem, defaultLev) {
+            if (row.length <= GENERICS_DATA || row[GENERICS_DATA].length === 0) {
+                return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1;
+            } else if (row[GENERICS_DATA].length > 0 && row[GENERICS_DATA][0][NAME] === "") {
+                if (row.length > GENERICS_DATA) {
+                    return checkGenerics(row[GENERICS_DATA][0], elem, defaultLev);
+                }
+                return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1;
             }
-            return {
-                name: val,
-                generics: [],
-            };
-        }
-
-        function checkGenerics(obj, val) {
             // The names match, but we need to be sure that all generics kinda
             // match as well.
-            var tmp_lev, elem_name;
-            if (val.generics.length > 0) {
-                if (obj.length > GENERICS_DATA &&
-                      obj[GENERICS_DATA].length >= val.generics.length) {
-                    var elems = Object.create(null);
-                    var elength = obj[GENERICS_DATA].length;
-                    for (var x = 0; x < elength; ++x) {
-                        if (!elems[obj[GENERICS_DATA][x][NAME]]) {
-                            elems[obj[GENERICS_DATA][x][NAME]] = 0;
+            let elem_name;
+            if (elem.generics.length > 0 && row[GENERICS_DATA].length >= elem.generics.length) {
+                const elems = Object.create(null);
+                for (const entry of row[GENERICS_DATA]) {
+                    elem_name = entry[NAME];
+                    if (elem_name === "") {
+                        // Pure generic, needs to check into it.
+                        if (checkGenerics(entry, elem, MAX_LEV_DISTANCE + 1) !== 0) {
+                            return MAX_LEV_DISTANCE + 1;
                         }
-                        elems[obj[GENERICS_DATA][x][NAME]] += 1;
+                        continue;
+                    }
+                    if (elems[elem_name] === undefined) {
+                        elems[elem_name] = 0;
                     }
-                    var total = 0;
-                    var done = 0;
-                    // We need to find the type that matches the most to remove it in order
-                    // to move forward.
-                    var vlength = val.generics.length;
-                    for (x = 0; x < vlength; ++x) {
-                        var lev = MAX_LEV_DISTANCE + 1;
-                        var firstGeneric = val.generics[x];
-                        var match = null;
-                        if (elems[firstGeneric]) {
-                            match = firstGeneric;
-                            lev = 0;
-                        } else {
-                            for (elem_name in elems) {
-                                tmp_lev = levenshtein(elem_name, firstGeneric);
-                                if (tmp_lev < lev) {
-                                    lev = tmp_lev;
-                                    match = elem_name;
-                                }
+                    elems[elem_name] += 1;
+                }
+                // We need to find the type that matches the most to remove it in order
+                // to move forward.
+                for (const generic of elem.generics) {
+                    let match = null;
+                    if (elems[generic.name]) {
+                        match = generic.name;
+                    } else {
+                        for (elem_name in elems) {
+                            if (!hasOwnPropertyRustdoc(elems, elem_name)) {
+                                continue;
                             }
-                        }
-                        if (match !== null) {
-                            elems[match] -= 1;
-                            if (elems[match] == 0) {
-                                delete elems[match];
+                            if (elem_name === generic) {
+                                match = elem_name;
+                                break;
                             }
-                            total += lev;
-                            done += 1;
-                        } else {
-                            return MAX_LEV_DISTANCE + 1;
                         }
                     }
-                    return Math.ceil(total / done);
+                    if (match === null) {
+                        return MAX_LEV_DISTANCE + 1;
+                    }
+                    elems[match] -= 1;
+                    if (elems[match] === 0) {
+                        delete elems[match];
+                    }
                 }
+                return 0;
             }
             return MAX_LEV_DISTANCE + 1;
         }
 
         /**
-          * This function checks if the object (`obj`) matches the given type (`val`) and its
+          * This function checks if the object (`row`) matches the given type (`elem`) and its
           * generics (if any).
           *
-          * @param {Object} obj
-          * @param {string} val
+          * @param {Row} row
+          * @param {QueryElement} elem    - The element from the parsed query.
+          *
+          * @return {integer} - Returns a Levenshtein distance to the best match.
+          */
+        function checkIfInGenerics(row, elem) {
+            let lev = MAX_LEV_DISTANCE + 1;
+            for (const entry of row[GENERICS_DATA]) {
+                lev = Math.min(checkType(entry, elem, true), lev);
+                if (lev === 0) {
+                    break;
+                }
+            }
+            return lev;
+        }
+
+        /**
+          * This function checks if the object (`row`) matches the given type (`elem`) and its
+          * generics (if any).
+          *
+          * @param {Row} row
+          * @param {QueryElement} elem      - The element from the parsed query.
           * @param {boolean} literalSearch
           *
           * @return {integer} - Returns a Levenshtein distance to the best match. If there is
           *                     no match, returns `MAX_LEV_DISTANCE + 1`.
           */
-        function checkType(obj, val, literalSearch) {
-            var lev_distance = MAX_LEV_DISTANCE + 1;
-            var tmp_lev = MAX_LEV_DISTANCE + 1;
-            var len, x, firstGeneric;
-            if (obj[NAME] === val.name) {
-                if (literalSearch) {
-                    if (val.generics && val.generics.length !== 0) {
-                        if (obj.length > GENERICS_DATA &&
-                             obj[GENERICS_DATA].length > 0) {
-                            var elems = Object.create(null);
-                            len = obj[GENERICS_DATA].length;
-                            for (x = 0; x < len; ++x) {
-                                if (!elems[obj[GENERICS_DATA][x][NAME]]) {
-                                    elems[obj[GENERICS_DATA][x][NAME]] = 0;
-                                }
-                                elems[obj[GENERICS_DATA][x][NAME]] += 1;
-                            }
+        function checkType(row, elem, literalSearch) {
+            if (row[NAME].length === 0) {
+                // This is a pure "generic" search, no need to run other checks.
+                if (row.length > GENERICS_DATA) {
+                    return checkIfInGenerics(row, elem);
+                }
+                return MAX_LEV_DISTANCE + 1;
+            }
 
-                            len = val.generics.length;
-                            for (x = 0; x < len; ++x) {
-                                firstGeneric = val.generics[x];
-                                if (elems[firstGeneric]) {
-                                    elems[firstGeneric] -= 1;
-                                } else {
-                                    // Something wasn't found and this is a literal search so
-                                    // abort and return a "failing" distance.
-                                    return MAX_LEV_DISTANCE + 1;
-                                }
-                            }
-                            // Everything was found, success!
+            let lev = levenshtein(row[NAME], elem.name);
+            if (literalSearch) {
+                if (lev !== 0) {
+                    // The name didn't match, let's try to check if the generics do.
+                    if (elem.generics.length === 0) {
+                        const checkGeneric = (row.length > GENERICS_DATA &&
+                            row[GENERICS_DATA].length > 0);
+                        if (checkGeneric && row[GENERICS_DATA].findIndex(function(tmp_elem) {
+                            return tmp_elem[NAME] === elem.name;
+                        }) !== -1) {
                             return 0;
                         }
-                        return MAX_LEV_DISTANCE + 1;
                     }
-                    return 0;
-                } else {
-                    // If the type has generics but don't match, then it won't return at this point.
-                    // Otherwise, `checkGenerics` will return 0 and it'll return.
-                    if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
-                        tmp_lev = checkGenerics(obj, val);
-                        if (tmp_lev <= MAX_LEV_DISTANCE) {
-                            return tmp_lev;
-                        }
-                    }
-                }
-            } else if (literalSearch) {
-                var found = false;
-                if ((!val.generics || val.generics.length === 0) &&
-                      obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                    found = obj[GENERICS_DATA].some(
-                        function(gen) {
-                            return gen[NAME] === val.name;
-                        });
-                }
-                return found ? 0 : MAX_LEV_DISTANCE + 1;
-            }
-            lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
-            if (lev_distance <= MAX_LEV_DISTANCE) {
-                // The generics didn't match but the name kinda did so we give it
-                // a levenshtein distance value that isn't *this* good so it goes
-                // into the search results but not too high.
-                lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
-            }
-            if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                // We can check if the type we're looking for is inside the generics!
-                var olength = obj[GENERICS_DATA].length;
-                for (x = 0; x < olength; ++x) {
-                    tmp_lev = Math.min(levenshtein(obj[GENERICS_DATA][x][NAME], val.name), tmp_lev);
+                    return MAX_LEV_DISTANCE + 1;
+                } else if (elem.generics.length > 0) {
+                    return checkGenerics(row, elem, MAX_LEV_DISTANCE + 1);
                 }
-                if (tmp_lev !== 0) {
-                    // If we didn't find a good enough result, we go check inside the generics of
-                    // the generics.
-                    for (x = 0; x < olength && tmp_lev !== 0; ++x) {
-                        tmp_lev = Math.min(
-                            checkType(obj[GENERICS_DATA][x], val, literalSearch),
-                            tmp_lev
-                        );
+                return 0;
+            } else if (row.length > GENERICS_DATA) {
+                if (elem.generics.length === 0) {
+                    if (lev === 0) {
+                        return 0;
+                    }
+                    // The name didn't match so we now check if the type we're looking for is inside
+                    // the generics!
+                    lev = checkIfInGenerics(row, elem);
+                    // Now whatever happens, the returned distance is "less good" so we should mark
+                    // it as such, and so we add 0.5 to the distance to make it "less good".
+                    return lev + 0.5;
+                } else if (lev > MAX_LEV_DISTANCE) {
+                    // So our item's name doesn't match at all and has generics.
+                    //
+                    // Maybe it's present in a sub generic? For example "f<A<B<C>>>()", if we're
+                    // looking for "B<C>", we'll need to go down.
+                    return checkIfInGenerics(row, elem);
+                } else {
+                    // At this point, the name kinda match and we have generics to check, so
+                    // let's go!
+                    const tmp_lev = checkGenerics(row, elem, lev);
+                    if (tmp_lev > MAX_LEV_DISTANCE) {
+                        return MAX_LEV_DISTANCE + 1;
                     }
+                    // We compute the median value of both checks and return it.
+                    return (tmp_lev + lev) / 2;
                 }
+            } else if (elem.generics.length > 0) {
+                // In this case, we were expecting generics but there isn't so we simply reject this
+                // one.
+                return MAX_LEV_DISTANCE + 1;
             }
-            // Now whatever happens, the returned distance is "less good" so we should mark it
-            // as such, and so we add 1 to the distance to make it "less good".
-            return Math.min(lev_distance, tmp_lev) + 1;
+            // No generics on our query or on the target type so we can return without doing
+            // anything else.
+            return lev;
         }
 
         /**
-         * This function checks if the object (`obj`) has an argument with the given type (`val`).
+         * This function checks if the object (`row`) has an argument with the given type (`elem`).
          *
-         * @param {Object} obj
-         * @param {string} val
-         * @param {boolean} literalSearch
+         * @param {Row} row
+         * @param {QueryElement} elem    - The element from the parsed query.
          * @param {integer} typeFilter
          *
          * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
          *                      match, returns `MAX_LEV_DISTANCE + 1`.
          */
-        function findArg(obj, val, literalSearch, typeFilter) {
-            var lev_distance = MAX_LEV_DISTANCE + 1;
-
-            if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
-                var length = obj.type[INPUTS_DATA].length;
-                for (var i = 0; i < length; i++) {
-                    var tmp = obj.type[INPUTS_DATA][i];
-                    if (!typePassesFilter(typeFilter, tmp[1])) {
+        function findArg(row, elem, typeFilter) {
+            let lev = MAX_LEV_DISTANCE + 1;
+
+            if (row && row.type && row.type[INPUTS_DATA] && row.type[INPUTS_DATA].length > 0) {
+                for (const input of row.type[INPUTS_DATA]) {
+                    if (!typePassesFilter(typeFilter, input[1])) {
                         continue;
                     }
-                    tmp = checkType(tmp, val, literalSearch);
-                    if (tmp === 0) {
+                    lev = Math.min(lev, checkType(input, elem, parsedQuery.literalSearch));
+                    if (lev === 0) {
                         return 0;
-                    } else if (literalSearch) {
-                        continue;
                     }
-                    lev_distance = Math.min(tmp, lev_distance);
                 }
             }
-            return literalSearch ? MAX_LEV_DISTANCE + 1 : lev_distance;
+            return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev;
         }
 
-        function checkReturned(obj, val, literalSearch, typeFilter) {
-            var lev_distance = MAX_LEV_DISTANCE + 1;
+        /**
+         * This function checks if the object (`row`) returns the given type (`elem`).
+         *
+         * @param {Row} row
+         * @param {QueryElement} elem   - The element from the parsed query.
+         * @param {integer} typeFilter
+         *
+         * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
+         *                      match, returns `MAX_LEV_DISTANCE + 1`.
+         */
+        function checkReturned(row, elem, typeFilter) {
+            let lev = MAX_LEV_DISTANCE + 1;
 
-            if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
-                var ret = obj.type[OUTPUT_DATA];
+            if (row && row.type && row.type.length > OUTPUT_DATA) {
+                let ret = row.type[OUTPUT_DATA];
                 if (typeof ret[0] === "string") {
                     ret = [ret];
                 }
-                for (var x = 0, len = ret.length; x < len; ++x) {
-                    var tmp = ret[x];
-                    if (!typePassesFilter(typeFilter, tmp[1])) {
+                for (const ret_ty of ret) {
+                    if (!typePassesFilter(typeFilter, ret_ty[1])) {
                         continue;
                     }
-                    tmp = checkType(tmp, val, literalSearch);
-                    if (tmp === 0) {
+                    lev = Math.min(lev, checkType(ret_ty, elem, parsedQuery.literalSearch));
+                    if (lev === 0) {
                         return 0;
-                    } else if (literalSearch) {
-                        continue;
                     }
-                    lev_distance = Math.min(tmp, lev_distance);
                 }
             }
-            return literalSearch ? MAX_LEV_DISTANCE + 1 : lev_distance;
+            return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev;
         }
 
         function checkPath(contains, lastElem, ty) {
             if (contains.length === 0) {
                 return 0;
             }
-            var ret_lev = MAX_LEV_DISTANCE + 1;
-            var path = ty.path.split("::");
+            let ret_lev = MAX_LEV_DISTANCE + 1;
+            const path = ty.path.split("::");
 
             if (ty.parent && ty.parent.name) {
                 path.push(ty.parent.name.toLowerCase());
             }
 
-            var length = path.length;
-            var clength = contains.length;
+            const length = path.length;
+            const clength = contains.length;
             if (clength > length) {
                 return MAX_LEV_DISTANCE + 1;
             }
-            for (var i = 0; i < length; ++i) {
+            for (let i = 0; i < length; ++i) {
                 if (i + clength > length) {
                     break;
                 }
-                var lev_total = 0;
-                var aborted = false;
-                for (var x = 0; x < clength; ++x) {
-                    var lev = levenshtein(path[i + x], contains[x]);
+                let lev_total = 0;
+                let aborted = false;
+                for (let x = 0; x < clength; ++x) {
+                    const lev = levenshtein(path[i + x], contains[x]);
                     if (lev > MAX_LEV_DISTANCE) {
                         aborted = true;
                         break;
@@ -591,7 +1126,7 @@ window.initSearch = function(rawSearchIndex) {
             if (filter <= NO_TYPE_FILTER || filter === type) return true;
 
             // Match related items
-            var name = itemTypes[type];
+            const name = itemTypes[type];
             switch (itemTypes[filter]) {
                 case "constant":
                     return name === "associatedconstant";
@@ -621,32 +1156,31 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         function handleAliases(ret, query, filterCrates) {
+            const lowerQuery = query.toLowerCase();
             // We separate aliases and crate aliases because we want to have current crate
             // aliases to be before the others in the displayed results.
-            var aliases = [];
-            var crateAliases = [];
+            const aliases = [];
+            const crateAliases = [];
             if (filterCrates !== null) {
-                if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
-                    var query_aliases = ALIASES[filterCrates][query.search];
-                    var len = query_aliases.length;
-                    for (var i = 0; i < len; ++i) {
-                        aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                if (ALIASES[filterCrates] && ALIASES[filterCrates][lowerQuery]) {
+                    const query_aliases = ALIASES[filterCrates][lowerQuery];
+                    for (const alias of query_aliases) {
+                        aliases.push(createAliasFromItem(searchIndex[alias]));
                     }
                 }
             } else {
                 Object.keys(ALIASES).forEach(function(crate) {
-                    if (ALIASES[crate][query.search]) {
-                        var pushTo = crate === window.currentCrate ? crateAliases : aliases;
-                        var query_aliases = ALIASES[crate][query.search];
-                        var len = query_aliases.length;
-                        for (var i = 0; i < len; ++i) {
-                            pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                    if (ALIASES[crate][lowerQuery]) {
+                        const pushTo = crate === window.currentCrate ? crateAliases : aliases;
+                        const query_aliases = ALIASES[crate][lowerQuery];
+                        for (const alias of query_aliases) {
+                            pushTo.push(createAliasFromItem(searchIndex[alias]));
                         }
                     }
                 });
             }
 
-            var sortFunc = function(aaa, bbb) {
+            const sortFunc = function(aaa, bbb) {
                 if (aaa.path < bbb.path) {
                     return 1;
                 } else if (aaa.path === bbb.path) {
@@ -657,9 +1191,9 @@ window.initSearch = function(rawSearchIndex) {
             crateAliases.sort(sortFunc);
             aliases.sort(sortFunc);
 
-            var pushFunc = function(alias) {
-                alias.alias = query.raw;
-                var res = buildHrefAndPath(alias);
+            const pushFunc = function(alias) {
+                alias.alias = query;
+                const res = buildHrefAndPath(alias);
                 alias.displayPath = pathSplitter(res[0]);
                 alias.fullPath = alias.displayPath + alias.name;
                 alias.href = res[1];
@@ -674,208 +1208,235 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         /**
-         * This function adds the given result into the provided `res` map if it matches the
+         * This function adds the given result into the provided `results` map if it matches the
          * following condition:
          *
-         * * If it is a "literal search" (`isExact`), then `lev` must be 0.
+         * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0.
          * * If it is not a "literal search", `lev` must be <= `MAX_LEV_DISTANCE`.
          *
-         * The `res` map contains information which will be used to sort the search results:
+         * The `results` map contains information which will be used to sort the search results:
          *
-         * * `fullId` is a `string`` used as the key of the object we use for the `res` map.
+         * * `fullId` is a `string`` used as the key of the object we use for the `results` map.
          * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
          * * `index` is an `integer`` used to sort by the position of the word in the item's name.
          * * `lev` is the main metric used to sort the search results.
          *
-         * @param {boolean} isExact
-         * @param {Object} res
+         * @param {Results} results
          * @param {string} fullId
          * @param {integer} id
          * @param {integer} index
          * @param {integer} lev
          */
-        function addIntoResults(isExact, res, fullId, id, index, lev) {
-            if (lev === 0 || (!isExact && lev <= MAX_LEV_DISTANCE)) {
-                if (res[fullId] !== undefined) {
-                    var result = res[fullId];
+        function addIntoResults(results, fullId, id, index, lev) {
+            if (lev === 0 || (!parsedQuery.literalSearch && lev <= MAX_LEV_DISTANCE)) {
+                if (results[fullId] !== undefined) {
+                    const result = results[fullId];
                     if (result.dontValidate || result.lev <= lev) {
                         return;
                     }
                 }
-                res[fullId] = {
+                results[fullId] = {
                     id: id,
                     index: index,
-                    dontValidate: isExact,
+                    dontValidate: parsedQuery.literalSearch,
                     lev: lev,
                 };
             }
         }
 
-        // quoted values mean literal search
-        var nSearchWords = searchWords.length;
-        var i, it;
-        var ty;
-        var fullId;
-        var returned;
-        var in_args;
-        var len;
-        if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
-            val.charAt(val.length - 1) === val.charAt(0))
-        {
-            val = extractGenerics(val.substr(1, val.length - 2));
-            for (i = 0; i < nSearchWords; ++i) {
-                if (filterCrates !== null && searchIndex[i].crate !== filterCrates) {
-                    continue;
+        /**
+         * This function is called in case the query is only one element (with or without generics).
+         * This element will be compared to arguments' and returned values' items and also to items.
+         *
+         * Other important thing to note: since there is only one element, we use levenshtein
+         * distance for name comparisons.
+         *
+         * @param {Row} row
+         * @param {integer} pos              - Position in the `searchIndex`.
+         * @param {QueryElement} elem        - The element from the parsed query.
+         * @param {Results} results_others   - Unqualified results (not in arguments nor in
+         *                                     returned values).
+         * @param {Results} results_in_args  - Matching arguments results.
+         * @param {Results} results_returned - Matching returned arguments results.
+         */
+        function handleSingleArg(
+            row,
+            pos,
+            elem,
+            results_others,
+            results_in_args,
+            results_returned
+        ) {
+            if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
+                return;
+            }
+            let lev, lev_add = 0, index = -1;
+            const fullId = row.id;
+
+            const in_args = findArg(row, elem, parsedQuery.typeFilter);
+            const returned = checkReturned(row, elem, parsedQuery.typeFilter);
+
+            addIntoResults(results_in_args, fullId, pos, index, in_args);
+            addIntoResults(results_returned, fullId, pos, index, returned);
+
+            if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
+                return;
+            }
+            const searchWord = searchWords[pos];
+
+            if (parsedQuery.literalSearch) {
+                if (searchWord === elem.name) {
+                    addIntoResults(results_others, fullId, pos, -1, 0);
+                }
+                return;
+            }
+
+            // No need to check anything else if it's a "pure" generics search.
+            if (elem.name.length === 0) {
+                if (row.type !== null) {
+                    lev = checkGenerics(row.type, elem, MAX_LEV_DISTANCE + 1);
+                    addIntoResults(results_others, fullId, pos, index, lev);
                 }
-                in_args = findArg(searchIndex[i], val, true, typeFilter);
-                returned = checkReturned(searchIndex[i], val, true, typeFilter);
-                ty = searchIndex[i];
-                fullId = ty.id;
-
-                if (searchWords[i] === val.name
-                    && typePassesFilter(typeFilter, searchIndex[i].ty)) {
-                    addIntoResults(true, results, fullId, i, -1, 0);
+                return;
+            }
+
+            if (elem.fullPath.length > 1) {
+                lev = checkPath(elem.pathWithoutLast, elem.pathLast, row);
+                if (lev > MAX_LEV_DISTANCE || (parsedQuery.literalSearch && lev !== 0)) {
+                    return;
+                } else if (lev > 0) {
+                    lev_add = lev / 10;
                 }
-                addIntoResults(true, results_in_args, fullId, i, -1, in_args);
-                addIntoResults(true, results_returned, fullId, i, -1, returned);
-            }
-            query.inputs = [val];
-            query.output = val;
-            query.search = val;
-        // searching by type
-        } else if (val.search("->") > -1) {
-            var trimmer = function(s) { return s.trim(); };
-            var parts = val.split("->").map(trimmer);
-            var input = parts[0];
-            // sort inputs so that order does not matter
-            var inputs = input.split(",").map(trimmer).sort();
-            for (i = 0, len = inputs.length; i < len; ++i) {
-                inputs[i] = extractGenerics(inputs[i]);
-            }
-            var output = extractGenerics(parts[1]);
-
-            for (i = 0; i < nSearchWords; ++i) {
-                if (filterCrates !== null && searchIndex[i].crate !== filterCrates) {
-                    continue;
+            }
+
+            if (searchWord.indexOf(elem.pathLast) > -1 ||
+                row.normalizedName.indexOf(elem.pathLast) > -1)
+            {
+                // filter type: ... queries
+                if (!results_others[fullId] !== undefined) {
+                    index = row.normalizedName.indexOf(elem.pathLast);
                 }
-                var type = searchIndex[i].type;
-                ty = searchIndex[i];
-                if (!type) {
-                    continue;
+            }
+            lev = levenshtein(searchWord, elem.pathLast);
+            lev += lev_add;
+            if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1)
+            {
+                if (elem.pathLast.length < 6) {
+                    lev = 1;
+                } else {
+                    lev = 0;
                 }
-                fullId = ty.id;
+            }
+            if (lev > MAX_LEV_DISTANCE) {
+                return;
+            } else if (index !== -1 && elem.fullPath.length < 2) {
+                lev -= 1;
+            }
+            if (lev < 0) {
+                lev = 0;
+            }
+            addIntoResults(results_others, fullId, pos, index, lev);
+        }
 
-                returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
-                if (output.name === "*" || returned === 0) {
-                    in_args = false;
-                    var is_module = false;
+        /**
+         * This function is called in case the query has more than one element. In this case, it'll
+         * try to match the items which validates all the elements. For `aa -> bb` will look for
+         * functions which have a parameter `aa` and has `bb` in its returned values.
+         *
+         * @param {Row} row
+         * @param {integer} pos      - Position in the `searchIndex`.
+         * @param {Object} results
+         */
+        function handleArgs(row, pos, results) {
+            if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
+                return;
+            }
 
-                    if (input === "*") {
-                        is_module = true;
+            let totalLev = 0;
+            let nbLev = 0;
+
+            // If the result is too "bad", we return false and it ends this search.
+            function checkArgs(elems, callback) {
+                for (const elem of elems) {
+                    // There is more than one parameter to the query so all checks should be "exact"
+                    const lev = callback(row, elem, NO_TYPE_FILTER);
+                    if (lev <= 1) {
+                        nbLev += 1;
+                        totalLev += lev;
                     } else {
-                        var firstNonZeroDistance = 0;
-                        for (it = 0, len = inputs.length; it < len; it++) {
-                            var distance = checkType(type, inputs[it], true);
-                            if (distance > 0) {
-                                firstNonZeroDistance = distance;
-                                break;
-                            }
-                        }
-                        in_args = firstNonZeroDistance;
-                    }
-                    addIntoResults(true, results_in_args, fullId, i, -1, in_args);
-                    addIntoResults(true, results_returned, fullId, i, -1, returned);
-                    if (is_module) {
-                        addIntoResults(true, results, fullId, i, -1, 0);
+                        return false;
                     }
                 }
+                return true;
+            }
+            if (!checkArgs(parsedQuery.elems, findArg)) {
+                return;
+            }
+            if (!checkArgs(parsedQuery.returned, checkReturned)) {
+                return;
             }
-            query.inputs = inputs.map(function(input) {
-                return input.name;
-            });
-            query.output = output.name;
-        } else {
-            query.inputs = [val];
-            query.output = val;
-            query.search = val;
-            // gather matching search results up to a certain maximum
-            val = val.replace(/_/g, "");
-
-            var valGenerics = extractGenerics(val);
-
-            var paths = valLower.split("::");
-            removeEmptyStringsFromArray(paths);
-            val = paths[paths.length - 1];
-            var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
-
-            var lev, j;
-            for (j = 0; j < nSearchWords; ++j) {
-                ty = searchIndex[j];
-                if (!ty || (filterCrates !== null && ty.crate !== filterCrates)) {
-                    continue;
-                }
-                var lev_add = 0;
-                if (paths.length > 1) {
-                    lev = checkPath(contains, paths[paths.length - 1], ty);
-                    if (lev > MAX_LEV_DISTANCE) {
-                        continue;
-                    } else if (lev > 0) {
-                        lev_add = lev / 10;
-                    }
-                }
 
-                returned = MAX_LEV_DISTANCE + 1;
-                in_args = MAX_LEV_DISTANCE + 1;
-                var index = -1;
-                // we want lev results to go lower than others
-                lev = MAX_LEV_DISTANCE + 1;
-                fullId = ty.id;
+            if (nbLev === 0) {
+                return;
+            }
+            const lev = Math.round(totalLev / nbLev);
+            addIntoResults(results, row.id, pos, 0, lev);
+        }
 
-                if (searchWords[j].indexOf(split[i]) > -1 ||
-                    searchWords[j].indexOf(val) > -1 ||
-                    ty.normalizedName.indexOf(val) > -1)
-                {
-                    // filter type: ... queries
-                    if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
-                        index = ty.normalizedName.indexOf(val);
+        function innerRunQuery() {
+            let elem, i, nSearchWords, in_returned, row;
+
+            if (parsedQuery.foundElems === 1) {
+                if (parsedQuery.elems.length === 1) {
+                    elem = parsedQuery.elems[0];
+                    for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                        // It means we want to check for this element everywhere (in names, args and
+                        // returned).
+                        handleSingleArg(
+                            searchIndex[i],
+                            i,
+                            elem,
+                            results_others,
+                            results_in_args,
+                            results_returned
+                        );
                     }
-                }
-                if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
-                    if (typePassesFilter(typeFilter, ty.ty)) {
-                        lev += 1;
-                    } else {
-                        lev = MAX_LEV_DISTANCE + 1;
+                } else if (parsedQuery.returned.length === 1) {
+                    // We received one returned argument to check, so looking into returned values.
+                    elem = parsedQuery.returned[0];
+                    for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                        row = searchIndex[i];
+                        in_returned = checkReturned(row, elem, parsedQuery.typeFilter);
+                        addIntoResults(results_returned, row.id, i, -1, in_returned);
                     }
                 }
-                in_args = findArg(ty, valGenerics, false, typeFilter);
-                returned = checkReturned(ty, valGenerics, false, typeFilter);
-
-                lev += lev_add;
-                if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
-                    if (val.length < 6) {
-                        lev -= 1;
-                    } else {
-                        lev = 0;
-                    }
+            } else if (parsedQuery.foundElems > 0) {
+                let container = results_others;
+                // In the special case where only a "returned" information is available, we want to
+                // put the information into the "results_returned" dict.
+                if (parsedQuery.returned.length !== 0 && parsedQuery.elems.length === 0) {
+                    container = results_returned;
                 }
-                addIntoResults(false, results_in_args, fullId, j, index, in_args);
-                addIntoResults(false, results_returned, fullId, j, index, returned);
-                if (typePassesFilter(typeFilter, ty.ty) &&
-                        (index !== -1 || lev <= MAX_LEV_DISTANCE)) {
-                    if (index !== -1 && paths.length < 2) {
-                        lev = 0;
-                    }
-                    addIntoResults(false, results, fullId, j, index, lev);
+                for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                    handleArgs(searchIndex[i], i, container);
                 }
             }
         }
 
-        var ret = {
-            "in_args": sortResults(results_in_args, true),
-            "returned": sortResults(results_returned, true),
-            "others": sortResults(results, false),
-        };
-        handleAliases(ret, query, filterCrates);
+        if (parsedQuery.error === null) {
+            innerRunQuery();
+        }
+
+        const ret = createQueryResults(
+            sortResults(results_in_args, true),
+            sortResults(results_returned, true),
+            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;
     }
 
@@ -892,53 +1453,33 @@ window.initSearch = function(rawSearchIndex) {
      * @param  {string} path   - The path of the result
      * @param  {string} keys   - The keys to be used (["file", "open"])
      * @param  {Object} parent - The parent of the result
+     *
      * @return {boolean}       - Whether the result is valid or not
      */
     function validateResult(name, path, keys, parent) {
-        for (var i = 0, len = keys.length; i < len; ++i) {
+        if (!keys || !keys.length) {
+            return true;
+        }
+        for (const key of keys) {
             // each check is for validation so we negate the conditions and invalidate
             if (!(
                 // check for an exact name match
-                name.indexOf(keys[i]) > -1 ||
+                name.indexOf(key) > -1 ||
                 // then an exact path match
-                path.indexOf(keys[i]) > -1 ||
+                path.indexOf(key) > -1 ||
                 // next if there is a parent, check for exact parent match
                 (parent !== undefined && parent.name !== undefined &&
-                    parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
+                    parent.name.toLowerCase().indexOf(key) > -1) ||
                 // lastly check to see if the name was a levenshtein match
-                levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
+                levenshtein(name, key) <= MAX_LEV_DISTANCE)) {
                 return false;
             }
         }
         return true;
     }
 
-    /**
-     * Parse a string into a query object.
-     *
-     * @param {string} raw - The text that the user typed.
-     * @returns {ParsedQuery}
-     */
-    function getQuery(raw) {
-        var matches, type = "", query;
-        query = raw;
-
-        matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
-        if (matches) {
-            type = matches[1].replace(/^const$/, "constant");
-            query = query.substring(matches[0].length);
-        }
-
-        return {
-            raw: raw,
-            query: query,
-            type: type,
-            id: query + type
-        };
-    }
-
     function nextTab(direction) {
-        var next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length;
+        const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length;
         searchState.focusedByTab[searchState.currentTab] = document.activeElement;
         printTab(next);
         focusSearchResult();
@@ -947,7 +1488,7 @@ window.initSearch = function(rawSearchIndex) {
     // Focus the first search result on the active tab, or the result that
     // was focused last time this tab was active.
     function focusSearchResult() {
-        var target = searchState.focusedByTab[searchState.currentTab] ||
+        const target = searchState.focusedByTab[searchState.currentTab] ||
             document.querySelectorAll(".search-results.active a").item(0) ||
             document.querySelectorAll("#titles > button").item(searchState.currentTab);
         if (target) {
@@ -956,11 +1497,11 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function buildHrefAndPath(item) {
-        var displayPath;
-        var href;
-        var type = itemTypes[item.ty];
-        var name = item.name;
-        var path = item.path;
+        let displayPath;
+        let href;
+        const type = itemTypes[item.ty];
+        const name = item.name;
+        let path = item.path;
 
         if (type === "mod") {
             displayPath = path + "::";
@@ -974,19 +1515,19 @@ window.initSearch = function(rawSearchIndex) {
             displayPath = "";
             href = window.rootPath + name + "/index.html";
         } else if (item.parent !== undefined) {
-            var myparent = item.parent;
-            var anchor = "#" + type + "." + name;
-            var parentType = itemTypes[myparent.ty];
-            var pageType = parentType;
-            var pageName = myparent.name;
+            const myparent = item.parent;
+            let anchor = "#" + type + "." + name;
+            const parentType = itemTypes[myparent.ty];
+            let pageType = parentType;
+            let pageName = myparent.name;
 
             if (parentType === "primitive") {
                 displayPath = myparent.name + "::";
             } else if (type === "structfield" && parentType === "variant") {
                 // Structfields belonging to variants are special: the
                 // final path element is the enum name.
-                var enumNameIdx = item.path.lastIndexOf("::");
-                var enumName = item.path.substr(enumNameIdx + 2);
+                const enumNameIdx = item.path.lastIndexOf("::");
+                const enumName = item.path.substr(enumNameIdx + 2);
                 path = item.path.substr(0, enumNameIdx);
                 displayPath = path + "::" + enumName + "::" + myparent.name + "::";
                 anchor = "#variant." + myparent.name + ".field." + name;
@@ -1008,13 +1549,13 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function escape(content) {
-        var h1 = document.createElement("h1");
+        const h1 = document.createElement("h1");
         h1.textContent = content;
         return h1.innerHTML;
     }
 
     function pathSplitter(path) {
-        var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
+        const tmp = "<span>" + path.replace(/::/g, "::</span><span>");
         if (tmp.endsWith("<span>")) {
             return tmp.slice(0, tmp.length - 6);
         }
@@ -1028,42 +1569,42 @@ window.initSearch = function(rawSearchIndex) {
      * @param {boolean}     display - True if this is the active tab
      */
     function addTab(array, query, display) {
-        var extraClass = "";
+        let extraClass = "";
         if (display === true) {
             extraClass = " active";
         }
 
-        var output = document.createElement("div");
-        var length = 0;
+        const output = document.createElement("div");
+        let length = 0;
         if (array.length > 0) {
             output.className = "search-results " + extraClass;
 
             array.forEach(function(item) {
-                var name = item.name;
-                var type = itemTypes[item.ty];
+                const name = item.name;
+                const type = itemTypes[item.ty];
 
                 length += 1;
 
-                var extra = "";
+                let extra = "";
                 if (type === "primitive") {
                     extra = " <i>(primitive type)</i>";
                 } else if (type === "keyword") {
                     extra = " <i>(keyword)</i>";
                 }
 
-                var link = document.createElement("a");
+                const link = document.createElement("a");
                 link.className = "result-" + type;
                 link.href = item.href;
 
-                var wrapper = document.createElement("div");
-                var resultName = document.createElement("div");
+                const wrapper = document.createElement("div");
+                const resultName = document.createElement("div");
                 resultName.className = "result-name";
 
                 if (item.is_alias) {
-                    var alias = document.createElement("span");
+                    const alias = document.createElement("span");
                     alias.className = "alias";
 
-                    var bold = document.createElement("b");
+                    const bold = document.createElement("b");
                     bold.innerText = item.alias;
                     alias.appendChild(bold);
 
@@ -1078,9 +1619,9 @@ window.initSearch = function(rawSearchIndex) {
                     item.displayPath + "<span class=\"" + type + "\">" + name + extra + "</span>");
                 wrapper.appendChild(resultName);
 
-                var description = document.createElement("div");
+                const description = document.createElement("div");
                 description.className = "desc";
-                var spanDesc = document.createElement("span");
+                const spanDesc = document.createElement("span");
                 spanDesc.insertAdjacentHTML("beforeend", item.desc);
 
                 description.appendChild(spanDesc);
@@ -1088,11 +1629,11 @@ window.initSearch = function(rawSearchIndex) {
                 link.appendChild(wrapper);
                 output.appendChild(link);
             });
-        } else {
+        } else if (query.error === null) {
             output.className = "search-failed" + extraClass;
             output.innerHTML = "No results :(<br/>" +
                 "Try on <a href=\"https://duckduckgo.com/?q=" +
-                encodeURIComponent("rust " + query.query) +
+                encodeURIComponent("rust " + query.userQuery) +
                 "\">DuckDuckGo</a>?<br/><br/>" +
                 "Or try looking in one of these:<ul><li>The <a " +
                 "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
@@ -1115,8 +1656,13 @@ window.initSearch = function(rawSearchIndex) {
         return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
     }
 
+    /**
+     * @param {ResultsTable} results
+     * @param {boolean} go_to_first
+     * @param {string} filterCrates
+     */
     function showResults(results, go_to_first, filterCrates) {
-        var search = searchState.outputElement();
+        const search = searchState.outputElement();
         if (go_to_first || (results.others.length === 1
             && getSettingValue("go-to-only-result") === "true"
             // By default, the search DOM element is "empty" (meaning it has no children not
@@ -1124,7 +1670,7 @@ window.initSearch = function(rawSearchIndex) {
             // ESC or empty the search input (which also "cancels" the search).
             && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)))
         {
-            var elem = document.createElement("a");
+            const elem = document.createElement("a");
             elem.href = results.others[0].href;
             removeClass(elem, "active");
             // For firefox, we need the element to be in the DOM so it can be clicked.
@@ -1132,18 +1678,20 @@ window.initSearch = function(rawSearchIndex) {
             elem.click();
             return;
         }
-        var query = getQuery(searchState.input.value);
+        if (results.query === undefined) {
+            results.query = parseQuery(searchState.input.value);
+        }
 
-        currentResults = query.id;
+        currentResults = results.query.userQuery;
 
-        var ret_others = addTab(results.others, query, true);
-        var ret_in_args = addTab(results.in_args, query, false);
-        var ret_returned = addTab(results.returned, query, false);
+        const ret_others = addTab(results.others, results.query, true);
+        const ret_in_args = addTab(results.in_args, results.query, false);
+        const ret_returned = addTab(results.returned, results.query, false);
 
         // Navigate to the relevant tab if the current tab is empty, like in case users search
         // for "-> String". If they had selected another tab previously, they have to click on
         // it again.
-        var currentTab = searchState.currentTab;
+        let currentTab = searchState.currentTab;
         if ((currentTab === 0 && ret_others[1] === 0) ||
                 (currentTab === 1 && ret_in_args[1] === 0) ||
                 (currentTab === 2 && ret_returned[1] === 0)) {
@@ -1159,29 +1707,37 @@ window.initSearch = function(rawSearchIndex) {
         let crates = "";
         if (window.ALL_CRATES.length > 1) {
             crates = ` in <select id="crate-search"><option value="All crates">All crates</option>`;
-            for (let c of window.ALL_CRATES) {
+            for (const c of window.ALL_CRATES) {
                 crates += `<option value="${c}" ${c == filterCrates && "selected"}>${c}</option>`;
             }
             crates += `</select>`;
         }
-        var output = `<div id="search-settings">
-            <h1 class="search-results-title">Results for ${escape(query.query)} ` +
-            (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
-            crates +
-            `</div><div id="titles">` +
+
+        let typeFilter = "";
+        if (results.query.typeFilter !== NO_TYPE_FILTER) {
+            typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")";
+        }
+
+        let output = `<div id="search-settings">` +
+            `<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` +
+            `${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]) +
             "</div>";
 
-        var resultsElem = document.createElement("div");
+        const resultsElem = document.createElement("div");
         resultsElem.id = "results";
         resultsElem.appendChild(ret_others[0]);
         resultsElem.appendChild(ret_in_args[0]);
         resultsElem.appendChild(ret_returned[0]);
 
         search.innerHTML = output;
-        let crateSearch = document.getElementById("crate-search");
+        const crateSearch = document.getElementById("crate-search");
         if (crateSearch) {
             crateSearch.addEventListener("input", updateCrate);
         }
@@ -1189,35 +1745,13 @@ window.initSearch = function(rawSearchIndex) {
         // Reset focused elements.
         searchState.focusedByTab = [null, null, null];
         searchState.showResults(search);
-        var elems = document.getElementById("titles").childNodes;
+        const elems = document.getElementById("titles").childNodes;
         elems[0].onclick = function() { printTab(0); };
         elems[1].onclick = function() { printTab(1); };
         elems[2].onclick = function() { printTab(2); };
         printTab(currentTab);
     }
 
-    function execSearch(query, searchWords, filterCrates) {
-        query = query.raw.trim();
-        var results = {
-            "in_args": [],
-            "returned": [],
-            "others": [],
-        };
-
-        if (query.length !== 0) {
-            var tmp = execQuery(getQuery(query), searchWords, filterCrates);
-
-            results.in_args.push(tmp.in_args);
-            results.returned.push(tmp.returned);
-            results.others.push(tmp.others);
-        }
-        return {
-            "in_args": results.in_args[0],
-            "returned": results.returned[0],
-            "others": results.others[0],
-        };
-    }
-
     /**
      * Perform a search based on the current state of the search input element
      * and display the results.
@@ -1225,24 +1759,21 @@ window.initSearch = function(rawSearchIndex) {
      * @param {boolean} [forced]
      */
     function search(e, forced) {
-        var params = searchState.getQueryStringParams();
-        var query = getQuery(searchState.input.value.trim());
+        const params = searchState.getQueryStringParams();
+        const query = parseQuery(searchState.input.value.trim());
 
         if (e) {
             e.preventDefault();
         }
 
-        if (query.query.length === 0) {
-            return;
-        }
-        if (!forced && query.id === currentResults) {
-            if (query.query.length > 0) {
+        if (!forced && query.userQuery === currentResults) {
+            if (query.userQuery.length > 0) {
                 putBackSearch();
             }
             return;
         }
 
-        var filterCrates = getFilterCrates();
+        let filterCrates = getFilterCrates();
 
         // In case we have no information about the saved crate and there is a URL query parameter,
         // we override it with the URL query parameter.
@@ -1251,13 +1782,12 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         // Update document title to maintain a meaningful browser history
-        searchState.title = "Results for " + query.query + " - Rust";
+        searchState.title = "Results for " + query.original + " - Rust";
 
         // Because searching is incremental by character, only the most
         // recent search query is added to the browser history.
         if (searchState.browserSupportsHistoryApi()) {
-            var newURL = buildUrl(query.raw, filterCrates);
-
+            const newURL = buildUrl(query.original, filterCrates);
             if (!history.state && !params.search) {
                 history.pushState(null, "", newURL);
             } else {
@@ -1265,8 +1795,10 @@ window.initSearch = function(rawSearchIndex) {
             }
         }
 
-        showResults(execSearch(query, searchWords, filterCrates),
-            params["go_to_first"], filterCrates);
+        showResults(
+            execQuery(query, searchWords, filterCrates),
+            params.go_to_first,
+            filterCrates);
     }
 
     function buildIndex(rawSearchIndex) {
@@ -1274,17 +1806,17 @@ window.initSearch = function(rawSearchIndex) {
         /**
          * @type {Array<string>}
          */
-        var searchWords = [];
-        var i, word;
-        var currentIndex = 0;
-        var id = 0;
+        const searchWords = [];
+        let i, word;
+        let currentIndex = 0;
+        let id = 0;
 
-        for (var crate in rawSearchIndex) {
+        for (const crate in rawSearchIndex) {
             if (!hasOwnPropertyRustdoc(rawSearchIndex, crate)) {
                 continue;
             }
 
-            var crateSize = 0;
+            let crateSize = 0;
 
             /**
              * The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f`
@@ -1316,13 +1848,13 @@ window.initSearch = function(rawSearchIndex) {
              *   p: Array<Object>,
              * }}
              */
-            var crateCorpus = rawSearchIndex[crate];
+            const crateCorpus = rawSearchIndex[crate];
 
             searchWords.push(crate);
             // This object should have exactly the same set of fields as the "row"
             // object defined below. Your JavaScript runtime will thank you.
             // https://mathiasbynens.be/notes/shapes-ics
-            var crateRow = {
+            const crateRow = {
                 crate: crate,
                 ty: 1, // == ExternCrate
                 name: crate,
@@ -1338,26 +1870,26 @@ window.initSearch = function(rawSearchIndex) {
             currentIndex += 1;
 
             // an array of (Number) item types
-            var itemTypes = crateCorpus.t;
+            const itemTypes = crateCorpus.t;
             // an array of (String) item names
-            var itemNames = crateCorpus.n;
+            const itemNames = crateCorpus.n;
             // an array of (String) full paths (or empty string for previous path)
-            var itemPaths = crateCorpus.q;
+            const itemPaths = crateCorpus.q;
             // an array of (String) descriptions
-            var itemDescs = crateCorpus.d;
+            const itemDescs = crateCorpus.d;
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
-            var itemParentIdxs = crateCorpus.i;
+            const itemParentIdxs = crateCorpus.i;
             // an array of (Object | null) the type of the function, if any
-            var itemFunctionSearchTypes = crateCorpus.f;
+            const itemFunctionSearchTypes = crateCorpus.f;
             // an array of [(Number) item type,
             //              (String) name]
-            var paths = crateCorpus.p;
+            const paths = crateCorpus.p;
             // an array of [(String) alias name
             //             [Number] index to items]
-            var aliases = crateCorpus.a;
+            const aliases = crateCorpus.a;
 
             // convert `rawPaths` entries into object form
-            var len = paths.length;
+            let len = paths.length;
             for (i = 0; i < len; ++i) {
                 paths[i] = {ty: paths[i][0], name: paths[i][1]};
             }
@@ -1370,7 +1902,7 @@ window.initSearch = function(rawSearchIndex) {
             // all other search operations have access to this cached data for
             // faster analysis operations
             len = itemTypes.length;
-            var lastPath = "";
+            let lastPath = "";
             for (i = 0; i < len; ++i) {
                 // This object should have exactly the same set of fields as the "crateRow"
                 // object defined above.
@@ -1381,7 +1913,7 @@ window.initSearch = function(rawSearchIndex) {
                     word = "";
                     searchWords.push("");
                 }
-                var row = {
+                const row = {
                     crate: crate,
                     ty: itemTypes[i],
                     name: itemNames[i],
@@ -1400,8 +1932,7 @@ window.initSearch = function(rawSearchIndex) {
 
             if (aliases) {
                 ALIASES[crate] = {};
-                var j, local_aliases;
-                for (var alias_name in aliases) {
+                for (const alias_name in aliases) {
                     if (!hasOwnPropertyRustdoc(aliases, alias_name)) {
                         continue;
                     }
@@ -1409,9 +1940,8 @@ window.initSearch = function(rawSearchIndex) {
                     if (!hasOwnPropertyRustdoc(ALIASES[crate], alias_name)) {
                         ALIASES[crate][alias_name] = [];
                     }
-                    local_aliases = aliases[alias_name];
-                    for (j = 0, len = local_aliases.length; j < len; ++j) {
-                        ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
+                    for (const local_alias of aliases[alias_name]) {
+                        ALIASES[crate][alias_name].push(local_alias + currentIndex);
                     }
                 }
             }
@@ -1431,11 +1961,11 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function putBackSearch() {
-        var search_input = searchState.input;
+        const search_input = searchState.input;
         if (!searchState.input) {
             return;
         }
-        var search = searchState.outputElement();
+        const search = searchState.outputElement();
         if (search_input.value !== "" && hasClass(search, "hidden")) {
             searchState.showResults(search);
             if (searchState.browserSupportsHistoryApi()) {
@@ -1447,7 +1977,7 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function registerSearchEvents() {
-        var searchAfter500ms = function() {
+        const searchAfter500ms = function() {
             searchState.clearInputTimeout();
             if (searchState.input.value.length === 0) {
                 if (searchState.browserSupportsHistoryApi()) {
@@ -1485,7 +2015,7 @@ window.initSearch = function(rawSearchIndex) {
             // up and down arrow select next/previous search result, or the
             // search box if we're already at the top.
             if (e.which === 38) { // up
-                var previous = document.activeElement.previousElementSibling;
+                const previous = document.activeElement.previousElementSibling;
                 if (previous) {
                     previous.focus();
                 } else {
@@ -1493,11 +2023,11 @@ window.initSearch = function(rawSearchIndex) {
                 }
                 e.preventDefault();
             } else if (e.which === 40) { // down
-                var next = document.activeElement.nextElementSibling;
+                const next = document.activeElement.nextElementSibling;
                 if (next) {
                     next.focus();
                 }
-                var rect = document.activeElement.getBoundingClientRect();
+                const rect = document.activeElement.getBoundingClientRect();
                 if (window.innerHeight - rect.bottom < rect.height) {
                     window.scrollBy(0, rect.height);
                 }
@@ -1530,10 +2060,10 @@ window.initSearch = function(rawSearchIndex) {
         // history.
         if (searchState.browserSupportsHistoryApi()) {
             // Store the previous <title> so we can revert back to it later.
-            var previousTitle = document.title;
+            const previousTitle = document.title;
 
             window.addEventListener("popstate", function(e) {
-                var params = searchState.getQueryStringParams();
+                const params = searchState.getQueryStringParams();
                 // Revert to the previous title manually since the History
                 // API ignores the title parameter.
                 document.title = previousTitle;
@@ -1569,7 +2099,7 @@ window.initSearch = function(rawSearchIndex) {
         // that try to sync state between the URL and the search input. To work around it,
         // do a small amount of re-init on page show.
         window.onpageshow = function(){
-            var qSearch = searchState.getQueryStringParams().search;
+            const qSearch = searchState.getQueryStringParams().search;
             if (searchState.input.value === "" && qSearch) {
                 searchState.input.value = qSearch;
             }
@@ -1580,8 +2110,8 @@ window.initSearch = function(rawSearchIndex) {
     function updateCrate(ev) {
         if (ev.target.value === "All crates") {
             // If we don't remove it from the URL, it'll be picked up again by the search.
-            var params = searchState.getQueryStringParams();
-            var query = searchState.input.value.trim();
+            const params = searchState.getQueryStringParams();
+            const query = searchState.input.value.trim();
             if (!history.state && !params.search) {
                 history.pushState(null, "", buildUrl(query, null));
             } else {
@@ -1595,7 +2125,10 @@ window.initSearch = function(rawSearchIndex) {
         search(undefined, true);
     }
 
-    searchWords = buildIndex(rawSearchIndex);
+    /**
+     *  @type {Array<string>}
+     */
+    const searchWords = buildIndex(rawSearchIndex);
     registerSearchEvents();
 
     function runSearchIfNeeded() {
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 139fa5c9a11..7bc6f6cfe04 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -1,3 +1,6 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
 // Local js definitions:
 /* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */
 /* global addClass, removeClass */
@@ -55,9 +58,9 @@
     function setEvents() {
         updateLightAndDark();
         onEachLazy(document.getElementsByClassName("slider"), function(elem) {
-            var toggle = elem.previousElementSibling;
-            var settingId = toggle.id;
-            var settingValue = getSettingValue(settingId);
+            const toggle = elem.previousElementSibling;
+            const settingId = toggle.id;
+            const settingValue = getSettingValue(settingId);
             if (settingValue !== null) {
                 toggle.checked = settingValue === "true";
             }
@@ -68,9 +71,9 @@
             toggle.onkeyrelease = handleKey;
         });
         onEachLazy(document.getElementsByClassName("select-wrapper"), function(elem) {
-            var select = elem.getElementsByTagName("select")[0];
-            var settingId = select.id;
-            var settingValue = getSettingValue(settingId);
+            const select = elem.getElementsByTagName("select")[0];
+            const settingId = select.id;
+            const settingValue = getSettingValue(settingId);
             if (settingValue !== null) {
                 select.value = settingValue;
             }
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index aa77e62ba5a..c48a847665e 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -1,3 +1,7 @@
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+
 // From rust:
 /* global search, sourcesIndex */
 
@@ -7,15 +11,15 @@
 (function() {
 
 function getCurrentFilePath() {
-    var parts = window.location.pathname.split("/");
-    var rootPathParts = window.rootPath.split("/");
+    const parts = window.location.pathname.split("/");
+    const rootPathParts = window.rootPath.split("/");
 
-    for (var i = 0, len = rootPathParts.length; i < len; ++i) {
-        if (rootPathParts[i] === "..") {
+    for (const rootPathPart of rootPathParts) {
+        if (rootPathPart === "..") {
             parts.pop();
         }
     }
-    var file = window.location.pathname.substring(parts.join("/").length);
+    let file = window.location.pathname.substring(parts.join("/").length);
     if (file.startsWith("/")) {
         file = file.substring(1);
     }
@@ -23,7 +27,7 @@ function getCurrentFilePath() {
 }
 
 function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
-    var name = document.createElement("div");
+    const name = document.createElement("div");
     name.className = "name";
 
     fullPath += elem["name"] + "/";
@@ -37,16 +41,13 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
     };
     name.innerText = elem["name"];
 
-    var i, len;
-
-    var children = document.createElement("div");
+    const children = document.createElement("div");
     children.className = "children";
-    var folders = document.createElement("div");
+    const folders = document.createElement("div");
     folders.className = "folders";
     if (elem.dirs) {
-        for (i = 0, len = elem.dirs.length; i < len; ++i) {
-            if (createDirEntry(elem.dirs[i], folders, fullPath, currentFile,
-                               hasFoundFile)) {
+        for (const dir of elem.dirs) {
+            if (createDirEntry(dir, folders, fullPath, currentFile, hasFoundFile)) {
                 addClass(name, "expand");
                 hasFoundFile = true;
             }
@@ -54,14 +55,14 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
     }
     children.appendChild(folders);
 
-    var files = document.createElement("div");
+    const files = document.createElement("div");
     files.className = "files";
     if (elem.files) {
-        for (i = 0, len = elem.files.length; i < len; ++i) {
-            var file = document.createElement("a");
-            file.innerText = elem.files[i];
-            file.href = window.rootPath + "src/" + fullPath + elem.files[i] + ".html";
-            if (!hasFoundFile && currentFile === fullPath + elem.files[i]) {
+        for (const file_text of elem.files) {
+            const file = document.createElement("a");
+            file.innerText = file_text;
+            file.href = window.rootPath + "src/" + fullPath + file_text + ".html";
+            if (!hasFoundFile && currentFile === fullPath + file_text) {
                 file.className = "selected";
                 addClass(name, "expand");
                 hasFoundFile = true;
@@ -77,8 +78,8 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
 }
 
 function toggleSidebar() {
-    var sidebar = document.querySelector("nav.sidebar");
-    var child = this.children[0];
+    const sidebar = document.querySelector("nav.sidebar");
+    const child = this.children[0];
     if (child.innerText === ">") {
         sidebar.classList.add("expanded");
         child.innerText = "<";
@@ -91,11 +92,11 @@ function toggleSidebar() {
 }
 
 function createSidebarToggle() {
-    var sidebarToggle = document.createElement("div");
+    const sidebarToggle = document.createElement("div");
     sidebarToggle.id = "sidebar-toggle";
     sidebarToggle.onclick = toggleSidebar;
 
-    var inner = document.createElement("div");
+    const inner = document.createElement("div");
 
     if (getCurrentValue("source-sidebar-show") === "true") {
         inner.innerText = "<";
@@ -113,12 +114,12 @@ function createSourceSidebar() {
     if (!window.rootPath.endsWith("/")) {
         window.rootPath += "/";
     }
-    var container = document.querySelector("nav.sidebar");
+    const container = document.querySelector("nav.sidebar");
 
-    var sidebarToggle = createSidebarToggle();
+    const sidebarToggle = createSidebarToggle();
     container.insertBefore(sidebarToggle, container.firstChild);
 
-    var sidebar = document.createElement("div");
+    const sidebar = document.createElement("div");
     sidebar.id = "source-sidebar";
     if (getCurrentValue("source-sidebar-show") !== "true") {
         container.classList.remove("expanded");
@@ -126,10 +127,10 @@ function createSourceSidebar() {
         container.classList.add("expanded");
     }
 
-    var currentFile = getCurrentFilePath();
-    var hasFoundFile = false;
+    const currentFile = getCurrentFilePath();
+    let hasFoundFile = false;
 
-    var title = document.createElement("div");
+    const title = document.createElement("div");
     title.className = "title";
     title.innerText = "Files";
     sidebar.appendChild(title);
@@ -141,13 +142,13 @@ function createSourceSidebar() {
 
     container.appendChild(sidebar);
     // Focus on the current file in the source files sidebar.
-    var selected_elem = sidebar.getElementsByClassName("selected")[0];
+    const selected_elem = sidebar.getElementsByClassName("selected")[0];
     if (typeof selected_elem !== "undefined") {
         selected_elem.focus();
     }
 }
 
-var lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
+const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
 
 function highlightSourceLines(match) {
     if (typeof match === "undefined") {
@@ -156,21 +157,21 @@ function highlightSourceLines(match) {
     if (!match) {
         return;
     }
-    var from = parseInt(match[1], 10);
-    var to = from;
+    let from = parseInt(match[1], 10);
+    let to = from;
     if (typeof match[2] !== "undefined") {
         to = parseInt(match[2], 10);
     }
     if (to < from) {
-        var tmp = to;
+        const tmp = to;
         to = from;
         from = tmp;
     }
-    var elem = document.getElementById(from);
+    let elem = document.getElementById(from);
     if (!elem) {
         return;
     }
-    var x = document.getElementById(from);
+    const x = document.getElementById(from);
     if (x) {
         x.scrollIntoView();
     }
@@ -179,7 +180,7 @@ function highlightSourceLines(match) {
             removeClass(i_e, "line-highlighted");
         });
     });
-    for (var i = from; i <= to; ++i) {
+    for (let i = from; i <= to; ++i) {
         elem = document.getElementById(i);
         if (!elem) {
             break;
@@ -188,11 +189,11 @@ function highlightSourceLines(match) {
     }
 }
 
-var handleSourceHighlight = (function() {
-    var prev_line_id = 0;
+const handleSourceHighlight = (function() {
+    let prev_line_id = 0;
 
-    var set_fragment = function(name) {
-        var x = window.scrollX,
+    const set_fragment = function(name) {
+        const x = window.scrollX,
             y = window.scrollY;
         if (searchState.browserSupportsHistoryApi()) {
             history.replaceState(null, null, "#" + name);
@@ -205,13 +206,13 @@ var handleSourceHighlight = (function() {
     };
 
     return function(ev) {
-        var cur_line_id = parseInt(ev.target.id, 10);
+        let cur_line_id = parseInt(ev.target.id, 10);
         ev.preventDefault();
 
         if (ev.shiftKey && prev_line_id) {
             // Swap selection if needed
             if (prev_line_id > cur_line_id) {
-                var tmp = prev_line_id;
+                const tmp = prev_line_id;
                 prev_line_id = cur_line_id;
                 cur_line_id = tmp;
             }
@@ -226,7 +227,7 @@ var handleSourceHighlight = (function() {
 }());
 
 window.addEventListener("hashchange", function() {
-    var match = window.location.hash.match(lineNumbersRegex);
+    const match = window.location.hash.match(lineNumbersRegex);
     if (match) {
         return highlightSourceLines(match);
     }
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index ccf3d0a581a..ae670ed9894 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -1,13 +1,17 @@
-var darkThemes = ["dark", "ayu"];
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+
+const darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
 window.mainTheme = document.getElementById("mainThemeStyle");
 
-var settingsDataset = (function () {
-    var settingsElement = document.getElementById("default-settings");
+const settingsDataset = (function () {
+    const settingsElement = document.getElementById("default-settings");
     if (settingsElement === null) {
         return null;
     }
-    var dataset = settingsElement.dataset;
+    const dataset = settingsElement.dataset;
     if (dataset === undefined) {
         return null;
     }
@@ -15,14 +19,14 @@ var settingsDataset = (function () {
 })();
 
 function getSettingValue(settingName) {
-    var current = getCurrentValue(settingName);
+    const current = getCurrentValue(settingName);
     if (current !== null) {
         return current;
     }
     if (settingsDataset !== null) {
         // See the comment for `default_settings.into_iter()` etc. in
         // `Options::from_matches` in `librustdoc/config.rs`.
-        var def = settingsDataset[settingName.replace(/-/g,'_')];
+        const def = settingsDataset[settingName.replace(/-/g,'_')];
         if (def !== undefined) {
             return def;
         }
@@ -30,9 +34,9 @@ function getSettingValue(settingName) {
     return null;
 }
 
-var localStoredTheme = getSettingValue("theme");
+const localStoredTheme = getSettingValue("theme");
 
-var savedHref = [];
+const savedHref = [];
 
 // eslint-disable-next-line no-unused-vars
 function hasClass(elem, className) {
@@ -63,17 +67,16 @@ function removeClass(elem, className) {
  */
 function onEach(arr, func, reversed) {
     if (arr && arr.length > 0 && func) {
-        var length = arr.length;
-        var i;
         if (reversed) {
-            for (i = length - 1; i >= 0; --i) {
+            const length = arr.length;
+            for (let i = length - 1; i >= 0; --i) {
                 if (func(arr[i])) {
                     return true;
                 }
             }
         } else {
-            for (i = 0; i < length; ++i) {
-                if (func(arr[i])) {
+            for (const elem of arr) {
+                if (func(elem)) {
                     return true;
                 }
             }
@@ -121,7 +124,7 @@ function getCurrentValue(name) {
 }
 
 function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
-    var newHref = mainStyleElem.href.replace(
+    const newHref = mainStyleElem.href.replace(
         /\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
 
     // If this new value comes from a system setting or from the previously
@@ -134,7 +137,7 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
         return;
     }
 
-    var found = false;
+    let found = false;
     if (savedHref.length === 0) {
         onEachLazy(document.getElementsByTagName("link"), function(el) {
             savedHref.push(el.href);
@@ -161,17 +164,17 @@ function useSystemTheme(value) {
     updateLocalStorage("use-system-theme", value);
 
     // update the toggle if we're on the settings page
-    var toggle = document.getElementById("use-system-theme");
+    const toggle = document.getElementById("use-system-theme");
     if (toggle && toggle instanceof HTMLInputElement) {
         toggle.checked = value;
     }
 }
 
-var updateSystemTheme = (function() {
+const updateSystemTheme = (function() {
     if (!window.matchMedia) {
         // fallback to the CSS computed value
         return function() {
-            var cssTheme = getComputedStyle(document.documentElement)
+            const cssTheme = getComputedStyle(document.documentElement)
                 .getPropertyValue('content');
 
             switchTheme(
@@ -184,16 +187,16 @@ var updateSystemTheme = (function() {
     }
 
     // only listen to (prefers-color-scheme: dark) because light is the default
-    var mql = window.matchMedia("(prefers-color-scheme: dark)");
+    const mql = window.matchMedia("(prefers-color-scheme: dark)");
 
     function handlePreferenceChange(mql) {
-        let use = function(theme) {
+        const use = function(theme) {
             switchTheme(window.currentTheme, window.mainTheme, theme, true);
         };
         // maybe the user has disabled the setting in the meantime!
         if (getSettingValue("use-system-theme") !== "false") {
-            var lightTheme = getSettingValue("preferred-light-theme") || "light";
-            var darkTheme = getSettingValue("preferred-dark-theme") || "dark";
+            const lightTheme = getSettingValue("preferred-light-theme") || "light";
+            const darkTheme = getSettingValue("preferred-dark-theme") || "dark";
 
             if (mql.matches) {
                 use(darkTheme);