about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/clean/types.rs9
-rw-r--r--src/librustdoc/html/layout.rs1
-rw-r--r--src/librustdoc/html/render.rs43
-rw-r--r--src/librustdoc/html/render/cache.rs64
-rw-r--r--src/librustdoc/html/static/main.js136
-rw-r--r--src/test/rustdoc-js-std/alias-2.js4
-rw-r--r--src/test/rustdoc-js/doc-alias.js263
-rw-r--r--src/test/rustdoc-js/doc-alias.rs79
-rw-r--r--src/tools/rustdoc-js/tester.js105
9 files changed, 561 insertions, 143 deletions
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 8bf811877a6..38123816527 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -643,6 +643,15 @@ impl Attributes {
             })
             .collect()
     }
+
+    pub fn get_doc_aliases(&self) -> FxHashSet<String> {
+        self.other_attrs
+            .lists(sym::doc)
+            .filter(|a| a.check_name(sym::alias))
+            .filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", "")))
+            .filter(|v| !v.is_empty())
+            .collect::<FxHashSet<_>>()
+    }
 }
 
 impl PartialEq for Attributes {
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 0922c8cdd12..ea65b390527 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -114,7 +114,6 @@ pub fn render<T: Print, S: Print>(
         window.rootPath = \"{root_path}\";\
         window.currentCrate = \"{krate}\";\
     </script>\
-    <script src=\"{root_path}aliases{suffix}.js\"></script>\
     <script src=\"{static_root_path}main{suffix}.js\"></script>\
     {static_extra_scripts}\
     {extra_scripts}\
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 00c9e46570a..646c663ad9c 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -293,7 +293,12 @@ impl Serialize for IndexItem {
     where
         S: Serializer,
     {
-        assert_eq!(self.parent.is_some(), self.parent_idx.is_some());
+        assert_eq!(
+            self.parent.is_some(),
+            self.parent_idx.is_some(),
+            "`{}` is missing idx",
+            self.name
+        );
 
         (self.ty, &self.name, &self.path, &self.desc, self.parent_idx, &self.search_type)
             .serialize(serializer)
@@ -819,42 +824,6 @@ themePicker.onblur = handleThemeButtonsBlur;
         Ok((ret, krates))
     }
 
-    fn show_item(item: &IndexItem, krate: &str) -> String {
-        format!(
-            "{{'crate':'{}','ty':{},'name':'{}','desc':'{}','p':'{}'{}}}",
-            krate,
-            item.ty as usize,
-            item.name,
-            item.desc.replace("'", "\\'"),
-            item.path,
-            if let Some(p) = item.parent_idx { format!(",'parent':{}", p) } else { String::new() }
-        )
-    }
-
-    let dst = cx.dst.join(&format!("aliases{}.js", cx.shared.resource_suffix));
-    {
-        let (mut all_aliases, _) = try_err!(collect(&dst, &krate.name, "ALIASES"), &dst);
-        let mut output = String::with_capacity(100);
-        for (alias, items) in &cx.cache.aliases {
-            if items.is_empty() {
-                continue;
-            }
-            output.push_str(&format!(
-                "\"{}\":[{}],",
-                alias,
-                items.iter().map(|v| show_item(v, &krate.name)).collect::<Vec<_>>().join(",")
-            ));
-        }
-        all_aliases.push(format!("ALIASES[\"{}\"] = {{{}}};", krate.name, output));
-        all_aliases.sort();
-        let mut v = Buffer::html();
-        writeln!(&mut v, "var ALIASES = {{}};");
-        for aliases in &all_aliases {
-            writeln!(&mut v, "{}", aliases);
-        }
-        cx.shared.fs.write(&dst, v.into_inner().into_bytes())?;
-    }
-
     use std::ffi::OsString;
 
     #[derive(Debug)]
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index 5b090291227..57d385de320 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -120,7 +120,7 @@ crate struct Cache {
 
     /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
     /// we need the alias element to have an array of items.
-    pub(super) aliases: FxHashMap<String, Vec<IndexItem>>,
+    pub(super) aliases: BTreeMap<String, Vec<usize>>,
 }
 
 impl Cache {
@@ -311,7 +311,7 @@ impl DocFolder for Cache {
             };
 
             match parent {
-                (parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => {
+                (parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => {
                     debug_assert!(!item.is_stripped());
 
                     // A crate has a module at its root, containing all items,
@@ -327,6 +327,13 @@ impl DocFolder for Cache {
                             parent_idx: None,
                             search_type: get_index_search_type(&item),
                         });
+
+                        for alias in item.attrs.get_doc_aliases() {
+                            self.aliases
+                                .entry(alias.to_lowercase())
+                                .or_insert(Vec::new())
+                                .push(self.search_index.len() - 1);
+                        }
                     }
                 }
                 (Some(parent), None) if is_inherent_impl_item => {
@@ -376,11 +383,8 @@ impl DocFolder for Cache {
                 {
                     self.paths.insert(item.def_id, (self.stack.clone(), item.type_()));
                 }
-                self.add_aliases(&item);
             }
-
             clean::PrimitiveItem(..) => {
-                self.add_aliases(&item);
                 self.paths.insert(item.def_id, (self.stack.clone(), item.type_()));
             }
 
@@ -488,40 +492,6 @@ impl DocFolder for Cache {
     }
 }
 
-impl Cache {
-    fn add_aliases(&mut self, item: &clean::Item) {
-        if item.def_id.index == CRATE_DEF_INDEX {
-            return;
-        }
-        if let Some(ref item_name) = item.name {
-            let path = self
-                .paths
-                .get(&item.def_id)
-                .map(|p| p.0[..p.0.len() - 1].join("::"))
-                .unwrap_or("std".to_owned());
-            for alias in item
-                .attrs
-                .lists(sym::doc)
-                .filter(|a| a.check_name(sym::alias))
-                .filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", "")))
-                .filter(|v| !v.is_empty())
-                .collect::<FxHashSet<_>>()
-                .into_iter()
-            {
-                self.aliases.entry(alias).or_insert(Vec::with_capacity(1)).push(IndexItem {
-                    ty: item.type_(),
-                    name: item_name.to_string(),
-                    path: path.clone(),
-                    desc: shorten(plain_summary_line(item.doc_value())),
-                    parent: None,
-                    parent_idx: None,
-                    search_type: get_index_search_type(&item),
-                });
-            }
-        }
-    }
-}
-
 /// Attempts to find where an external crate is located, given that we're
 /// rendering in to the specified source destination.
 fn extern_location(
@@ -567,7 +537,8 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
     let mut crate_items = Vec::with_capacity(cache.search_index.len());
     let mut crate_paths = vec![];
 
-    let Cache { ref mut search_index, ref orphan_impl_items, ref paths, .. } = *cache;
+    let Cache { ref mut search_index, ref orphan_impl_items, ref paths, ref mut aliases, .. } =
+        *cache;
 
     // Attach all orphan items to the type's definition if the type
     // has since been learned.
@@ -582,6 +553,12 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
                 parent_idx: None,
                 search_type: get_index_search_type(&item),
             });
+            for alias in item.attrs.get_doc_aliases() {
+                aliases
+                    .entry(alias.to_lowercase())
+                    .or_insert(Vec::new())
+                    .push(search_index.len() - 1);
+            }
         }
     }
 
@@ -630,6 +607,12 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
         items: Vec<&'a IndexItem>,
         #[serde(rename = "p")]
         paths: Vec<(ItemType, String)>,
+        // The String is alias name and the vec is the list of the elements with this alias.
+        //
+        // To be noted: the `usize` elements are indexes to `items`.
+        #[serde(rename = "a")]
+        #[serde(skip_serializing_if = "BTreeMap::is_empty")]
+        aliases: &'a BTreeMap<String, Vec<usize>>,
     }
 
     // Collect the index into a string
@@ -640,6 +623,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
             doc: crate_doc,
             items: crate_items,
             paths: crate_paths,
+            aliases,
         })
         .expect("failed serde conversion")
         // All these `replace` calls are because we have to go through JS string for JSON content.
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index a023d5a2d95..9b498d66249 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -531,6 +531,7 @@ function getSearchElement() {
         var OUTPUT_DATA = 1;
         var NO_TYPE_FILTER = -1;
         var currentResults, index, searchIndex;
+        var ALIASES = {};
         var params = getQueryStringParams();
 
         // Populate search bar with query string search term when provided,
@@ -963,6 +964,72 @@ function getSearchElement() {
                 return itemTypes[ty.ty] + ty.path + ty.name;
             }
 
+            function createAliasFromItem(item) {
+                return {
+                    crate: item.crate,
+                    name: item.name,
+                    path: item.path,
+                    desc: item.desc,
+                    ty: item.ty,
+                    parent: item.parent,
+                    type: item.type,
+                    is_alias: true,
+                };
+            }
+
+            function handleAliases(ret, query, filterCrates) {
+                // 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 = [];
+                var i;
+                if (filterCrates !== undefined &&
+                        ALIASES[filterCrates] &&
+                        ALIASES[filterCrates][query.search]) {
+                    for (i = 0; i < ALIASES[crate][query.search].length; ++i) {
+                        aliases.push(
+                            createAliasFromItem(searchIndex[ALIASES[filterCrates][query.search]]));
+                    }
+                } else {
+                    Object.keys(ALIASES).forEach(function(crate) {
+                        if (ALIASES[crate][query.search]) {
+                            var pushTo = crate === window.currentCrate ? crateAliases : aliases;
+                            for (i = 0; i < ALIASES[crate][query.search].length; ++i) {
+                                pushTo.push(
+                                    createAliasFromItem(
+                                        searchIndex[ALIASES[crate][query.search][i]]));
+                            }
+                        }
+                    });
+                }
+
+                var sortFunc = function(aaa, bbb) {
+                    if (aaa.path < bbb.path) {
+                        return 1;
+                    } else if (aaa.path === bbb.path) {
+                        return 0;
+                    }
+                    return -1;
+                };
+                crateAliases.sort(sortFunc);
+                aliases.sort(sortFunc);
+
+                var pushFunc = function(alias) {
+                    alias.alias = query.raw;
+                    var res = buildHrefAndPath(alias);
+                    alias.displayPath = pathSplitter(res[0]);
+                    alias.fullPath = alias.displayPath + alias.name;
+                    alias.href = res[1];
+
+                    ret.others.unshift(alias);
+                    if (ret.others.length > MAX_RESULTS) {
+                        ret.others.pop();
+                    }
+                };
+                onEach(aliases, pushFunc);
+                onEach(crateAliases, pushFunc);
+            }
+
             // quoted values mean literal search
             var nSearchWords = searchWords.length;
             var i;
@@ -1190,23 +1257,7 @@ function getSearchElement() {
                 "returned": sortResults(results_returned, true),
                 "others": sortResults(results),
             };
-            if (ALIASES && ALIASES[window.currentCrate] &&
-                    ALIASES[window.currentCrate][query.raw]) {
-                var aliases = ALIASES[window.currentCrate][query.raw];
-                for (i = 0; i < aliases.length; ++i) {
-                    aliases[i].is_alias = true;
-                    aliases[i].alias = query.raw;
-                    aliases[i].path = aliases[i].p;
-                    var res = buildHrefAndPath(aliases[i]);
-                    aliases[i].displayPath = pathSplitter(res[0]);
-                    aliases[i].fullPath = aliases[i].displayPath + aliases[i].name;
-                    aliases[i].href = res[1];
-                    ret.others.unshift(aliases[i]);
-                    if (ret.others.length > MAX_RESULTS) {
-                        ret.others.pop();
-                    }
-                }
-            }
+            handleAliases(ret, query, filterCrates);
             return ret;
         }
 
