]> git.lizzy.rs Git - rust.git/commitdiff
Rename `rustdoc::html::render::cache` to `search_index`
authorNoah Lev <camelidcamel@gmail.com>
Tue, 28 Dec 2021 02:39:35 +0000 (18:39 -0800)
committerNoah Lev <camelidcamel@gmail.com>
Tue, 28 Dec 2021 02:42:13 +0000 (18:42 -0800)
The old name wasn't very clear, while the new one makes it clear that
this is the code responsible for creating the search index.

src/librustdoc/clean/types.rs
src/librustdoc/formats/cache.rs
src/librustdoc/html/format.rs
src/librustdoc/html/render/cache.rs [deleted file]
src/librustdoc/html/render/context.rs
src/librustdoc/html/render/mod.rs
src/librustdoc/html/render/search_index.rs [new file with mode: 0644]
src/librustdoc/json/mod.rs

index fe32b75d668ce548dc15581cf7771ef8a553400e..f0e6d4315648e00e25d9530eb5770937e9c66df4 100644 (file)
@@ -38,7 +38,7 @@
 use crate::core::DocContext;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::html::render::cache::ExternalLocation;
+use crate::html::render::search_index::ExternalLocation;
 use crate::html::render::Context;
 
 crate use self::FnRetTy::*;
index 5813062ceab773ff0825b9e0c5f002af2a2d7f80..8a5c8651f99925b6770a775492bf520ba36c2cd5 100644 (file)
@@ -12,7 +12,7 @@
 use crate::formats::item_type::ItemType;
 use crate::formats::Impl;
 use crate::html::markdown::short_markdown_summary;
-use crate::html::render::cache::{get_index_search_type, ExternalLocation};
+use crate::html::render::search_index::{get_index_search_type, ExternalLocation};
 use crate::html::render::IndexItem;
 
 /// This cache is used to store information about the [`clean::Crate`] being
