about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/constraints/graph.rs1
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs7
-rw-r--r--compiler/rustc_borrowck/src/region_infer/dump_mir.rs8
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs7
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs6
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs1
-rw-r--r--library/core/src/intrinsics.rs8
-rw-r--r--library/std/src/sys/unix/os.rs22
-rw-r--r--src/bootstrap/builder.rs12
-rw-r--r--src/librustdoc/html/static/js/externs.js59
-rw-r--r--src/librustdoc/html/static/js/search.js1376
-rw-r--r--src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir16
-rw-r--r--src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir4
-rw-r--r--src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir4
-rw-r--r--src/test/mir-opt/storage_ranges.main.nll.0.mir2
-rw-r--r--src/test/rustdoc-js-std/filter-crate.js2
-rw-r--r--src/test/rustdoc-js-std/parser-errors.js365
-rw-r--r--src/test/rustdoc-js-std/parser-filter.js43
-rw-r--r--src/test/rustdoc-js-std/parser-generics.js62
-rw-r--r--src/test/rustdoc-js-std/parser-literal.js27
-rw-r--r--src/test/rustdoc-js-std/parser-paths.js90
-rw-r--r--src/test/rustdoc-js-std/parser-quote.js87
-rw-r--r--src/test/rustdoc-js-std/parser-returned.js78
-rw-r--r--src/test/rustdoc-js-std/parser-separators.js206
-rw-r--r--src/test/rustdoc-js-std/parser-weird-queries.js123
-rw-r--r--src/test/rustdoc-js-std/quoted.js10
-rw-r--r--src/test/rustdoc-js-std/struct-vec.js4
-rw-r--r--src/test/rustdoc-js-std/typed-query.js5
-rw-r--r--src/test/rustdoc-js-std/vec-new.js2
-rw-r--r--src/test/rustdoc-js/doc-alias-filter.js2
-rw-r--r--src/test/rustdoc-js/doc-alias.js20
-rw-r--r--src/test/rustdoc-js/generics.js23
-rw-r--r--src/test/rustdoc-js/generics.rs2
-rw-r--r--src/test/ui/rfc1623.base.stderr2
-rw-r--r--src/test/ui/rfc1623.nll.stderr52
-rw-r--r--src/test/ui/rfc1623.rs8
-rw-r--r--src/tools/rustdoc-js/tester.js170
40 files changed, 2389 insertions, 531 deletions
diff --git a/compiler/rustc_borrowck/src/constraints/graph.rs b/compiler/rustc_borrowck/src/constraints/graph.rs
index cb9e0234c49..4ceca60e23c 100644
--- a/compiler/rustc_borrowck/src/constraints/graph.rs
+++ b/compiler/rustc_borrowck/src/constraints/graph.rs
@@ -156,6 +156,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> Iterator for Edges<'s, 'tcx, D> {
                 sup: self.static_region,
                 sub: next_static_idx.into(),
                 locations: Locations::All(DUMMY_SP),
+                span: DUMMY_SP,
                 category: ConstraintCategory::Internal,
                 variance_info: VarianceDiagInfo::default(),
             })
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index d41143ee763..14f0e5f620a 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -2,6 +2,7 @@ use rustc_data_structures::graph::scc::Sccs;
 use rustc_index::vec::IndexVec;
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
+use rustc_span::Span;
 use std::fmt;
 use std::ops::Index;
 
@@ -87,6 +88,12 @@ pub struct OutlivesConstraint<'tcx> {
     /// Where did this constraint arise?
     pub locations: Locations,
 
+    /// The `Span` associated with the creation of this constraint.
+    /// This should be used in preference to obtaining the span from
+    /// `locations`, since the `locations` may give a poor span
+    /// in some cases (e.g. converting a constraint from a promoted).
+    pub span: Span,
+
     /// What caused this constraint?
     pub category: ConstraintCategory,
 
diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
index 97233b930c3..fe5193102f9 100644
--- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
+++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
@@ -74,14 +74,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
         constraints.sort_by_key(|c| (c.sup, c.sub));
         for constraint in &constraints {
-            let OutlivesConstraint { sup, sub, locations, category, variance_info: _ } = constraint;
+            let OutlivesConstraint { sup, sub, locations, category, span, variance_info: _ } =
+                constraint;
             let (name, arg) = match locations {
                 Locations::All(span) => {
                     ("All", tcx.sess.source_map().span_to_embeddable_string(*span))
                 }
                 Locations::Single(loc) => ("Single", format!("{:?}", loc)),
             };
-            with_msg(&format!("{:?}: {:?} due to {:?} at {}({})", sup, sub, category, name, arg))?;
+            with_msg(&format!(
+                "{:?}: {:?} due to {:?} at {}({}) ({:?}",
+                sup, sub, category, name, arg, span
+            ))?;
         }
 
         Ok(())
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index dabf61715ce..cf03f34a4ec 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1733,7 +1733,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
     crate fn retrieve_closure_constraint_info(
         &self,
-        body: &Body<'tcx>,
+        _body: &Body<'tcx>,
         constraint: &OutlivesConstraint<'tcx>,
     ) -> BlameConstraint<'tcx> {
         let loc = match constraint.locations {
@@ -1760,7 +1760,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             .unwrap_or(BlameConstraint {
                 category: constraint.category,
                 from_closure: false,
-                cause: ObligationCause::dummy_with_span(body.source_info(loc).span),
+                cause: ObligationCause::dummy_with_span(constraint.span),
                 variance_info: constraint.variance_info,
             })
     }
@@ -1869,6 +1869,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     sup: r,
                     sub: constraint.min_choice,
                     locations: Locations::All(p_c.definition_span),
+                    span: p_c.definition_span,
                     category: ConstraintCategory::OpaqueType,
                     variance_info: ty::VarianceDiagInfo::default(),
                 };
@@ -2017,7 +2018,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                         category: constraint.category,
                         from_closure: false,
                         cause: ObligationCause::new(
-                            constraint.locations.span(body),
+                            constraint.span,
                             CRATE_HIR_ID,
                             cause_code.clone(),
                         ),
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index 5022cb98b82..21190a850b7 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -8,7 +8,7 @@ use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_span::DUMMY_SP;
+use rustc_span::{Span, DUMMY_SP};
 
 use crate::{
     constraints::OutlivesConstraint,
@@ -26,6 +26,7 @@ crate struct ConstraintConversion<'a, 'tcx> {
     implicit_region_bound: Option<ty::Region<'tcx>>,
     param_env: ty::ParamEnv<'tcx>,
     locations: Locations,
+    span: Span,
     category: ConstraintCategory,
     constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
 }
@@ -38,6 +39,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         implicit_region_bound: Option<ty::Region<'tcx>>,
         param_env: ty::ParamEnv<'tcx>,
         locations: Locations,
+        span: Span,
         category: ConstraintCategory,
         constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
     ) -> Self {
@@ -49,6 +51,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
             implicit_region_bound,
             param_env,
             locations,
+            span,
             category,
             constraints,
         }
@@ -153,6 +156,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         self.constraints.outlives_constraints.push(OutlivesConstraint {
             locations: self.locations,
             category: self.category,
+            span: self.span,
             sub,
             sup,
             variance_info: ty::VarianceDiagInfo::default(),
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index f8439d2e163..f08f2e1b12d 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -316,6 +316,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                 self.implicit_region_bound,
                 self.param_env,
                 Locations::All(DUMMY_SP),
+                DUMMY_SP,
                 ConstraintCategory::Internal,
                 &mut self.constraints,
             )
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 83c8ecba1f1..e6f996491a4 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -235,6 +235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 Some(self.implicit_region_bound),
                 self.param_env,
                 Locations::All(DUMMY_SP),
+                DUMMY_SP,
                 ConstraintCategory::Internal,
                 &mut self.borrowck_context.constraints,
             )
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index ece801716b2..6dcdd46816e 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1141,6 +1141,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             Some(self.implicit_region_bound),
             self.param_env,
             locations,
+            locations.span(self.body),
             category,
             &mut self.borrowck_context.constraints,
         )
