1 use std::collections::BTreeMap;
4 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5 use rustc_middle::ty::TyCtxt;
6 use rustc_span::symbol::{sym, Symbol};
9 use crate::clean::types::{
10 FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, TypeKind, WherePredicate,
12 use crate::clean::{self, AttributesExt};
13 use crate::formats::cache::Cache;
14 use crate::formats::item_type::ItemType;
15 use crate::html::markdown::short_markdown_summary;
16 use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
18 /// Indicates where an external crate can be found.
19 crate enum ExternalLocation {
20 /// Remote URL root of the external crate
22 /// This external crate can be found in the local doc/ folder
24 /// The external crate could not be found.
28 /// Attempts to find where an external crate is located, given that we're
29 /// rendering in to the specified source destination.
30 crate fn extern_location(
31 e: &clean::ExternalCrate,
32 extern_url: Option<&str>,
34 ) -> ExternalLocation {
35 use ExternalLocation::*;
36 // See if there's documentation generated into the local directory
37 let local_location = dst.join(&*e.name.as_str());
38 if local_location.is_dir() {
42 if let Some(url) = extern_url {
43 let mut url = url.to_string();
44 if !url.ends_with('/') {
50 // Failing that, see if there's an attribute specifying where to find this
54 .filter(|a| a.has_name(sym::html_root_url))
55 .filter_map(|a| a.value_str())
57 let mut url = url.to_string();
58 if !url.ends_with('/') {
64 .unwrap_or(Unknown) // Well, at least we tried.
67 /// Builds the search index from the collected metadata
68 crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>) -> String {
69 let mut defid_to_pathid = FxHashMap::default();
70 let mut crate_items = Vec::with_capacity(cache.search_index.len());
71 let mut crate_paths = vec![];
73 // Attach all orphan items to the type's definition if the type
74 // has since been learned.
75 for &(did, ref item) in &cache.orphan_impl_items {
76 if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
77 cache.search_index.push(IndexItem {
79 name: item.name.unwrap().to_string(),
80 path: fqp[..fqp.len() - 1].join("::"),
81 desc: item.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)),
84 search_type: get_index_search_type(&item, cache, tcx),
86 for alias in item.attrs.get_doc_aliases() {
89 .entry(alias.to_lowercase())
90 .or_insert(Vec::new())
91 .push(cache.search_index.len() - 1);
96 let Cache { ref mut search_index, ref paths, ref mut aliases, .. } = *cache;
98 // Reduce `DefId` in paths into smaller sequential numbers,
99 // and prune the paths that do not appear in the index.
100 let mut lastpath = String::new();
101 let mut lastpathid = 0usize;
103 for item in search_index {
104 item.parent_idx = item.parent.and_then(|defid| {
105 if defid_to_pathid.contains_key(&defid) {
106 defid_to_pathid.get(&defid).copied()
108 let pathid = lastpathid;
109 defid_to_pathid.insert(defid, pathid);
112 if let Some(&(ref fqp, short)) = paths.get(&defid) {
113 crate_paths.push((short, fqp.last().unwrap().clone()));
121 // Omit the parent path if it is same to that of the prior item.
122 if lastpath == item.path {
125 lastpath = item.path.clone();
127 crate_items.push(&*item);
130 let crate_doc = krate
133 .map(|module| module.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)))
134 .unwrap_or_default();
137 struct CrateData<'a> {
139 #[serde(rename = "i")]
140 items: Vec<&'a IndexItem>,
141 #[serde(rename = "p")]
142 paths: Vec<(ItemType, String)>,
143 // The String is alias name and the vec is the list of the elements with this alias.
145 // To be noted: the `usize` elements are indexes to `items`.
146 #[serde(rename = "a")]
147 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
148 aliases: &'a BTreeMap<String, Vec<usize>>,
151 // Collect the index into a string
155 serde_json::to_string(&CrateData {
161 .expect("failed serde conversion")
162 // All these `replace` calls are because we have to go through JS string for JSON content.
163 .replace(r"\", r"\\")
165 // We need to escape double quotes for the JSON.
166 .replace("\\\"", "\\\\\"")
170 crate fn get_index_search_type<'tcx>(
174 ) -> Option<IndexItemFunctionType> {
175 let (all_types, ret_types) = match *item.kind {
176 clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx, &cache),
177 clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx, &cache),
178 clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx, &cache),
182 let inputs = all_types
184 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty, &cache), *kind)))
185 .filter(|a| a.ty.name.is_some())
187 let output = ret_types
189 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty, &cache), *kind)))
190 .filter(|a| a.ty.name.is_some())
191 .collect::<Vec<_>>();
192 let output = if output.is_empty() { None } else { Some(output) };
194 Some(IndexItemFunctionType { inputs, output })
197 fn get_index_type(clean_type: &clean::Type, cache: &Cache) -> RenderType {
199 ty: clean_type.def_id_full(cache),
201 name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
202 generics: get_generics(clean_type, cache),
206 fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<Symbol> {
208 clean::ResolvedPath { ref path, .. } => {
209 let segments = &path.segments;
210 let path_segment = segments.iter().last().unwrap_or_else(|| {
212 "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
213 clean_type, accept_generic
216 Some(path_segment.name)
218 clean::Generic(s) if accept_generic => Some(s),
219 clean::Primitive(ref p) => Some(p.as_sym()),
220 clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
222 | clean::BareFunction(_)
227 | clean::RawPointer(_, _)
228 | clean::QPath { .. }
230 | clean::ImplTrait(_) => None,
234 fn get_generics(clean_type: &clean::Type, cache: &Cache) -> Option<Vec<Generic>> {
235 clean_type.generics().and_then(|types| {
239 get_index_type_name(t, false).map(|name| Generic {
240 name: name.as_str().to_ascii_lowercase(),
241 defid: t.def_id_full(cache),
245 .collect::<Vec<_>>();
246 if r.is_empty() { None } else { Some(r) }
250 /// The point of this function is to replace bounds with types.
252 /// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
253 /// `[Display, Option]` (we just returns the list of the types, we don't care about the
254 /// wrapped types in here).
255 crate fn get_real_types<'tcx>(
261 res: &mut FxHashSet<(Type, TypeKind)>,
263 fn insert(res: &mut FxHashSet<(Type, TypeKind)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
264 if let Some(kind) = ty.def_id().map(|did| tcx.def_kind(did).into()) {
265 res.insert((ty, kind));
267 } else if ty.is_primitive() {
268 // This is a primitive, let's store it as such.
269 res.insert((ty, TypeKind::Primitive));
277 // FIXME: remove this whole recurse thing when the recursion bug is fixed
280 let mut nb_added = 0;
282 if arg.is_full_generic() {
283 let arg_s = Symbol::intern(&arg.print(cache).to_string());
284 if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
285 WherePredicate::BoundPredicate { ty, .. } => ty.def_id() == arg.def_id(),
288 let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
289 for bound in bounds.iter() {
290 if let GenericBound::TraitBound(poly_trait, _) = bound {
291 for x in poly_trait.generic_params.iter() {
295 if let Some(ty) = x.get_type() {
296 let adds = get_real_types(generics, &ty, tcx, recurse + 1, cache, res);
298 if adds == 0 && !ty.is_full_generic() {
299 nb_added += insert(res, tcx, ty);
306 if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
307 for bound in bound.get_bounds().unwrap_or(&[]) {
308 if let Some(ty) = bound.get_trait_type() {
309 let adds = get_real_types(generics, &ty, tcx, recurse + 1, cache, res);
311 if adds == 0 && !ty.is_full_generic() {
312 nb_added += insert(res, tcx, ty);
318 nb_added += insert(res, tcx, arg.clone());
319 if let Some(gens) = arg.generics() {
320 for gen in gens.iter() {
321 if gen.is_full_generic() {
322 nb_added += get_real_types(generics, gen, tcx, recurse + 1, cache, res);
324 nb_added += insert(res, tcx, (*gen).clone());
332 /// Return the full list of types when bounds have been resolved.
334 /// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
335 /// `[u32, Display, Option]`.
336 crate fn get_all_types<'tcx>(
341 ) -> (Vec<(Type, TypeKind)>, Vec<(Type, TypeKind)>) {
342 let mut all_types = FxHashSet::default();
343 for arg in decl.inputs.values.iter() {
344 if arg.type_.is_self_type() {
347 let mut args = FxHashSet::default();
348 get_real_types(generics, &arg.type_, tcx, 0, cache, &mut args);
349 if !args.is_empty() {
350 all_types.extend(args);
352 if let Some(kind) = arg.type_.def_id().map(|did| tcx.def_kind(did).into()) {
353 all_types.insert((arg.type_.clone(), kind));
358 let ret_types = match decl.output {
359 FnRetTy::Return(ref return_type) => {
360 let mut ret = FxHashSet::default();
361 get_real_types(generics, &return_type, tcx, 0, cache, &mut ret);
363 if let Some(kind) = return_type.def_id().map(|did| tcx.def_kind(did).into()) {
364 ret.insert((return_type.clone(), kind));
367 ret.into_iter().collect()
371 (all_types.into_iter().collect(), ret_types)