index 3a2effa625cf364333d293cf140a499f91b2b136..f7396f758b103f419a56916c28eb33a4ae22917c 100644 (file)
@@ -24,7 +24,7 @@
 use crate::clean::{self, utils::find_nearest_parent_module, ExternalCrate, ItemId, PrimitiveType};
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
-use crate::html::render::cache::ExternalLocation;
+use crate::html::render::search_index::ExternalLocation;
 use crate::html::render::Context;
 
 use super::url_parts_builder::UrlPartsBuilder;
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
deleted file mode 100644 (file)
index 631eacc..0000000
+++ /dev/null
@@ -1,438 +0,0 @@
-use std::collections::hash_map::Entry;
-use std::collections::BTreeMap;
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::Symbol;
-use serde::ser::{Serialize, SerializeStruct, Serializer};
-
-use crate::clean;
-use crate::clean::types::{FnDecl, FnRetTy, GenericBound, Generics, Type, WherePredicate};
-use crate::formats::cache::Cache;
-use crate::formats::item_type::ItemType;
-use crate::html::markdown::short_markdown_summary;
-use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
-
-/// Indicates where an external crate can be found.
-crate enum ExternalLocation {
-    /// Remote URL root of the external crate
-    Remote(String),
-    /// This external crate can be found in the local doc/ folder
-    Local,
-    /// The external crate could not be found.
-    Unknown,
-}
-
-/// Builds the search index from the collected metadata
-crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>) -> String {
-    let mut defid_to_pathid = FxHashMap::default();
-    let mut crate_paths = vec![];
-
-    // Attach all orphan items to the type's definition if the type
-    // has since been learned.
-    for &(did, ref item) in &cache.orphan_impl_items {
-        if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
-            let desc = item
-                .doc_value()
-                .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
-            cache.search_index.push(IndexItem {
-                ty: item.type_(),
-                name: item.name.unwrap().to_string(),
-                path: fqp[..fqp.len() - 1].join("::"),
-                desc,
-                parent: Some(did),
-                parent_idx: None,
-                search_type: get_index_search_type(item, tcx, cache),
-                aliases: item.attrs.get_doc_aliases(),
-            });
-        }
-    }
-
-    let crate_doc = krate
-        .module
-        .doc_value()
-        .map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(cache)));
-
-    let Cache { ref mut search_index, ref paths, .. } = *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.
-    let mut aliases: BTreeMap<String, Vec<usize>> = BTreeMap::new();
-
-    // Sort search index items. This improves the compressibility of the search index.
-    search_index.sort_unstable_by(|k1, k2| {
-        // `sort_unstable_by_key` produces lifetime errors
-        let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
-        let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
-        std::cmp::Ord::cmp(&k1, &k2)
-    });
-
-    // Set up alias indexes.
-    for (i, item) in search_index.iter().enumerate() {
-        for alias in &item.aliases[..] {
-            aliases.entry(alias.as_str().to_lowercase()).or_default().push(i);
-        }
-    }
-
-    // Reduce `DefId` in paths into smaller sequential numbers,
-    // and prune the paths that do not appear in the index.
-    let mut lastpath = "";
-    let mut lastpathid = 0usize;
-
-    let crate_items: Vec<&IndexItem> = search_index
-        .iter_mut()
-        .map(|item| {
-            item.parent_idx = item.parent.and_then(|defid| match defid_to_pathid.entry(defid) {
-                Entry::Occupied(entry) => Some(*entry.get()),
-                Entry::Vacant(entry) => {
-                    let pathid = lastpathid;
-                    entry.insert(pathid);
-                    lastpathid += 1;
-
-                    if let Some(&(ref fqp, short)) = paths.get(&defid) {
-                        crate_paths.push((short, fqp.last().unwrap().clone()));
-                        Some(pathid)
-                    } else {
-                        None
-                    }
-                }
-            });
-
-            // Omit the parent path if it is same to that of the prior item.
-            if lastpath == &item.path {
-                item.path.clear();
-            } else {
-                lastpath = &item.path;
-            }
-
-            &*item
-        })
-        .collect();
-
-    struct CrateData<'a> {
-        doc: String,
-        items: Vec<&'a IndexItem>,
-        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`.
-        aliases: &'a BTreeMap<String, Vec<usize>>,
-    }
-
-    impl<'a> Serialize for CrateData<'a> {
-        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-        where
-            S: Serializer,
-        {
-            let has_aliases = !self.aliases.is_empty();
-            let mut crate_data =
-                serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
-            crate_data.serialize_field("doc", &self.doc)?;
-            crate_data.serialize_field(
-                "t",
-                &self.items.iter().map(|item| &item.ty).collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "n",
-                &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "q",
-                &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "d",
-                &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "i",
-                &self
-                    .items
-                    .iter()
-                    .map(|item| {
-                        assert_eq!(
-                            item.parent.is_some(),
-                            item.parent_idx.is_some(),
-                            "`{}` is missing idx",
-                            item.name
-                        );
-                        item.parent_idx.map(|x| x + 1).unwrap_or(0)
-                    })
-                    .collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "f",
-                &self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field("p", &self.paths)?;
-            if has_aliases {
-                crate_data.serialize_field("a", &self.aliases)?;
-            }
-            crate_data.end()
-        }
-    }
-
-    // Collect the index into a string
-    format!(
-        r#""{}":{}"#,
-        krate.name(tcx),
-        serde_json::to_string(&CrateData {
-            doc: crate_doc,
-            items: crate_items,
-            paths: crate_paths,
-            aliases: &aliases,
-        })
-        .expect("failed serde conversion")
-        // All these `replace` calls are because we have to go through JS string for JSON content.
-        .replace(r#"\"#, r"\\")
-        .replace(r#"'"#, r"\'")
-        // We need to escape double quotes for the JSON.
-        .replace("\\\"", "\\\\\"")
-    )
-}
-
-crate fn get_index_search_type<'tcx>(
-    item: &clean::Item,
-    tcx: TyCtxt<'tcx>,
-    cache: &Cache,
-) -> Option<IndexItemFunctionType> {
-    let (mut inputs, mut output) = match *item.kind {
-        clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx, cache),
-        clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx, cache),
-        clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx, cache),
-        _ => return None,
-    };
-
-    inputs.retain(|a| a.ty.name.is_some());
-    output.retain(|a| a.ty.name.is_some());
-
-    Some(IndexItemFunctionType { inputs, output })
-}
-
-fn get_index_type(clean_type: &clean::Type, generics: Vec<TypeWithKind>) -> RenderType {
-    RenderType {
-        name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
-        generics: if generics.is_empty() { None } else { Some(generics) },
-    }
-}
-
-fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<Symbol> {
-    match *clean_type {
-        clean::Type::Path { ref path, .. } => {
-            let path_segment = path.segments.last().unwrap();
-            Some(path_segment.name)
-        }
-        clean::DynTrait(ref bounds, _) => {
-            let path = &bounds[0].trait_;
-            Some(path.segments.last().unwrap().name)
-        }
-        clean::Generic(s) if accept_generic => Some(s),
-        clean::Primitive(ref p) => Some(p.as_sym()),
-        clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
-        clean::Generic(_)
-        | clean::BareFunction(_)
-        | clean::Tuple(_)
-        | clean::Slice(_)
-        | clean::Array(_, _)
-        | clean::RawPointer(_, _)
-        | clean::QPath { .. }
-        | clean::Infer
-        | clean::ImplTrait(_) => None,
-    }
-}
-
-/// The point of this function is to replace bounds with types.
-///
-/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
-///
-/// Important note: It goes through generics recursively. So if you have
-/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
-crate fn get_real_types<'tcx>(
-    generics: &Generics,
-    arg: &Type,
-    tcx: TyCtxt<'tcx>,
-    recurse: usize,
-    res: &mut Vec<TypeWithKind>,
-    cache: &Cache,
-) {
-    fn insert_ty(
-        res: &mut Vec<TypeWithKind>,
-        tcx: TyCtxt<'_>,
-        ty: Type,
-        mut generics: Vec<TypeWithKind>,
-        _cache: &Cache,
-    ) {
-        let is_full_generic = ty.is_full_generic();
-
-        if is_full_generic {
-            if generics.is_empty() {
-                // This is a type parameter with no trait bounds (for example: `T` in
-                // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
-                // with an empty type with an empty name. Let's just discard it.
-                return;
-            } else if generics.len() == 1 {
-                // In this case, no need to go through an intermediate state if the type parameter
-                // contains only one trait bound.
-                //
-                // For example:
-                //
-                // `fn foo<T: Display>(r: Option<T>) {}`
-                //
-                // In this case, it would contain:
-                //
-                // ```
-                // [{
-                //     name: "option",
-                //     generics: [{
-                //         name: "",
-                //         generics: [
-                //             name: "Display",
-                //             generics: []
-                //         }]
-                //     }]
-                // }]
-                // ```
-                //
-                // After removing the intermediate (unnecessary) type parameter, it'll become:
-                //
-                // ```
-                // [{
-                //     name: "option",
-                //     generics: [{
-                //         name: "Display",
-                //         generics: []
-                //     }]
-                // }]
-                // ```
-                //
-                // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
-                // need to keep it as is!
-                res.push(generics.pop().unwrap());
-                return;
-            }
-        }
-        let mut index_ty = get_index_type(&ty, generics);
-        if index_ty.name.as_ref().map(|s| s.is_empty()).unwrap_or(true) {
-            return;
-        }
-        if is_full_generic {
-            // We remove the name of the full generic because we have no use for it.
-            index_ty.name = Some(String::new());
-            res.push(TypeWithKind::from((index_ty, ItemType::Generic)));
-        } else if let Some(kind) = ty.def_id_no_primitives().map(|did| tcx.def_kind(did).into()) {
-            res.push(TypeWithKind::from((index_ty, kind)));
-        } else if ty.is_primitive() {
-            // This is a primitive, let's store it as such.
-            res.push(TypeWithKind::from((index_ty, ItemType::Primitive)));
-        }
-    }
-
-    if recurse >= 10 {
-        // FIXME: remove this whole recurse thing when the recursion bug is fixed
-        return;
-    }
-
-    // If this argument is a type parameter and not a trait bound or a type, we need to look
-    // for its bounds.
-    if let Type::Generic(arg_s) = *arg {
-        // First we check if the bounds are in a `where` predicate...
-        if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
-            WherePredicate::BoundPredicate { ty, .. } => {
-                ty.def_id_no_primitives() == arg.def_id_no_primitives()
-            }
-            _ => false,
-        }) {
-            let mut ty_generics = Vec::new();
-            let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
-            for bound in bounds.iter() {
-                if let GenericBound::TraitBound(poly_trait, _) = bound {
-                    for param_def in poly_trait.generic_params.iter() {
-                        match &param_def.kind {
-                            clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
-                                get_real_types(
-                                    generics,
-                                    ty,
-                                    tcx,
-                                    recurse + 1,
-                                    &mut ty_generics,
-                                    cache,
-                                )
-                            }
-                            _ => {}
-                        }
-                    }
-                }
-            }
-            insert_ty(res, tcx, arg.clone(), ty_generics, cache);
-        }
-        // Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
-        if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
-            let mut ty_generics = Vec::new();
-            for bound in bound.get_bounds().unwrap_or(&[]) {
-                if let Some(path) = bound.get_trait_path() {
-                    let ty = Type::Path { path };
-                    get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics, cache);
-                }
-            }
-            insert_ty(res, tcx, arg.clone(), ty_generics, cache);
-        }
-    } else {
-        // This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
-        // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
-        //
-        // So in here, we can add it directly and look for its own type parameters (so for `Option`,
-        // we will look for them but not for `T`).
-        let mut ty_generics = Vec::new();
-        if let Some(arg_generics) = arg.generics() {
-            for gen in arg_generics.iter() {
-                get_real_types(generics, gen, tcx, recurse + 1, &mut ty_generics, cache);
-            }
-        }
-        insert_ty(res, tcx, arg.clone(), ty_generics, cache);
-    }
-}
-
-/// Return the full list of types when bounds have been resolved.
-///
-/// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
-/// `[u32, Display, Option]`.
-crate fn get_all_types<'tcx>(
-    generics: &Generics,
-    decl: &FnDecl,
-    tcx: TyCtxt<'tcx>,
-    cache: &Cache,
-) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
-    let mut all_types = Vec::new();
-    for arg in decl.inputs.values.iter() {
-        if arg.type_.is_self_type() {
-            continue;
-        }
-        let mut args = Vec::new();
-        get_real_types(generics, &arg.type_, tcx, 0, &mut args, cache);
-        if !args.is_empty() {
-            all_types.extend(args);
-        } else {
-            if let Some(kind) = arg.type_.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
-            {
-                all_types.push(TypeWithKind::from((get_index_type(&arg.type_, vec![]), kind)));
-            }
-        }
-    }
-
-    let mut ret_types = Vec::new();
-    match decl.output {
-        FnRetTy::Return(ref return_type) => {
-            get_real_types(generics, return_type, tcx, 0, &mut ret_types, cache);
-            if ret_types.is_empty() {
-                if let Some(kind) =
-                    return_type.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
-                {
-                    ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind)));
-                }
-            }
-        }
-        _ => {}
-    };
-    (all_types, ret_types)
-}
index 45a436c44871002268af23b8e7ac9756f5721e7b..6729129027f551f6eb14aa481d5d7b304bcd0114 100644 (file)
@@ -13,8 +13,8 @@
 use rustc_span::source_map::FileName;
 use rustc_span::symbol::sym;
 