@@ -2401,6 +2402,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 sup: ref_region.to_region_vid(),
                                 sub: borrow_region.to_region_vid(),
                                 locations: location.to_locations(),
+                                span: location.to_locations().span(body),
                                 category,
                                 variance_info: ty::VarianceDiagInfo::default(),
                             });
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index f0106630797..f98d2c3128c 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -116,6 +116,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
                 sup,
                 sub,
                 locations: self.locations,
+                span: self.locations.span(self.type_checker.body),
                 category: self.category,
                 variance_info: info,
             },
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 8e02ca84317..bddbe2b9b0d 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1,7 +1,7 @@
 //! Compiler intrinsics.
 //!
-//! The corresponding definitions are in `compiler/rustc_codegen_llvm/src/intrinsic.rs`.
-//! The corresponding const implementations are in `compiler/rustc_mir/src/interpret/intrinsics.rs`
+//! The corresponding definitions are in <https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/intrinsic.rs>.
+//! The corresponding const implementations are in <https://github.com/rust-lang/rust/blob/master/compiler/rustc_const_eval/src/interpret/intrinsics.rs>.
 //!
 //! # Const intrinsics
 //!
@@ -10,8 +10,8 @@
 //!
 //! In order to make an intrinsic usable at compile-time, one needs to copy the implementation
 //! from <https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs> to
-//! `compiler/rustc_mir/src/interpret/intrinsics.rs` and add a
-//! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic.
+//! <https://github.com/rust-lang/rust/blob/master/compiler/rustc_const_eval/src/interpret/intrinsics.rs> and add a
+//! `#[rustc_const_unstable(feature = "const_such_and_such", issue = "01234")]` to the intrinsic declaration.
 //!
 //! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute,
 //! the intrinsic's attribute must be `rustc_const_stable`, too. Such a change should not be done
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 1be733ba106..92bea9346d8 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -427,7 +427,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
     crate::fs::read_to_string("sys:exe").map(PathBuf::from)
 }
 
-#[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
+#[cfg(target_os = "l4re")]
 pub fn current_exe() -> io::Result<PathBuf> {
     use crate::io::ErrorKind;
     Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!"))
@@ -451,6 +451,26 @@ pub fn current_exe() -> io::Result<PathBuf> {
     super::unsupported::unsupported()
 }
 
+#[cfg(target_os = "fuchsia")]
+pub fn current_exe() -> io::Result<PathBuf> {
+    use crate::io::ErrorKind;
+
+    #[cfg(test)]
+    use realstd::env;
+
+    #[cfg(not(test))]
+    use crate::env;
+
+    let exe_path = env::args().next().ok_or(io::const_io_error!(
+        ErrorKind::Uncategorized,
+        "an executable path was not found because no arguments were provided through argv"
+    ))?;
+    let path = PathBuf::from(exe_path);
+
+    // Prepend the current working directory to the path if it's not absolute.
+    if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
+}
+
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
 }
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index d688f798956..965e66880f4 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -388,11 +388,13 @@ impl<'a> ShouldRun<'a> {
             paths
                 .iter()
                 .map(|p| {
-                    assert!(
-                        self.builder.src.join(p).exists(),
-                        "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}",
-                        p
-                    );
+                    // FIXME(#96188): make sure this is actually a path.
+                    // This currently breaks for paths within submodules.
+                    //assert!(
+                    //    self.builder.src.join(p).exists(),
+                    //    "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}",
+                    //    p
+                    //);
                     TaskPath { path: p.into(), kind: Some(self.kind) }
                 })
                 .collect(),
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index 629f90728d2..0fe0fdadbd2 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -8,10 +8,34 @@ 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 QueryElement;
+
+/**
+ * @typedef {{
+ *      pos: number,
+ *      totalElems: number,
+ *      typeFilter: (null|string),
+ *      userQuery: string,
+ * }}
+ */
+var ParserState;
+
+/**
+ * @typedef {{
+ *     original: string,
+ *     userQuery: string,
+ *     typeFilter: number,
+ *     elems: Array<QueryElement>,
+ *     args: Array<QueryElement>,
+ *     returned: Array<QueryElement>,
+ *     foundElems: number,
  * }}
  */
 var ParsedQuery;
@@ -30,3 +54,30 @@ var ParsedQuery;
  * }}
  */
 var Row;