@@ -1599,13 +1650,12 @@ function getSearchElement() {
                     "returned": mergeArrays(results.returned),
                     "others": mergeArrays(results.others),
                 };
-            } else {
-                return {
-                    "in_args": results.in_args[0],
-                    "returned": results.returned[0],
-                    "others": results.others[0],
-                };
             }
+            return {
+                "in_args": results.in_args[0],
+                "returned": results.returned[0],
+                "others": results.others[0],
+            };
         }
 
         function getFilterCrates() {
@@ -1656,10 +1706,13 @@ function getSearchElement() {
             searchIndex = [];
             var searchWords = [];
             var i;
+            var currentIndex = 0;
 
             for (var crate in rawSearchIndex) {
                 if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
 
+                var crateSize = 0;
+
                 searchWords.push(crate);
                 searchIndex.push({
                     crate: crate,
@@ -1669,6 +1722,7 @@ function getSearchElement() {
                     desc: rawSearchIndex[crate].doc,
                     type: null,
                 });
+                currentIndex += 1;
 
                 // an array of [(Number) item type,
                 //              (String) name,
@@ -1680,6 +1734,9 @@ function getSearchElement() {
                 // an array of [(Number) item type,
                 //              (String) name]
                 var paths = rawSearchIndex[crate].p;
+                // a array of [(String) alias name
+                //             [Number] index to items]
+                var aliases = rawSearchIndex[crate].a;
 
                 // convert `rawPaths` entries into object form
                 var len = paths.length;
@@ -1698,9 +1755,18 @@ function getSearchElement() {
                 var lastPath = "";
                 for (i = 0; i < len; ++i) {
                     var rawRow = items[i];
-                    var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
-                               path: rawRow[2] || lastPath, desc: rawRow[3],
-                               parent: paths[rawRow[4]], type: rawRow[5]};
+                    if (!rawRow[2]) {
+                        rawRow[2] = lastPath;
+                    }
+                    var row = {
+                        crate: crate,
+                        ty: rawRow[0],
+                        name: rawRow[1],
+                        path: rawRow[2],
+                        desc: rawRow[3],
+                        parent: paths[rawRow[4]],
+                        type: rawRow[5],
+                    };
                     searchIndex.push(row);
                     if (typeof row.name === "string") {
                         var word = row.name.toLowerCase();
@@ -1709,7 +1775,25 @@ function getSearchElement() {
                         searchWords.push("");
                     }
                     lastPath = row.path;
+                    crateSize += 1;
+                }
+
+                if (aliases) {
+                    ALIASES[crate] = {};
+                    var j, local_aliases;
+                    for (var alias_name in aliases) {
+                        if (!aliases.hasOwnProperty(alias_name)) { continue; }
+
+                        if (!ALIASES[crate].hasOwnProperty(alias_name)) {
+                            ALIASES[crate][alias_name] = [];
+                        }
+                        local_aliases = aliases[alias_name];
+                        for (j = 0; j < local_aliases.length; ++j) {
+                            ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
+                        }
+                    }
                 }
+                currentIndex += crateSize;
             }
             return searchWords;
         }
diff --git a/src/test/rustdoc-js-std/alias-2.js b/src/test/rustdoc-js-std/alias-2.js
index f3c6713692b..798fa29efbd 100644
--- a/src/test/rustdoc-js-std/alias-2.js
+++ b/src/test/rustdoc-js-std/alias-2.js
@@ -1,10 +1,10 @@
-// ignore-order
-
 const QUERY = '+';
 
 const EXPECTED = {
     'others': [
         { 'path': 'std::ops', 'name': 'AddAssign' },
         { 'path': 'std::ops', 'name': 'Add' },
+        { 'path': 'core::ops', 'name': 'AddAssign' },
+        { 'path': 'core::ops', 'name': 'Add' },
     ],
 };
diff --git a/src/test/rustdoc-js/doc-alias.js b/src/test/rustdoc-js/doc-alias.js
new file mode 100644
index 00000000000..896808d4157
--- /dev/null
+++ b/src/test/rustdoc-js/doc-alias.js
@@ -0,0 +1,263 @@
+// exact-check
+
+const QUERY = [
+    'StructItem',
+    'StructFieldItem',
+    'StructMethodItem',
+    'ImplTraitItem',
+    'ImplAssociatedConstItem',
+    'ImplTraitFunction',
+    'EnumItem',
+    'VariantItem',
+    'EnumMethodItem',
+    'TypedefItem',
+    'TraitItem',
+    'TraitTypeItem',
+    'AssociatedConstItem',
+    'TraitFunctionItem',
+    'FunctionItem',
+    'ModuleItem',
+    'ConstItem',
+    'StaticItem',
+    'UnionItem',
+    'UnionFieldItem',
+    'UnionMethodItem',
+    'MacroItem',
+];
+
+const EXPECTED = [
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Struct',
+                'alias': 'StructItem',
+                'href': '../doc_alias/struct.Struct.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Struct',
+                'name': 'field',
+                'alias': 'StructFieldItem',
+                'href': '../doc_alias/struct.Struct.html#structfield.field',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Struct',
+                'name': 'method',
+                'alias': 'StructMethodItem',
+                'href': '../doc_alias/struct.Struct.html#method.method',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        // ImplTraitItem
+        'others': [],
+    },
+    {
+        // ImplAssociatedConstItem
+        'others': [],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Struct',
+                'name': 'function',
+                'alias': 'ImplTraitFunction',
+                'href': '../doc_alias/struct.Struct.html#method.function',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Enum',
+                'alias': 'EnumItem',
+                'href': '../doc_alias/enum.Enum.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Enum',
+                'name': 'Variant',
+                'alias': 'VariantItem',
+                'href': '../doc_alias/enum.Enum.html#variant.Variant',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Enum',
+                'name': 'method',
+                'alias': 'EnumMethodItem',
+                'href': '../doc_alias/enum.Enum.html#method.method',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Typedef',
+                'alias': 'TypedefItem',
+                'href': '../doc_alias/type.Typedef.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Trait',
+                'alias': 'TraitItem',
+                'href': '../doc_alias/trait.Trait.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Trait',
+                'name': 'Target',
+                'alias': 'TraitTypeItem',
+                'href': '../doc_alias/trait.Trait.html#associatedtype.Target',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Trait',
+                'name': 'AssociatedConst',
+                'alias': 'AssociatedConstItem',
+                'href': '../doc_alias/trait.Trait.html#associatedconstant.AssociatedConst',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Trait',
+                'name': 'function',
+                'alias': 'TraitFunctionItem',
+                'href': '../doc_alias/trait.Trait.html#tymethod.function',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'function',
+                'alias': 'FunctionItem',
+                'href': '../doc_alias/fn.function.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Module',
+                'alias': 'ModuleItem',
+                'href': '../doc_alias/Module/index.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Const',
+                'alias': 'ConstItem',
+                'href': '../doc_alias/constant.Const.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Static',
+                'alias': 'StaticItem',
+                'href': '../doc_alias/static.Static.html',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Union',
+                'alias': 'UnionItem',
+                'href': '../doc_alias/union.Union.html',
+                'is_alias': true
+            },
+            // Not an alias!
+            {
+                'path': 'doc_alias::Union',
+                'name': 'union_item',
+                'href': '../doc_alias/union.Union.html#structfield.union_item'
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Union',
+                'name': 'union_item',
+                'alias': 'UnionFieldItem',
+                'href': '../doc_alias/union.Union.html#structfield.union_item',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias::Union',
+                'name': 'method',
+                'alias': 'UnionMethodItem',
+                'href': '../doc_alias/union.Union.html#method.method',
+                'is_alias': true
+            },
+        ],
+    },
+    {
+        'others': [
+            {
+                'path': 'doc_alias',
+                'name': 'Macro',
+                'alias': 'MacroItem',
+                'href': '../doc_alias/macro.Macro.html',
+                'is_alias': true
+            },
+        ],
+    },
+];
diff --git a/src/test/rustdoc-js/doc-alias.rs b/src/test/rustdoc-js/doc-alias.rs
new file mode 100644
index 00000000000..84c638a1995
--- /dev/null
+++ b/src/test/rustdoc-js/doc-alias.rs
@@ -0,0 +1,79 @@
+#![feature(doc_alias)]
+
+#[doc(alias = "StructItem")]
+pub struct Struct {
+    #[doc(alias = "StructFieldItem")]
+    pub field: u32,
+}
+
+impl Struct {
+    #[doc(alias = "StructMethodItem")]
+    pub fn method(&self) {}
+}
+
+impl Trait for Struct {
+    // Shouldn't be listed in aliases!
+    #[doc(alias = "ImplTraitItem")]
+    type Target = u32;
+    // Shouldn't be listed in aliases!
+    #[doc(alias = "ImplAssociatedConstItem")]
+    const AssociatedConst: i32 = 12;
+
+    #[doc(alias = "ImplTraitFunction")]
+    fn function() -> Self::Target { 0 }
+}
+
+#[doc(alias = "EnumItem")]
+pub enum Enum {
+    #[doc(alias = "VariantItem")]
+    Variant,
+}
+
+impl Enum {
+    #[doc(alias = "EnumMethodItem")]
+    pub fn method(&self) {}
+}
+
+#[doc(alias = "TypedefItem")]
+pub type Typedef = i32;
+
+#[doc(alias = "TraitItem")]
+pub trait Trait {
+    #[doc(alias = "TraitTypeItem")]
+    type Target;
+    #[doc(alias = "AssociatedConstItem")]
+    const AssociatedConst: i32;
+
+    #[doc(alias = "TraitFunctionItem")]
+    fn function() -> Self::Target;
+}
+
+#[doc(alias = "FunctionItem")]
+pub fn function() {}
+
+#[doc(alias = "ModuleItem")]
+pub mod Module {}
+
+#[doc(alias = "ConstItem")]
+pub const Const: u32 = 0;
+
+#[doc(alias = "StaticItem")]
+pub static Static: u32 = 0;
+
+#[doc(alias = "UnionItem")]
+pub union Union {
+    #[doc(alias = "UnionFieldItem")]
+    pub union_item: u32,
+    pub y: f32,
+}
+
+impl Union {
+    #[doc(alias = "UnionMethodItem")]
+    pub fn method(&self) {}
+}
+
+#[doc(alias = "MacroItem")]
+#[macro_export]
+macro_rules! Macro {
+    () => {}
+}
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 03f06fc1c6c..1fa46ce99f5 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -181,7 +181,7 @@ function loadThings(thingsToLoad, kindOfLoad, funcToCall, fileContent) {
     for (var i = 0; i < thingsToLoad.length; ++i) {
         var tmp = funcToCall(fileContent, thingsToLoad[i]);
         if (tmp === null) {
-            console.error('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"');
+            console.log('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"');
             process.exit(1);
         }
         content += tmp;
@@ -218,12 +218,13 @@ function lookForEntry(entry, data) {
     return null;
 }
 
-function loadMainJsAndIndex(mainJs, aliases, searchIndex, crate) {
+function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) {
     if (searchIndex[searchIndex.length - 1].length === 0) {
         searchIndex.pop();
     }
     searchIndex.pop();
-    searchIndex = loadContent(searchIndex.join("\n") + '\nexports.searchIndex = searchIndex;');
+    var fullSearchIndex = searchIndex.join("\n") + '\nexports.rawSearchIndex = searchIndex;';
+    searchIndex = loadContent(fullSearchIndex);
     var finalJS = "";
 
     var arraysToLoad = ["itemTypes"];
@@ -235,34 +236,28 @@ function loadMainJsAndIndex(mainJs, aliases, searchIndex, crate) {
     // execQuery last parameter is built in buildIndex.
     // buildIndex requires the hashmap from search-index.
     var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult",
-                           "getQuery", "buildIndex", "execQuery", "execSearch"];
+                           "handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch"];
 
+    ALIASES = {};
     finalJS += 'window = { "currentCrate": "' + crate + '" };\n';
     finalJS += 'var rootPath = "../";\n';
-    finalJS += aliases;
+    finalJS += loadThings(["onEach"], 'function', extractFunction, storageJs);
     finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs);
     finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs);
     finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs);
 
     var loaded = loadContent(finalJS);