-use super::cache::{build_index, ExternalLocation};
 use super::print_item::{full_path, item_path, print_item};
+use super::search_index::{build_index, ExternalLocation};
 use super::templates;
 use super::write_shared::write_shared;
 use super::{
index eb606178d244d3127f50cf53fe2de931a200d0d6..3e7711181f730b7c5210639ddd48d2d449635b7a 100644 (file)
@@ -23,7 +23,7 @@
 //! These threads are not parallelized (they haven't been a bottleneck yet), and
 //! both occur before the crate is rendered.
 
-crate mod cache;
+crate mod search_index;
 
 #[cfg(test)]
 mod tests;
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
new file mode 100644 (file)
index 0000000..631eacc
--- /dev/null
@@ -0,0 +1,438 @@
+use std::collections::hash_map::Entry;
+use std::collections::BTreeMap;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::Symbol;
+use serde::ser::{Serialize, SerializeStruct, Serializer};
+
+use crate::clean;
+use crate::clean::types::{FnDecl, FnRetTy, GenericBound, Generics, Type, WherePredicate};
+use crate::formats::cache::Cache;
+use crate::formats::item_type::ItemType;
+use crate::html::markdown::short_markdown_summary;
+use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
+
+/// Indicates where an external crate can be found.
+crate enum ExternalLocation {
+    /// Remote URL root of the external crate
+    Remote(String),
+    /// This external crate can be found in the local doc/ folder
+    Local,
+    /// The external crate could not be found.
+    Unknown,
+}
+
+/// Builds the search index from the collected metadata
+crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>) -> String {
+    let mut defid_to_pathid = FxHashMap::default();
+    let mut crate_paths = vec![];
+
+    // Attach all orphan items to the type's definition if the type
+    // has since been learned.
+    for &(did, ref item) in &cache.orphan_impl_items {
+        if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
+            let desc = item
+                .doc_value()
+                .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
+            cache.search_index.push(IndexItem {
+                ty: item.type_(),
+                name: item.name.unwrap().to_string(),
+                path: fqp[..fqp.len() - 1].join("::"),
+                desc,
+                parent: Some(did),
+                parent_idx: None,
+                search_type: get_index_search_type(item, tcx, cache),
+                aliases: item.attrs.get_doc_aliases(),
+            });
+        }
+    }
+
+    let crate_doc = krate
+        .module
+        .doc_value()
+        .map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(cache)));
+
+    let Cache { ref mut search_index, ref paths, .. } = *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.
+    let mut aliases: BTreeMap<String, Vec<usize>> = BTreeMap::new();
+
+    // Sort search index items. This improves the compressibility of the search index.
+    search_index.sort_unstable_by(|k1, k2| {
+        // `sort_unstable_by_key` produces lifetime errors
+        let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
+        let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
+        std::cmp::Ord::cmp(&k1, &k2)
+    });
+
+    // Set up alias indexes.
+    for (i, item) in search_index.iter().enumerate() {
+        for alias in &item.aliases[..] {
+            aliases.entry(alias.as_str().to_lowercase()).or_default().push(i);
+        }
+    }
+
+    // Reduce `DefId` in paths into smaller sequential numbers,
+    // and prune the paths that do not appear in the index.
+    let mut lastpath = "";
+    let mut lastpathid = 0usize;
+
+    let crate_items: Vec<&IndexItem> = search_index
+        .iter_mut()
+        .map(|item| {
+            item.parent_idx = item.parent.and_then(|defid| match defid_to_pathid.entry(defid) {
+                Entry::Occupied(entry) => Some(*entry.get()),
+                Entry::Vacant(entry) => {
+                    let pathid = lastpathid;
+                    entry.insert(pathid);
+                    lastpathid += 1;
+
+                    if let Some(&(ref fqp, short)) = paths.get(&defid) {
+                        crate_paths.push((short, fqp.last().unwrap().clone()));
+                        Some(pathid)
+                    } else {
+                        None
+                    }
+                }
+            });
+
+            // Omit the parent path if it is same to that of the prior item.
+            if lastpath == &item.path {
+                item.path.clear();
+            } else {
+                lastpath = &item.path;
+            }
+
+            &*item
+        })
+        .collect();
+
+    struct CrateData<'a> {
+        doc: String,
+        items: Vec<&'a IndexItem>,
+        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`.
+        aliases: &'a BTreeMap<String, Vec<usize>>,
+    }
+
+    impl<'a> Serialize for CrateData<'a> {
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            let has_aliases = !self.aliases.is_empty();
+            let mut crate_data =
+                serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
+            crate_data.serialize_field("doc", &self.doc)?;
+            crate_data.serialize_field(
+                "t",
+                &self.items.iter().map(|item| &item.ty).collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field(
+                "n",
+                &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field(
+                "q",
+                &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field(
+                "d",
+                &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field(
+                "i",
+                &self
+                    .items
+                    .iter()
+                    .map(|item| {
+                        assert_eq!(
+                            item.parent.is_some(),
+                            item.parent_idx.is_some(),
+                            "`{}` is missing idx",
+                            item.name
+                        );
+                        item.parent_idx.map(|x| x + 1).unwrap_or(0)
+                    })
+                    .collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field(
+                "f",
+                &self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field("p", &self.paths)?;
+            if has_aliases {
+                crate_data.serialize_field("a", &self.aliases)?;
+            }
+            crate_data.end()
+        }
+    }
+
+    // Collect the index into a string
+    format!(
+        r#""{}":{}"#,
+        krate.name(tcx),
+        serde_json::to_string(&CrateData {
+            doc: crate_doc,
+            items: crate_items,
+            paths: crate_paths,
+            aliases: &aliases,
+        })
+        .expect("failed serde conversion")
+        // All these `replace` calls are because we have to go through JS string for JSON content.
+        .replace(r#"\"#, r"\\")
+        .replace(r#"'"#, r"\'")
+        // We need to escape double quotes for the JSON.
+        .replace("\\\"", "\\\\\"")
+    )
+}
+
+crate fn get_index_search_type<'tcx>(
+    item: &clean::Item,
+    tcx: TyCtxt<'tcx>,
+    cache: &Cache,
+) -> Option<IndexItemFunctionType> {
+    let (mut inputs, mut output) = match *item.kind {
+        clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx, cache),
+        clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx, cache),
+        clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx, cache),
+        _ => return None,
+    };
+
+    inputs.retain(|a| a.ty.name.is_some());
+    output.retain(|a| a.ty.name.is_some());
+
+    Some(IndexItemFunctionType { inputs, output })
+}
+
+fn get_index_type(clean_type: &clean::Type, generics: Vec<TypeWithKind>) -> RenderType {
+    RenderType {
+        name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
+        generics: if generics.is_empty() { None } else { Some(generics) },
+    }
+}
+
+fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<Symbol> {
+    match *clean_type {
+        clean::Type::Path { ref path, .. } => {
+            let path_segment = path.segments.last().unwrap();
+            Some(path_segment.name)
+        }
+        clean::DynTrait(ref bounds, _) => {
+            let path = &bounds[0].trait_;
+            Some(path.segments.last().unwrap().name)
+        }
+        clean::Generic(s) if accept_generic => Some(s),
+        clean::Primitive(ref p) => Some(p.as_sym()),
+        clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
+        clean::Generic(_)
+        | clean::BareFunction(_)
+        | clean::Tuple(_)
+        | clean::Slice(_)
+        | clean::Array(_, _)
+        | clean::RawPointer(_, _)
+        | clean::QPath { .. }
+        | clean::Infer
+        | clean::ImplTrait(_) => None,
+    }
+}
+
+/// The point of this function is to replace bounds with types.
+///
+/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
+/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+///
+/// Important note: It goes through generics recursively. So if you have
+/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
+crate fn get_real_types<'tcx>(
+    generics: &Generics,
+    arg: &Type,
+    tcx: TyCtxt<'tcx>,
+    recurse: usize,
+    res: &mut Vec<TypeWithKind>,
+    cache: &Cache,
+) {
+    fn insert_ty(
+        res: &mut Vec<TypeWithKind>,
+        tcx: TyCtxt<'_>,
+        ty: Type,
+        mut generics: Vec<TypeWithKind>,
+        _cache: &Cache,
+    ) {
+        let is_full_generic = ty.is_full_generic();
+
+        if is_full_generic {
+            if generics.is_empty() {
+                // This is a type parameter with no trait bounds (for example: `T` in
+                // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
+                // with an empty type with an empty name. Let's just discard it.
+                return;
+            } else if generics.len() == 1 {
+                // In this case, no need to go through an intermediate state if the type parameter
+                // contains only one trait bound.
+                //
+                // For example:
+                //
+                // `fn foo<T: Display>(r: Option<T>) {}`
+                //
+                // In this case, it would contain:
+                //
+                // ```
+                // [{
+                //     name: "option",
+                //     generics: [{
+                //         name: "",
+                //         generics: [
+                //             name: "Display",
+                //             generics: []
+                //         }]
+                //     }]
+                // }]
+                // ```
+                //
+                // After removing the intermediate (unnecessary) type parameter, it'll become:
+                //
+                // ```
+                // [{
+                //     name: "option",
+                //     generics: [{
+                //         name: "Display",
+                //         generics: []
+                //     }]
+                // }]
+                // ```
+                //
+                // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
+                // need to keep it as is!
+                res.push(generics.pop().unwrap());
+                return;
+            }
+        }
+        let mut index_ty = get_index_type(&ty, generics);
+        if index_ty.name.as_ref().map(|s| s.is_empty()).unwrap_or(true) {
+            return;
+        }
+        if is_full_generic {
+            // We remove the name of the full generic because we have no use for it.
+            index_ty.name = Some(String::new());
+            res.push(TypeWithKind::from((index_ty, ItemType::Generic)));
+        } else if let Some(kind) = ty.def_id_no_primitives().map(|did| tcx.def_kind(did).into()) {
+            res.push(TypeWithKind::from((index_ty, kind)));
+        } else if ty.is_primitive() {
+            // This is a primitive, let's store it as such.
+            res.push(TypeWithKind::from((index_ty, ItemType::Primitive)));
+        }
+    }
+
+    if recurse >= 10 {
+        // FIXME: remove this whole recurse thing when the recursion bug is fixed
+        return;
+    }
+
+    // If this argument is a type parameter and not a trait bound or a type, we need to look
+    // for its bounds.
+    if let Type::Generic(arg_s) = *arg {
+        // First we check if the bounds are in a `where` predicate...
+        if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
+            WherePredicate::BoundPredicate { ty, .. } => {
+                ty.def_id_no_primitives() == arg.def_id_no_primitives()
+            }
+            _ => false,
+        }) {
+            let mut ty_generics = Vec::new();
+            let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
+            for bound in bounds.iter() {
+                if let GenericBound::TraitBound(poly_trait, _) = bound {
+                    for param_def in poly_trait.generic_params.iter() {
+                        match &param_def.kind {
+                            clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
+                                get_real_types(
+                                    generics,
+                                    ty,
+                                    tcx,
+                                    recurse + 1,
+                                    &mut ty_generics,
+                                    cache,
+                                )
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+            }
+            insert_ty(res, tcx, arg.clone(), ty_generics, cache);
+        }
+        // Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
+        if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
+            let mut ty_generics = Vec::new();
+            for bound in bound.get_bounds().unwrap_or(&[]) {
+                if let Some(path) = bound.get_trait_path() {
+                    let ty = Type::Path { path };
+                    get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics, cache);
+                }
+            }
+            insert_ty(res, tcx, arg.clone(), ty_generics, cache);
+        }
+    } else {
+        // This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
+        // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
+        //
+        // So in here, we can add it directly and look for its own type parameters (so for `Option`,
+        // we will look for them but not for `T`).
+        let mut ty_generics = Vec::new();
+        if let Some(arg_generics) = arg.generics() {
+            for gen in arg_generics.iter() {
+                get_real_types(generics, gen, tcx, recurse + 1, &mut ty_generics, cache);
+            }
+        }
+        insert_ty(res, tcx, arg.clone(), ty_generics, cache);
+    }
+}
+
+/// Return the full list of types when bounds have been resolved.
+///
+/// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
+/// `[u32, Display, Option]`.
+crate fn get_all_types<'tcx>(
+    generics: &Generics,
+    decl: &FnDecl,
+    tcx: TyCtxt<'tcx>,
+    cache: &Cache,
+) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
+    let mut all_types = Vec::new();
+    for arg in decl.inputs.values.iter() {
+        if arg.type_.is_self_type() {
+            continue;
+        }
+        let mut args = Vec::new();
+        get_real_types(generics, &arg.type_, tcx, 0, &mut args, cache);
+        if !args.is_empty() {
+            all_types.extend(args);
+        } else {
+            if let Some(kind) = arg.type_.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
+            {
+                all_types.push(TypeWithKind::from((get_index_type(&arg.type_, vec![]), kind)));
+            }
+        }
+    }
+
+    let mut ret_types = Vec::new();
+    match decl.output {
+        FnRetTy::Return(ref return_type) => {
+            get_real_types(generics, return_type, tcx, 0, &mut ret_types, cache);
+            if ret_types.is_empty() {
+                if let Some(kind) =
+                    return_type.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
+                {
+                    ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind)));
+                }
+            }
+        }
+        _ => {}
+    };
+    (all_types, ret_types)
+}
index 0031e3915fa40d7bbcaedc3d452000da0a7f913f..01641bae3845a77657b2b9bfdc4edcedc60f9d72 100644 (file)
@@ -24,7 +24,7 @@
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::FormatRenderer;
-use crate::html::render::cache::ExternalLocation;
+use crate::html::render::search_index::ExternalLocation;
 use crate::json::conversions::{from_item_id, IntoWithTcx};
 
 #[derive(Clone)]