+
+/**
+ * @typedef {{
+ *    in_args: Array<Object>,
+ *    returned: Array<Object>,
+ *    others: Array<Object>,
+ *    query: ParsedQuery,
+ * }}
+ */
+var 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,
+ * }}
+ */
+var Results;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index ab52304491a..0d4e0a0b328 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -61,15 +61,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
@@ -133,11 +124,436 @@ 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 (var 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;
+        var start = parserState.pos;
+        var 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");
+        }
+        var pathSegments = name.split("::");
+        if (pathSegments.length > 1) {
+            for (var i = 0, len = pathSegments.length; i < len; ++i) {
+                var pathSegment = pathSegments[i];
+
+                if (pathSegment.length === 0) {
+                    if (i === 0) {
+                        throw new Error("Paths cannot start with `::`");
+                    } else if (i + 1 === len) {
+                        throw new Error("Paths cannot end with `::`");
+                    }
+                    throw new Error("Unexpected `::::`");
+                }
+            }
+        }
+        // In case we only have something like `<p>`, there is no name.
+        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) {
+        var end = parserState.pos;
+        while (parserState.pos < parserState.length) {
+            var c = parserState.userQuery[parserState.pos];
+            if (!isIdentCharacter(c)) {
+                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;
+                } 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) {
+        var generics = [];
+
+        var start = parserState.pos;
+        var 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) {
+        var foundStopChar = true;
+
+        while (parserState.pos < parserState.length) {
+            var 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)) {
+                var 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}\``);
+            }
+            var 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) {
+        var query = parserState.userQuery;
+
+        for (var 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) {
+        var c, before;
+        var 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) {
@@ -167,33 +583,139 @@ 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();
+        var parserState = {
+            length: userQuery.length,
+            pos: 0,
+            // Total number of elements (includes generics).
+            totalElems: 0,
+            genericsElems: 0,
+            typeFilter: null,
+            userQuery: userQuery.toLowerCase(),
+        };
+        var query = newParsedQuery(userQuery);
+
+        try {
+            parseInput(query, parserState);
+            if (parserState.typeFilter !== null) {
+                var 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;
+    }
 
-        removeEmptyStringsFromArray(split);
+    /**
+     * 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,
+        };
+    }
+
+    /**
+     * 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) {
+        var results_others = {}, results_in_args = {}, results_returned = {};
 
         function transformResults(results) {
             var duplicates = {};
@@ -227,6 +749,7 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         function sortResults(results, isType) {
+            var userQuery = parsedQuery.userQuery;
             var ar = [];
             for (var entry in results) {
                 if (hasOwnPropertyRustdoc(results, entry)) {
@@ -246,8 +769,8 @@ window.initSearch = function(rawSearchIndex) {
                 var 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,6 +832,12 @@ window.initSearch = function(rawSearchIndex) {
                 return 0;
             });
 
+            var nameSplit = null;
+            if (parsedQuery.elems.length === 1) {
+                var hasPath = typeof parsedQuery.elems[0].path === "undefined";
+                nameSplit = hasPath ? null : parsedQuery.elems[0].path;
+            }
+
             for (var i = 0, len = results.length; i < len; ++i) {
                 result = results[i];
 
@@ -320,215 +849,222 @@ window.initSearch = function(rawSearchIndex) {
                     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;
+            var elem_name;
+            if (elem.generics.length > 0 && row[GENERICS_DATA].length >= elem.generics.length) {
+                var elems = Object.create(null);
+                for (var x = 0, length = row[GENERICS_DATA].length; x < length; ++x) {
+                    elem_name = row[GENERICS_DATA][x][NAME];
+                    if (elem_name === "") {
+                        // Pure generic, needs to check into it.
+                        if (checkGenerics(
+                                row[GENERICS_DATA][x], 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 (x = 0, length = elem.generics.length; x < length; ++x) {
+                    var generic = elem.generics[x];
+                    var 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 {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) {
+            var lev = MAX_LEV_DISTANCE + 1;
+            for (var x = 0, length = row[GENERICS_DATA].length; x < length && lev !== 0; ++x) {
+                lev = Math.min(
+                    checkType(row[GENERICS_DATA][x], elem, true),
+                    lev
+                );
+            }
+            return lev;
+        }
+
+        /**
+          * 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.
           * @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!
+            var 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) {
+                        var 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!
+                    var 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;
+        function findArg(row, elem, typeFilter) {
+            var lev = 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;
+            if (row && row.type && row.type[INPUTS_DATA] && row.type[INPUTS_DATA].length > 0) {
+                var length = row.type[INPUTS_DATA].length;
                 for (var i = 0; i < length; i++) {
-                    var tmp = obj.type[INPUTS_DATA][i];
+                    var tmp = row.type[INPUTS_DATA][i];
                     if (!typePassesFilter(typeFilter, tmp[1])) {
                         continue;
                     }
-                    tmp = checkType(tmp, val, literalSearch);
-                    if (tmp === 0) {
+                    lev = Math.min(lev, checkType(tmp, 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) {
+            var 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) {
+                var ret = row.type[OUTPUT_DATA];
                 if (typeof ret[0] === "string") {
                     ret = [ret];
                 }
@@ -537,16 +1073,13 @@ window.initSearch = function(rawSearchIndex) {
                     if (!typePassesFilter(typeFilter, tmp[1])) {
                         continue;
                     }
-                    tmp = checkType(tmp, val, literalSearch);
-                    if (tmp === 0) {
+                    lev = Math.min(lev, checkType(tmp, 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) {
@@ -621,13 +1154,14 @@ window.initSearch = function(rawSearchIndex) {
         }
 
         function handleAliases(ret, query, filterCrates) {
+            var 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 = [];
             if (filterCrates !== null) {
-                if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
-                    var query_aliases = ALIASES[filterCrates][query.search];
+                if (ALIASES[filterCrates] && ALIASES[filterCrates][lowerQuery]) {
+                    var query_aliases = ALIASES[filterCrates][lowerQuery];
                     var len = query_aliases.length;
                     for (var i = 0; i < len; ++i) {
                         aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
@@ -635,9 +1169,9 @@ window.initSearch = function(rawSearchIndex) {
                 }
             } else {
                 Object.keys(ALIASES).forEach(function(crate) {
-                    if (ALIASES[crate][query.search]) {
+                    if (ALIASES[crate][lowerQuery]) {
                         var pushTo = crate === window.currentCrate ? crateAliases : aliases;
-                        var query_aliases = ALIASES[crate][query.search];
+                        var query_aliases = ALIASES[crate][lowerQuery];
                         var len = query_aliases.length;
                         for (var i = 0; i < len; ++i) {
                             pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
@@ -658,7 +1192,7 @@ window.initSearch = function(rawSearchIndex) {
             aliases.sort(sortFunc);
 
             var pushFunc = function(alias) {
-                alias.alias = query.raw;
+                alias.alias = query;
                 var res = buildHrefAndPath(alias);
                 alias.displayPath = pathSplitter(res[0]);
                 alias.fullPath = alias.displayPath + alias.name;
@@ -674,208 +1208,237 @@ 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) {
+                    var 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;
+            }
+            var lev, lev_add = 0, index = -1;
+            var fullId = row.id;
+
+            var in_args = findArg(row, elem, parsedQuery.typeFilter);
+            var 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;
+            }
+            var searchWord = searchWords[pos];
+
+            if (parsedQuery.literalSearch) {
+                if (searchWord === elem.name) {
+                    addIntoResults(results_others, fullId, pos, -1, 0);
                 }
-                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;
+            }
+
+            // 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);
                 }
-                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;
+                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;
                 }
-                var type = searchIndex[i].type;
-                ty = searchIndex[i];
-                if (!type) {
-                    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);
                 }
-                fullId = ty.id;
+            }
+            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;
+                }
+            }
+            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;
+            var totalLev = 0;
+            var nbLev = 0;
+            var lev;
+
+            // If the result is too "bad", we return false and it ends this search.
+            function checkArgs(elems, callback) {
+                for (var i = 0, len = elems.length; i < len; ++i) {
+                    var elem = elems[i];
+                    // There is more than one parameter to the query so all checks should be "exact"
+                    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;
+            }
+            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() {
+            var 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) {
+                var 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();
+        }
+
+        var 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,9 +1455,13 @@ 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) {
+        if (!keys || !keys.length) {
+            return true;
+        }
         for (var i = 0, len = keys.length; i < len; ++i) {
             // each check is for validation so we negate the conditions and invalidate
             if (!(
@@ -913,30 +1480,6 @@ window.initSearch = function(rawSearchIndex) {
         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;
         searchState.focusedByTab[searchState.currentTab] = document.activeElement;
@@ -1088,11 +1631,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,6 +1658,11 @@ 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();
         if (go_to_first || (results.others.length === 1
@@ -1132,13 +1680,15 @@ 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);
+        var ret_others = addTab(results.others, results.query, true);
+        var ret_in_args = addTab(results.in_args, results.query, false);
+        var 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
@@ -1164,11 +1714,19 @@ window.initSearch = function(rawSearchIndex) {
             }
             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">` +
+
+        var typeFilter = "";
+        if (results.query.typeFilter !== NO_TYPE_FILTER) {
+            typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")";
+        }
+
+        var output = `<div id="search-settings">` +
+            `<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` +
+            `${typeFilter}</h1> in ${crates} </div>`;
+        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]) +
@@ -1196,28 +1754,6 @@ window.initSearch = function(rawSearchIndex) {
         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.
@@ -1226,17 +1762,14 @@ window.initSearch = function(rawSearchIndex) {
      */
     function search(e, forced) {
         var params = searchState.getQueryStringParams();
-        var query = getQuery(searchState.input.value.trim());
+        var 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;
@@ -1251,13 +1784,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);
-
+            var newURL = buildUrl(query.original, filterCrates);
             if (!history.state && !params.search) {
                 history.pushState(null, "", newURL);
             } else {
@@ -1265,8 +1797,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) {
diff --git a/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir
index 9f235248ca5..8423128123a 100644
--- a/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir
+++ b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir
@@ -25,14 +25,14 @@
 | '_#2r live at {bb0[0..=1]}
 | '_#3r live at {bb0[0..=1]}
 | '_#4r live at {bb0[0..=1]}
-| '_#1r: '_#6r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27)
-| '_#1r: '_#8r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55)
-| '_#2r: '_#7r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43)
-| '_#3r: '_#9r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67)
-| '_#6r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27)
-| '_#7r: '_#2r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43)
-| '_#8r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55)
-| '_#9r: '_#3r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67)
+| '_#1r: '_#6r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) ($DIR/named-lifetimes-basic.rs:12:26: 12:27 (#0)
+| '_#1r: '_#8r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) ($DIR/named-lifetimes-basic.rs:12:54: 12:55 (#0)
+| '_#2r: '_#7r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) ($DIR/named-lifetimes-basic.rs:12:42: 12:43 (#0)
+| '_#3r: '_#9r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) ($DIR/named-lifetimes-basic.rs:12:66: 12:67 (#0)
+| '_#6r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) ($DIR/named-lifetimes-basic.rs:12:26: 12:27 (#0)
+| '_#7r: '_#2r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) ($DIR/named-lifetimes-basic.rs:12:42: 12:43 (#0)
+| '_#8r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) ($DIR/named-lifetimes-basic.rs:12:54: 12:55 (#0)
+| '_#9r: '_#3r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) ($DIR/named-lifetimes-basic.rs:12:66: 12:67 (#0)
 |
 fn use_x(_1: &'_#6r mut i32, _2: &'_#7r u32, _3: &'_#8r u32, _4: &'_#9r u32) -> bool {
     debug w => _1;                       // in scope 0 at $DIR/named-lifetimes-basic.rs:12:26: 12:27
diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
index ed94a1ecf00..f79e2705ad2 100644
--- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
+++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
@@ -18,8 +18,8 @@
 | '_#3r live at {bb1[0]}
 | '_#4r live at {bb1[1..=3]}
 | '_#5r live at {bb1[4..=7], bb2[0..=2]}
-| '_#3r: '_#4r due to Assignment at Single(bb1[0])
-| '_#4r: '_#5r due to Assignment at Single(bb1[3])
+| '_#3r: '_#4r due to Assignment at Single(bb1[0]) ($DIR/region-subtyping-basic.rs:18:13: 18:18 (#0)
+| '_#4r: '_#5r due to Assignment at Single(bb1[3]) ($DIR/region-subtyping-basic.rs:19:13: 19:14 (#0)
 |
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11
diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
index 95997b20701..162cacef8a5 100644
--- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
+++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
@@ -18,8 +18,8 @@
 | '_#3r live at {bb1[0]}
 | '_#4r live at {bb1[1..=3]}
 | '_#5r live at {bb1[4..=7], bb2[0..=2]}
-| '_#3r: '_#4r due to Assignment at Single(bb1[0])
-| '_#4r: '_#5r due to Assignment at Single(bb1[3])
+| '_#3r: '_#4r due to Assignment at Single(bb1[0]) ($DIR/region-subtyping-basic.rs:18:13: 18:18 (#0)
+| '_#4r: '_#5r due to Assignment at Single(bb1[3]) ($DIR/region-subtyping-basic.rs:19:13: 19:14 (#0)
 |
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11
diff --git a/src/test/mir-opt/storage_ranges.main.nll.0.mir b/src/test/mir-opt/storage_ranges.main.nll.0.mir
index e02580135af..b383c5ec9dc 100644
--- a/src/test/mir-opt/storage_ranges.main.nll.0.mir
+++ b/src/test/mir-opt/storage_ranges.main.nll.0.mir
@@ -16,7 +16,7 @@
 | '_#1r live at {bb0[0..=22]}
 | '_#3r live at {bb0[10]}
 | '_#4r live at {bb0[11]}
-| '_#3r: '_#4r due to Assignment at Single(bb0[10])
+| '_#3r: '_#4r due to Assignment at Single(bb0[10]) ($DIR/storage_ranges.rs:6:17: 6:25 (#0)
 |
 fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/storage_ranges.rs:3:11: 3:11
diff --git a/src/test/rustdoc-js-std/filter-crate.js b/src/test/rustdoc-js-std/filter-crate.js
index 2e0330c4497..b47a1fefa41 100644
--- a/src/test/rustdoc-js-std/filter-crate.js
+++ b/src/test/rustdoc-js-std/filter-crate.js
@@ -1,6 +1,6 @@
 // exact-check
 
-const QUERY = 'hashmap';
+const QUERY = '"hashmap"';
 const FILTER_CRATE = 'core';
 
 const EXPECTED = {
diff --git a/src/test/rustdoc-js-std/parser-errors.js b/src/test/rustdoc-js-std/parser-errors.js
new file mode 100644
index 00000000000..779ab867c12
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-errors.js
@@ -0,0 +1,365 @@
+const QUERY = [
+    '<P>',
+    '-> <P>',
+    'a<"P">',
+    '"P" "P"',
+    'P "P"',
+    '"p" p',
+    '"const": p',
+    "a<:a>",
+    "a<::a>",
+    "((a))",
+    "(p -> p",
+    "::a::b",
+    "a::::b",
+    "a::b::",
+    ":a",
+    "a b:",
+    "a (b:",
+    "_:",
+    "a-bb",
+    "a>bb",
+    "ab'",
+    "a->",
+    '"p" <a>',
+    '"p" a<a>',
+    "a,<",
+    "aaaaa<>b",
+    "fn:aaaaa<>b",
+    "->a<>b",
+    "a<->",
+    "a:: a",
+    "a ::a",
+    "a<a>:",
+    "a<>:",
+    "a,:",
+    "  a<>  :",
+    "mod : :",
+];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 0,
+        original: "<P>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "<p>",
+        error: "Found generics without a path",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "-> <P>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "-> <p>",
+        error: "Found generics without a path",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<\"P\">",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<\"p\">",
+        error: "`\"` cannot be used in generics",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "\"P\" \"P\"",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "\"p\" \"p\"",
+        error: "Cannot have more than one literal search element",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "P \"P\"",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "p \"p\"",
+        error: "Cannot use literal search when there is more than one element",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "\"p\" p",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "\"p\" p",
+        error: "You cannot have more than one element if you use quotes",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "\"const\": p",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "\"const\": p",
+        error: "You cannot use quotes on type filter",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<:a>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<:a>",
+        error: "Unexpected `:` after `<`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<::a>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<::a>",
+        error: "Unexpected `::`: paths cannot start with `::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "((a))",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "((a))",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "(p -> p",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "(p -> p",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "::a::b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "::a::b",
+        error: "Paths cannot start with `::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a::::b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a::::b",
+        error: "Unexpected `::::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a::b::",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a::b::",
+        error: "Paths cannot end with `::`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: ":a",
+        returned: [],
+        typeFilter: -1,
+        userQuery: ":a",
+        error: "Expected type filter before `:`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a b:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a b:",
+        error: "Unexpected `:`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a (b:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a (b:",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "_:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "_:",
+        error: "Unknown type filter `_`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a-bb",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a-bb",
+        error: "Unexpected `-` (did you mean `->`?)",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a>bb",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a>bb",
+        error: "Unexpected `>` (did you mean `->`?)",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "ab'",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "ab'",
+        error: "Unexpected `'`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a->",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a->",
+        error: "Expected at least one item after `->`",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"p" <a>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p" <a>',
+        error: "Found generics without a path",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"p" a<a>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p" a<a>',
+        error: "You cannot have more than one element if you use quotes",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a,<',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a,<',
+        error: 'Found generics without a path',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'aaaaa<>b',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'aaaaa<>b',
+        error: 'Expected `,`, ` `, `:` or `->`, found `b`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'fn:aaaaa<>b',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'fn:aaaaa<>b',
+        error: 'Expected `,`, ` ` or `->`, found `b`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '->a<>b',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '->a<>b',
+        error: 'Expected `,` or ` `, found `b`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a<->',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a<->',
+        error: 'Unexpected `-` after `<`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a:: a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a:: a',
+        error: 'Paths cannot end with `::`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'a ::a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a ::a',
+        error: 'Paths cannot start with `::`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<a>:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<a>:",
+        error: 'Unexpected `:`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<>:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<>:",
+        error: 'Unexpected `<` in type filter',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a,:",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a,:",
+        error: 'Unexpected `,` in type filter',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<>  :",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<>  :",
+        error: 'Unexpected `<` in type filter',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "mod : :",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "mod : :",
+        error: 'Unexpected `:`',
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-filter.js b/src/test/rustdoc-js-std/parser-filter.js
new file mode 100644
index 00000000000..e5a87a415ac
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-filter.js
@@ -0,0 +1,43 @@
+const QUERY = ['fn:foo', 'enum : foo', 'macro<f>:foo'];
+
+const PARSED = [
+    {
+        elems: [{
+            name: "foo",
+            fullPath: ["foo"],
+            pathWithoutLast: [],
+            pathLast: "foo",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "fn:foo",
+        returned: [],
+        typeFilter: 5,
+        userQuery: "fn:foo",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "foo",
+            fullPath: ["foo"],
+            pathWithoutLast: [],
+            pathLast: "foo",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "enum : foo",
+        returned: [],
+        typeFilter: 4,
+        userQuery: "enum : foo",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "macro<f>:foo",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "macro<f>:foo",
+        error: "Unexpected `:`",
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-generics.js b/src/test/rustdoc-js-std/parser-generics.js
new file mode 100644
index 00000000000..0cf7f5019aa
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-generics.js
@@ -0,0 +1,62 @@
+const QUERY = ['A<B<C<D>,  E>', 'p<> u8', '"p"<a>'];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'A<B<C<D>,  E>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a<b<c<d>,  e>',
+        error: 'Unexpected `<` after `<`',
+    },
+    {
+        elems: [
+            {
+                name: "p",
+                fullPath: ["p"],
+                pathWithoutLast: [],
+                pathLast: "p",
+                generics: [],
+            },
+            {
+                name: "u8",
+                fullPath: ["u8"],
+                pathWithoutLast: [],
+                pathLast: "u8",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "p<> u8",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "p<> u8",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "p",
+                fullPath: ["p"],
+                pathWithoutLast: [],
+                pathLast: "p",
+                generics: [
+                    {
+                        name: "a",
+                        fullPath: ["a"],
+                        pathWithoutLast: [],
+                        pathLast: "a",
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: '"p"<a>',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p"<a>',
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-literal.js b/src/test/rustdoc-js-std/parser-literal.js
new file mode 100644
index 00000000000..87b3baff1e2
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-literal.js
@@ -0,0 +1,27 @@
+const QUERY = ['R<P>'];
+
+const PARSED = [
+    {
+        elems: [{
+            name: "r",
+            fullPath: ["r"],
+            pathWithoutLast: [],
+            pathLast: "r",
+            generics: [
+                {
+                    name: "p",
+                    fullPath: ["p"],
+                    pathWithoutLast: [],
+                    pathLast: "p",
+                    generics: [],
+                },
+            ],
+        }],
+        foundElems: 1,
+        original: "R<P>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "r<p>",
+        error: null,
+    }
+];
diff --git a/src/test/rustdoc-js-std/parser-paths.js b/src/test/rustdoc-js-std/parser-paths.js
new file mode 100644
index 00000000000..9f823f9336a
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-paths.js
@@ -0,0 +1,90 @@
+const QUERY = ['A::B', 'A::B,C',  'A::B<f>,C', 'mod::a'];
+
+const PARSED = [
+    {
+        elems: [{
+            name: "a::b",
+            fullPath: ["a", "b"],
+            pathWithoutLast: ["a"],
+            pathLast: "b",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "A::B",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a::b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "a::b",
+                fullPath: ["a", "b"],
+                pathWithoutLast: ["a"],
+                pathLast: "b",
+                generics: [],
+            },
+            {
+                name: "c",
+                fullPath: ["c"],
+                pathWithoutLast: [],
+                pathLast: "c",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: 'A::B,C',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a::b,c',
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "a::b",
+                fullPath: ["a", "b"],
+                pathWithoutLast: ["a"],
+                pathLast: "b",
+                generics: [
+                    {
+                        name: "f",
+                        fullPath: ["f"],
+                        pathWithoutLast: [],
+                        pathLast: "f",
+                        generics: [],
+                    },
+                ],
+            },
+            {
+                name: "c",
+                fullPath: ["c"],
+                pathWithoutLast: [],
+                pathLast: "c",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: 'A::B<f>,C',
+        returned: [],
+        typeFilter: -1,
+        userQuery: 'a::b<f>,c',
+        error: null,
+    },
+    {
+        elems: [{
+            name: "mod::a",
+            fullPath: ["mod", "a"],
+            pathWithoutLast: ["mod"],
+            pathLast: "a",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: "mod::a",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "mod::a",
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-quote.js b/src/test/rustdoc-js-std/parser-quote.js
new file mode 100644
index 00000000000..1e16c90de5e
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-quote.js
@@ -0,0 +1,87 @@
+const QUERY = [
+    '-> "p"',
+    '"p",',
+    '"p" -> a',
+    '"a" -> "p"',
+    '->"-"',
+    '"a',
+    '""',
+];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 1,
+        original: '-> "p"',
+        returned: [{
+            name: "p",
+            fullPath: ["p"],
+            pathWithoutLast: [],
+            pathLast: "p",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: '-> "p"',
+        error: null,
+    },
+    {
+        elems: [{
+            name: "p",
+            fullPath: ["p"],
+            pathWithoutLast: [],
+            pathLast: "p",
+            generics: [],
+        }],
+        foundElems: 1,
+        original: '"p",',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p",',
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"p" -> a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"p" -> a',
+        error: "You cannot have more than one element if you use quotes",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"a" -> "p"',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"a" -> "p"',
+        error: "Cannot have more than one literal search element",
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '->"-"',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '->"-"',
+        error: 'Unexpected `-` in a string element',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '"a',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '"a',
+        error: 'Unclosed `"`',
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: '""',
+        returned: [],
+        typeFilter: -1,
+        userQuery: '""',
+        error: 'Cannot have empty string element',
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-returned.js b/src/test/rustdoc-js-std/parser-returned.js
new file mode 100644
index 00000000000..b45466aa940
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-returned.js
@@ -0,0 +1,78 @@
+const QUERY = ['-> F<P>', '-> P', '->,a', 'aaaaa->a'];
+
+const PARSED = [
+    {
+        elems: [],
+        foundElems: 1,
+        original: "-> F<P>",
+        returned: [{
+            name: "f",
+            fullPath: ["f"],
+            pathWithoutLast: [],
+            pathLast: "f",
+            generics: [
+                {
+                    name: "p",
+                    fullPath: ["p"],
+                    pathWithoutLast: [],
+                    pathLast: "p",
+                    generics: [],
+                },
+            ],
+        }],
+        typeFilter: -1,
+        userQuery: "-> f<p>",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 1,
+        original: "-> P",
+        returned: [{
+            name: "p",
+            fullPath: ["p"],
+            pathWithoutLast: [],
+            pathLast: "p",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: "-> p",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 1,
+        original: "->,a",
+        returned: [{
+            name: "a",
+            fullPath: ["a"],
+            pathWithoutLast: [],
+            pathLast: "a",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: "->,a",
+        error: null,
+    },
+    {
+        elems: [{
+            name: "aaaaa",
+            fullPath: ["aaaaa"],
+            pathWithoutLast: [],
+            pathLast: "aaaaa",
+            generics: [],
+        }],
+        foundElems: 2,
+        original: "aaaaa->a",
+        returned: [{
+            name: "a",
+            fullPath: ["a"],
+            pathWithoutLast: [],
+            pathLast: "a",
+            generics: [],
+        }],
+        typeFilter: -1,
+        userQuery: "aaaaa->a",
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-separators.js b/src/test/rustdoc-js-std/parser-separators.js
new file mode 100644
index 00000000000..5b7abdfa8d6
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-separators.js
@@ -0,0 +1,206 @@
+// ignore-tidy-tab
+
+const QUERY = [
+    'aaaaaa	b',
+    'a b',
+    'a,b',
+    'a\tb',
+    'a<b c>',
+    'a<b,c>',
+    'a<b\tc>',
+];
+
+const PARSED = [
+    {
+        elems: [
+            {
+                name: 'aaaaaa',
+                fullPath: ['aaaaaa'],
+                pathWithoutLast: [],
+                pathLast: 'aaaaaa',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "aaaaaa	b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "aaaaaa	b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a,b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a,b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [],
+            },
+            {
+                name: 'b',
+                fullPath: ['b'],
+                pathWithoutLast: [],
+                pathLast: 'b',
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a\tb",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a\tb",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [
+                    {
+                        name: 'b',
+                        fullPath: ['b'],
+                        pathWithoutLast: [],
+                        pathLast: 'b',
+                        generics: [],
+                    },
+                    {
+                        name: 'c',
+                        fullPath: ['c'],
+                        pathWithoutLast: [],
+                        pathLast: 'c',
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: "a<b c>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<b c>",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [
+                    {
+                        name: 'b',
+                        fullPath: ['b'],
+                        pathWithoutLast: [],
+                        pathLast: 'b',
+                        generics: [],
+                    },
+                    {
+                        name: 'c',
+                        fullPath: ['c'],
+                        pathWithoutLast: [],
+                        pathLast: 'c',
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: "a<b,c>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<b,c>",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: 'a',
+                fullPath: ['a'],
+                pathWithoutLast: [],
+                pathLast: 'a',
+                generics: [
+                    {
+                        name: 'b',
+                        fullPath: ['b'],
+                        pathWithoutLast: [],
+                        pathLast: 'b',
+                        generics: [],
+                    },
+                    {
+                        name: 'c',
+                        fullPath: ['c'],
+                        pathWithoutLast: [],
+                        pathLast: 'c',
+                        generics: [],
+                    },
+                ],
+            },
+        ],
+        foundElems: 1,
+        original: "a<b\tc>",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<b\tc>",
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/parser-weird-queries.js b/src/test/rustdoc-js-std/parser-weird-queries.js
new file mode 100644
index 00000000000..a3d85aeca5e
--- /dev/null
+++ b/src/test/rustdoc-js-std/parser-weird-queries.js
@@ -0,0 +1,123 @@
+// This test is mostly to check that the parser still kinda outputs something
+// (and doesn't enter an infinite loop!) even though the query is completely
+// invalid.
+const QUERY = [
+    'a b',
+    'a   b',
+    'a,b(c)',
+    'aaa,a',
+    ',,,,',
+    'mod    :',
+    'mod\t:',
+];
+
+const PARSED = [
+    {
+        elems: [
+            {
+                name: "a",
+                fullPath: ["a"],
+                pathWithoutLast: [],
+                pathLast: "a",
+                generics: [],
+            },
+            {
+                name: "b",
+                fullPath: ["b"],
+                pathWithoutLast: [],
+                pathLast: "b",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a b",
+        error: null,
+    },
+    {
+        elems: [
+            {
+                name: "a",
+                fullPath: ["a"],
+                pathWithoutLast: [],
+                pathLast: "a",
+                generics: [],
+            },
+            {
+                name: "b",
+                fullPath: ["b"],
+                pathWithoutLast: [],
+                pathLast: "b",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "a   b",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a   b",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a,b(c)",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a,b(c)",
+        error: "Unexpected `(`",
+    },
+    {
+        elems: [
+            {
+                name: "aaa",
+                fullPath: ["aaa"],
+                pathWithoutLast: [],
+                pathLast: "aaa",
+                generics: [],
+            },
+            {
+                name: "a",
+                fullPath: ["a"],
+                pathWithoutLast: [],
+                pathLast: "a",
+                generics: [],
+            },
+        ],
+        foundElems: 2,
+        original: "aaa,a",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "aaa,a",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: ",,,,",
+        returned: [],
+        typeFilter: -1,
+        userQuery: ",,,,",
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'mod    :',
+        returned: [],
+        typeFilter: 0,
+        userQuery: 'mod    :',
+        error: null,
+    },
+    {
+        elems: [],
+        foundElems: 0,
+        original: 'mod\t:',
+        returned: [],
+        typeFilter: 0,
+        userQuery: 'mod\t:',
+        error: null,
+    },
+];
diff --git a/src/test/rustdoc-js-std/quoted.js b/src/test/rustdoc-js-std/quoted.js
index 924129f86c8..aec8484a41f 100644
--- a/src/test/rustdoc-js-std/quoted.js
+++ b/src/test/rustdoc-js-std/quoted.js
@@ -1,4 +1,7 @@
+// ignore-order
+
 const QUERY = '"error"';
+const FILTER_CRATE = 'std';
 
 const EXPECTED = {
     'others': [
@@ -6,7 +9,12 @@ const EXPECTED = {
         { 'path': 'std::fmt', 'name': 'Error' },
         { 'path': 'std::io', 'name': 'Error' },
     ],
-    'in_args': [],
+    'in_args': [
+        { 'path': 'std::fmt::Error', 'name': 'eq' },
+        { 'path': 'std::fmt::Error', 'name': 'cmp' },
+        { 'path': 'std::fmt::Error', 'name': 'partial_cmp' },
+
+    ],
     'returned': [
         { 'path': 'std::fmt::LowerExp', 'name': 'fmt' },
     ],
diff --git a/src/test/rustdoc-js-std/struct-vec.js b/src/test/rustdoc-js-std/struct-vec.js
index 2c808143bae..29609904b19 100644
--- a/src/test/rustdoc-js-std/struct-vec.js
+++ b/src/test/rustdoc-js-std/struct-vec.js
@@ -1,8 +1,8 @@
-const QUERY = 'struct:Vec';
+const QUERY = 'struct:VecD';
 
 const EXPECTED = {
     'others': [
-        { 'path': 'std::vec', 'name': 'Vec' },
         { 'path': 'std::collections', 'name': 'VecDeque' },
+        { 'path': 'std::vec', 'name': 'Vec' },
     ],
 };
diff --git a/src/test/rustdoc-js-std/typed-query.js b/src/test/rustdoc-js-std/typed-query.js
index 3915ee7dc5d..25efbad2695 100644
--- a/src/test/rustdoc-js-std/typed-query.js
+++ b/src/test/rustdoc-js-std/typed-query.js
@@ -1,6 +1,7 @@
 // exact-check
 
 const QUERY = 'macro:print';
+const FILTER_CRATE = 'std';
 
 const EXPECTED = {
     'others': [
@@ -9,6 +10,8 @@ const EXPECTED = {
         { 'path': 'std', 'name': 'println' },
         { 'path': 'std', 'name': 'eprintln' },
         { 'path': 'std::pin', 'name': 'pin' },
-        { 'path': 'core::pin', 'name': 'pin' },
+        { 'path': 'std::future', 'name': 'join' },
+        { 'path': 'std', 'name': 'line' },
+        { 'path': 'std', 'name': 'write' },
     ],
 };
diff --git a/src/test/rustdoc-js-std/vec-new.js b/src/test/rustdoc-js-std/vec-new.js
index e1a3256876b..cd0e8e7b4a9 100644
--- a/src/test/rustdoc-js-std/vec-new.js
+++ b/src/test/rustdoc-js-std/vec-new.js
@@ -4,6 +4,6 @@ const EXPECTED = {
     'others': [
         { 'path': 'std::vec::Vec', 'name': 'new' },
         { 'path': 'std::vec::Vec', 'name': 'ne' },
-        { 'path': 'std::rc::Rc', 'name': 'ne' },
+        { 'path': 'alloc::vec::Vec', 'name': 'ne' },
     ],
 };
diff --git a/src/test/rustdoc-js/doc-alias-filter.js b/src/test/rustdoc-js/doc-alias-filter.js
index 4b1e2e29704..e06047ba760 100644
--- a/src/test/rustdoc-js/doc-alias-filter.js
+++ b/src/test/rustdoc-js/doc-alias-filter.js
@@ -1,6 +1,6 @@
 // exact-check
 
-const QUERY = 'true';
+const QUERY = '"true"';
 
 const FILTER_CRATE = 'doc_alias_filter';
 
diff --git a/src/test/rustdoc-js/doc-alias.js b/src/test/rustdoc-js/doc-alias.js
index ff188d51458..7bb0cbe388f 100644
--- a/src/test/rustdoc-js/doc-alias.js
+++ b/src/test/rustdoc-js/doc-alias.js
@@ -27,6 +27,7 @@ const QUERY = [
 
 const EXPECTED = [
     {
+        // StructItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -38,6 +39,7 @@ const EXPECTED = [
         ],
     },
     {
+        // StructFieldItem
         'others': [
             {
                 'path': 'doc_alias::Struct',
@@ -49,6 +51,7 @@ const EXPECTED = [
         ],
     },
     {
+        // StructMethodItem
         'others': [
             {
                 'path': 'doc_alias::Struct',
@@ -76,6 +79,7 @@ const EXPECTED = [
         ],
     },
     {
+        // ImplTraitFunction
         'others': [
             {
                 'path': 'doc_alias::Struct',
@@ -87,6 +91,7 @@ const EXPECTED = [
         ],
     },
     {
+        // EnumItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -98,6 +103,7 @@ const EXPECTED = [
         ],
     },
     {
+        // VariantItem
         'others': [
             {
                 'path': 'doc_alias::Enum',
@@ -109,6 +115,7 @@ const EXPECTED = [
         ],
     },
     {
+        // EnumMethodItem
         'others': [
             {
                 'path': 'doc_alias::Enum',
@@ -120,6 +127,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TypedefItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -131,6 +139,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TraitItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -142,6 +151,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TraitTypeItem
         'others': [
             {
                 'path': 'doc_alias::Trait',
@@ -153,6 +163,7 @@ const EXPECTED = [
         ],
     },
     {
+        // AssociatedConstItem
         'others': [
             {
                 'path': 'doc_alias::Trait',
@@ -164,6 +175,7 @@ const EXPECTED = [
         ],
     },
     {
+        // TraitFunctionItem
         'others': [
             {
                 'path': 'doc_alias::Trait',
@@ -175,6 +187,7 @@ const EXPECTED = [
         ],
     },
     {
+        // FunctionItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -186,6 +199,7 @@ const EXPECTED = [
         ],
     },
     {
+        // ModuleItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -197,6 +211,7 @@ const EXPECTED = [
         ],
     },
     {
+        // ConstItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -212,6 +227,7 @@ const EXPECTED = [
         ],
     },
     {
+        // StaticItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -223,6 +239,7 @@ const EXPECTED = [
         ],
     },
     {
+        // UnionItem
         'others': [
             {
                 'path': 'doc_alias',
@@ -240,6 +257,7 @@ const EXPECTED = [
         ],
     },
     {
+        // UnionFieldItem
         'others': [
             {
                 'path': 'doc_alias::Union',
@@ -251,6 +269,7 @@ const EXPECTED = [
         ],
     },
     {
+        // UnionMethodItem
         'others': [
             {
                 'path': 'doc_alias::Union',
@@ -262,6 +281,7 @@ const EXPECTED = [
         ],
     },
     {
+        // MacroItem
         'others': [
             {
                 'path': 'doc_alias',
diff --git a/src/test/rustdoc-js/generics.js b/src/test/rustdoc-js/generics.js
index 63a9ad53812..5e5ba7cd9ac 100644
--- a/src/test/rustdoc-js/generics.js
+++ b/src/test/rustdoc-js/generics.js
@@ -1,16 +1,18 @@
 // exact-check
 
 const QUERY = [
-    '"R<P>"',
+    'R<P>',
     '"P"',
     'P',
-    '"ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>"',
+    'ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>',
     'TraitCat',
     'TraitDog',
+    'Result<String>',
 ];
 
 const EXPECTED = [
     {
+        // R<P>
         'returned': [
             { 'path': 'generics', 'name': 'alef' },
         ],
@@ -19,6 +21,7 @@ const EXPECTED = [
         ],
     },
     {
+        // "P"
         'others': [
             { 'path': 'generics', 'name': 'P' },
         ],
@@ -30,29 +33,41 @@ const EXPECTED = [
         ],
     },
     {
+        // P
         'returned': [
             { 'path': 'generics', 'name': 'alef' },
-            { 'path': 'generics', 'name': 'bet' },
         ],
         'in_args': [
             { 'path': 'generics', 'name': 'alpha' },
-            { 'path': 'generics', 'name': 'beta' },
         ],
     },
     {
+        // "ExtraCreditStructMulti"<ExtraCreditInnerMulti, ExtraCreditInnerMulti>
         'in_args': [
             { 'path': 'generics', 'name': 'extracreditlabhomework' },
         ],
         'returned': [],
     },
     {
+        // TraitCat
         'in_args': [
             { 'path': 'generics', 'name': 'gamma' },
         ],
     },
     {
+        // TraitDog
         'in_args': [
             { 'path': 'generics', 'name': 'gamma' },
         ],
     },
+    {
+        // Result<String>
+        'others': [],
+        'returned': [
+            { 'path': 'generics', 'name': 'super_soup' },
+        ],
+        'in_args': [
+            { 'path': 'generics', 'name': 'super_soup' },
+        ],
+    },
 ];
diff --git a/src/test/rustdoc-js/generics.rs b/src/test/rustdoc-js/generics.rs
index 5e11a6d6018..055c51c7ec5 100644
--- a/src/test/rustdoc-js/generics.rs
+++ b/src/test/rustdoc-js/generics.rs
@@ -24,3 +24,5 @@ pub trait TraitCat {}
 pub trait TraitDog {}
 
 pub fn gamma<T: TraitCat + TraitDog>(t: T) {}
+
+pub fn super_soup(s: Result<String, i32>) -> Result<String, i32> { s }
diff --git a/src/test/ui/rfc1623.base.stderr b/src/test/ui/rfc1623.base.stderr
index 6d389a1317a..364c8c8f706 100644
--- a/src/test/ui/rfc1623.base.stderr
+++ b/src/test/ui/rfc1623.base.stderr
@@ -1,5 +1,5 @@
 error: implementation of `FnOnce` is not general enough
-  --> $DIR/rfc1623.rs:36:8
+  --> $DIR/rfc1623.rs:32:8
    |
 LL |     f: &id,
    |        ^^^ implementation of `FnOnce` is not general enough
diff --git a/src/test/ui/rfc1623.nll.stderr b/src/test/ui/rfc1623.nll.stderr
index f85b6ff8ff7..2eff4708547 100644
--- a/src/test/ui/rfc1623.nll.stderr
+++ b/src/test/ui/rfc1623.nll.stderr
@@ -1,63 +1,35 @@
 error[E0308]: mismatched types
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ one type is more general than the other
+LL |     f: &id,
+   |        ^^^ one type is more general than the other
    |
    = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
               found type `Fn<(&Foo<'_>,)>`
 
 error[E0308]: mismatched types
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ one type is more general than the other
+LL |     f: &id,
+   |        ^^^ one type is more general than the other
    |
    = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
               found type `Fn<(&Foo<'_>,)>`
 
 error: implementation of `FnOnce` is not general enough
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ implementation of `FnOnce` is not general enough
+LL |     f: &id,
+   |        ^^^ implementation of `FnOnce` is not general enough
    |
    = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `FnOnce<(&'1 Foo<'b>,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&'2 Foo<'_>,)>`, for some specific lifetime `'2`
 
 error: implementation of `FnOnce` is not general enough
-  --> $DIR/rfc1623.rs:29:35
+  --> $DIR/rfc1623.rs:32:8
    |
-LL |   static SOME_STRUCT: &SomeStruct = &SomeStruct {
-   |  ___________________________________^
-LL | |
-LL | |
-LL | |
-...  |
-LL | |
-LL | | };
-   | |_^ implementation of `FnOnce` is not general enough
+LL |     f: &id,
+   |        ^^^ implementation of `FnOnce` is not general enough
    |
    = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&'a Foo<'1>,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&Foo<'2>,)>`, for some specific lifetime `'2`
diff --git a/src/test/ui/rfc1623.rs b/src/test/ui/rfc1623.rs
index 0e9d2140324..443da0aa955 100644
--- a/src/test/ui/rfc1623.rs
+++ b/src/test/ui/rfc1623.rs
@@ -27,14 +27,14 @@ fn id<T>(t: T) -> T {
 }
 
 static SOME_STRUCT: &SomeStruct = &SomeStruct {
-    //[nll]~^ ERROR mismatched types
-    //[nll]~| ERROR mismatched types
-    //[nll]~| ERROR implementation of `FnOnce` is not general enough
-    //[nll]~| ERROR implementation of `FnOnce` is not general enough
     foo: &Foo { bools: &[false, true] },
     bar: &Bar { bools: &[true, true] },
     f: &id,
     //[base]~^ ERROR implementation of `FnOnce` is not general enough
+    //[nll]~^^ ERROR mismatched types
+    //[nll]~| ERROR mismatched types
+    //[nll]~| ERROR implementation of `FnOnce` is not general enough
+    //[nll]~| ERROR implementation of `FnOnce` is not general enough
 };
 
 // very simple test for a 'static static with default lifetime
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index dbf5cf9650c..17362338355 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -58,7 +58,8 @@ function extractFunction(content, functionName) {
                 } while (pos < content.length && content[pos] !== '/' && content[pos - 1] !== '*');
 
             // Eat quoted strings
-            } else if (content[pos] === '"' || content[pos] === "'" || content[pos] === "`") {
+            } else if ((content[pos] === '"' || content[pos] === "'" || content[pos] === "`") &&
+                       (pos === 0 || content[pos - 1] !== '/')) {
                 stop = content[pos];
                 do {
                     if (content[pos] === '\\') {
@@ -269,8 +270,13 @@ function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
     // execQuery last parameter is built in buildIndex.
     // buildIndex requires the hashmap from search-index.
     var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult",
-                           "handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch",
-                           "removeEmptyStringsFromArray"];
+                           "buildIndex", "execQuery", "parseQuery", "createQueryResults",
+                           "isWhitespace", "isSpecialStartCharacter", "isStopCharacter",
+                           "parseInput", "getItemsBefore", "getNextElem", "createQueryElement",
+                           "isReturnArrow", "isPathStart", "getStringElem", "newParsedQuery",
+                           "itemTypeFromName", "isEndCharacter", "isErrorCharacter",
+                           "isIdentCharacter", "isSeparatorCharacter", "getIdentEndPosition",
+                           "checkExtraTypeFilterCharacters", "isWhitespaceCharacter"];
 
     const functions = ["hasOwnPropertyRustdoc", "onEach"];
     ALIASES = {};
@@ -286,12 +292,99 @@ function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
     return [loaded, index];
 }
 
+// This function checks if `expected` has all the required fields needed for the checks.
+function checkNeededFields(fullPath, expected, error_text, queryName, position) {
+    let fieldsToCheck;
+    if (fullPath.length === 0) {
+        fieldsToCheck = [
+            "foundElems",
+            "original",
+            "returned",
+            "typeFilter",
+            "userQuery",
+            "error",
+        ];
+    } else if (fullPath.endsWith("elems") || fullPath.endsWith("generics")) {
+        fieldsToCheck = [
+            "name",
+            "fullPath",
+            "pathWithoutLast",
+            "pathLast",
+            "generics",
+        ];
+    } else {
+        fieldsToCheck = [];
+    }
+    for (var i = 0; i < fieldsToCheck.length; ++i) {
+        const field = fieldsToCheck[i];
+        if (!expected.hasOwnProperty(field)) {
+            let text = `${queryName}==> Mandatory key \`${field}\` is not present`;
+            if (fullPath.length > 0) {
+                text += ` in field \`${fullPath}\``;
+                if (position != null) {
+                    text += ` (position ${position})`;
+                }
+            }
+            error_text.push(text);
+        }
+    }
+}
+
+function valueCheck(fullPath, expected, result, error_text, queryName) {
+    if (Array.isArray(expected)) {
+        for (var i = 0; i < expected.length; ++i) {
+            checkNeededFields(fullPath, expected[i], error_text, queryName, i);
+            if (i >= result.length) {
+                error_text.push(`${queryName}==> EXPECTED has extra value in array from field ` +
+                    `\`${fullPath}\` (position ${i}): \`${JSON.stringify(expected[i])}\``);
+            } else {
+                valueCheck(fullPath + '[' + i + ']', expected[i], result[i], error_text, queryName);
+            }
+        }
+        for (; i < result.length; ++i) {
+            error_text.push(`${queryName}==> RESULT has extra value in array from field ` +
+                `\`${fullPath}\` (position ${i}): \`${JSON.stringify(result[i])}\` ` +
+                'compared to EXPECTED');
+        }
+    } else if (expected !== null && typeof expected !== "undefined" &&
+               expected.constructor == Object)
+    {
+        for (const key in expected) {
+            if (!expected.hasOwnProperty(key)) {
+                continue;
+            }
+            if (!result.hasOwnProperty(key)) {
+                error_text.push('==> Unknown key "' + key + '"');
+                break;
+            }
+            const obj_path = fullPath + (fullPath.length > 0 ? '.' : '') + key;
+            valueCheck(obj_path, expected[key], result[key], error_text, queryName);
+        }
+    } else {
+        expectedValue = JSON.stringify(expected);
+        resultValue = JSON.stringify(result);
+        if (expectedValue != resultValue) {
+            error_text.push(`${queryName}==> Different values for field \`${fullPath}\`:\n` +
+                `EXPECTED: \`${expectedValue}\`\nRESULT:   \`${resultValue}\``);
+        }
+    }
+}
+
+function runParser(query, expected, loaded, loadedFile, queryName) {
+    var error_text = [];
+    checkNeededFields("", expected, error_text, queryName, null);
+    if (error_text.length === 0) {
+        valueCheck('', expected, loaded.parseQuery(query), error_text, queryName);
+    }
+    return error_text;
+}
+
 function runSearch(query, expected, index, loaded, loadedFile, queryName) {
     const filter_crate = loadedFile.FILTER_CRATE;
     const ignore_order = loadedFile.ignore_order;
     const exact_check = loadedFile.exact_check;
 
-    var results = loaded.execSearch(loaded.getQuery(query), index, filter_crate);
+    var results = loaded.execQuery(loaded.parseQuery(query), index, filter_crate);
     var error_text = [];
 
     for (var key in expected) {
@@ -353,40 +446,75 @@ function checkResult(error_text, loadedFile, displaySuccess) {
     return 1;
 }
 
-function runChecks(testFile, loaded, index) {
-    var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;';
-    if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
-        testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
-    } else {
-        testFileContent += "exports.FILTER_CRATE = null;";
-    }
-    var loadedFile = loadContent(testFileContent);
-
-    const expected = loadedFile.EXPECTED;
+function runCheck(loadedFile, key, callback) {
+    const expected = loadedFile[key];
     const query = loadedFile.QUERY;
 
     if (Array.isArray(query)) {
         if (!Array.isArray(expected)) {
             console.log("FAILED");
-            console.log("==> If QUERY variable is an array, EXPECTED should be an array too");
+            console.log(`==> If QUERY variable is an array, ${key} should be an array too`);
             return 1;
         } else if (query.length !== expected.length) {
             console.log("FAILED");
-            console.log("==> QUERY variable should have the same length as EXPECTED");
+            console.log(`==> QUERY variable should have the same length as ${key}`);
             return 1;
         }
         for (var i = 0; i < query.length; ++i) {
-            var error_text = runSearch(query[i], expected[i], index, loaded, loadedFile,
-                "[ query `" + query[i] + "`]");
+            var error_text = callback(query[i], expected[i], "[ query `" + query[i] + "`]");
             if (checkResult(error_text, loadedFile, false) !== 0) {
                 return 1;
             }
         }
         console.log("OK");
-        return 0;
+    } else {
+        var error_text = callback(query, expected, "");
+        if (checkResult(error_text, loadedFile, true) !== 0) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+function runChecks(testFile, loaded, index) {
+    var checkExpected = false;
+    var checkParsed = false;
+    var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;';
+
+    if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
+        testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
+    } else {
+        testFileContent += "exports.FILTER_CRATE = null;";
+    }
+
+    if (testFileContent.indexOf("\nconst EXPECTED") !== -1) {
+        testFileContent += 'exports.EXPECTED = EXPECTED;';
+        checkExpected = true;
+    }
+    if (testFileContent.indexOf("\nconst PARSED") !== -1) {
+        testFileContent += 'exports.PARSED = PARSED;';
+        checkParsed = true;
+    }
+    if (!checkParsed && !checkExpected) {
+        console.log("FAILED");
+        console.log("==> At least `PARSED` or `EXPECTED` is needed!");
+        return 1;
+    }
+
+    const loadedFile = loadContent(testFileContent);
+    var res = 0;
+
+    if (checkExpected) {
+        res += runCheck(loadedFile, "EXPECTED", (query, expected, text) => {
+            return runSearch(query, expected, index, loaded, loadedFile, text);
+        });
+    }
+    if (checkParsed) {
+        res += runCheck(loadedFile, "PARSED", (query, expected, text) => {
+            return runParser(query, expected, loaded, loadedFile, text);
+        });
     }
-    var error_text = runSearch(query, expected, index, loaded, loadedFile, "");
-    return checkResult(error_text, loadedFile, true);
+    return res;
 }
 
 function load_files(doc_folder, resource_suffix, crate) {