-    var index = loaded.buildIndex(searchIndex.searchIndex);
+    var index = loaded.buildIndex(searchIndex.rawSearchIndex);
 
     return [loaded, index];
 }
 
-function runChecks(testFile, loaded, index) {
-    var errors = 0;
-    var loadedFile = loadContent(
-        readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;');
-
-    const expected = loadedFile.EXPECTED;
-    const query = loadedFile.QUERY;
+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;
-    const should_fail = loadedFile.should_fail;
 
-    var results = loaded.execSearch(loaded.getQuery(query), index);
+    var results = loaded.execSearch(loaded.getQuery(query), index, filter_crate);
     var error_text = [];
 
     for (var key in expected) {
@@ -278,41 +273,77 @@ function runChecks(testFile, loaded, index) {
         for (var i = 0; i < entry.length; ++i) {
             var entry_pos = lookForEntry(entry[i], results[key]);
             if (entry_pos === null) {
-                error_text.push("==> Result not found in '" + key + "': '" +
+                error_text.push(queryName + "==> Result not found in '" + key + "': '" +
                                 JSON.stringify(entry[i]) + "'");
             } else if (exact_check === true && prev_pos + 1 !== entry_pos) {
-                error_text.push("==> Exact check failed at position " + (prev_pos + 1) + ": " +
-                                "expected '" + JSON.stringify(entry[i]) + "' but found '" +
+                error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) +
+                                ": expected '" + JSON.stringify(entry[i]) + "' but found '" +
                                 JSON.stringify(results[key][i]) + "'");
             } else if (ignore_order === false && entry_pos < prev_pos) {
-                error_text.push("==> '" + JSON.stringify(entry[i]) + "' was supposed to be " +
-                                " before '" + JSON.stringify(results[key][entry_pos]) + "'");
+                error_text.push(queryName + "==> '" + JSON.stringify(entry[i]) + "' was supposed " +
+                                "to be before '" + JSON.stringify(results[key][entry_pos]) + "'");
             } else {
                 prev_pos = entry_pos;
             }
         }
     }
-    if (error_text.length === 0 && should_fail === true) {
-        errors += 1;
-        console.error("FAILED");
-        console.error("==> Test was supposed to fail but all items were found...");
-    } else if (error_text.length !== 0 && should_fail === false) {
-        errors += 1;
-        console.error("FAILED");
-        console.error(error_text.join("\n"));
+    return error_text;
+}
+
+function checkResult(error_text, loadedFile, displaySuccess) {
+    if (error_text.length === 0 && loadedFile.should_fail === true) {
+        console.log("FAILED");
+        console.log("==> Test was supposed to fail but all items were found...");
+    } else if (error_text.length !== 0 && loadedFile.should_fail === false) {
+        console.log("FAILED");
+        console.log(error_text.join("\n"));
     } else {
+        if (displaySuccess) {
+            console.log("OK");
+        }
+        return 0;
+    }
+    return 1;
+}
+
+function runChecks(testFile, loaded, index) {
+    var loadedFile = loadContent(
+        readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;');
+
+    const expected = loadedFile.EXPECTED;
+    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");
+            return 1;
+        } else if (query.length !== expected.length) {
+            console.log("FAILED");
+            console.log("==> QUERY variable should have the same length as EXPECTED");
+            return 1;
+        }
+        for (var i = 0; i < query.length; ++i) {
+            var error_text = runSearch(query[i], expected[i], index, loaded, loadedFile,
+                "[ query `" + query[i] + "`]");
+            if (checkResult(error_text, loadedFile, false) !== 0) {
+                return 1;
+            }
+        }
         console.log("OK");
+        return 0;
     }
-    return errors;
+    var error_text = runSearch(query, expected, index, loaded, loadedFile, "");
+    return checkResult(error_text, loadedFile, true);
 }
 
 function load_files(doc_folder, resource_suffix, crate) {
     var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js"));
-    var aliases = readFile(path.join(doc_folder, "aliases" + resource_suffix + ".js"));
+    var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js"));
     var searchIndex = readFile(
         path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n");
 
-    return loadMainJsAndIndex(mainJs, aliases, searchIndex, crate);
+    return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate);
 }
 
 function showHelp() {
@@ -349,7 +380,7 @@ function parseOptions(args) {
             || args[i] === "--crate-name") {
             i += 1;
             if (i >= args.length) {
-                console.error("Missing argument after `" + args[i - 1] + "` option.");
+                console.log("Missing argument after `" + args[i - 1] + "` option.");
                 return null;
             }
             opts[correspondances[args[i - 1]]] = args[i];
@@ -357,17 +388,17 @@ function parseOptions(args) {
             showHelp();
             process.exit(0);
         } else {
-            console.error("Unknown option `" + args[i] + "`.");
-            console.error("Use `--help` to see the list of options");
+            console.log("Unknown option `" + args[i] + "`.");
+            console.log("Use `--help` to see the list of options");
             return null;
         }
     }
     if (opts["doc_folder"].length < 1) {
-        console.error("Missing `--doc-folder` option.");
+        console.log("Missing `--doc-folder` option.");
     } else if (opts["crate_name"].length < 1) {
-        console.error("Missing `--crate-name` option.");
+        console.log("Missing `--crate-name` option.");
     } else if (opts["test_folder"].length < 1 && opts["test_file"].length < 1) {
-        console.error("At least one of `--test-folder` or `--test-file` option is required.");
+        console.log("At least one of `--test-folder` or `--test-file` option is required.");
     } else {
         return opts;
     }