]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/render/cache.rs
97694150c6c711c8142f1bb779dc30bc5299c247
[rust.git] / src / librustdoc / html / render / cache.rs
1 use std::collections::BTreeMap;
2 use std::path::Path;
3
4 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5 use rustc_middle::ty::TyCtxt;
6 use rustc_span::symbol::{sym, Symbol};
7 use serde::Serialize;
8
9 use crate::clean::types::{
10     FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, TypeKind, WherePredicate,
11 };
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};
17
18 /// Indicates where an external crate can be found.
19 crate enum ExternalLocation {
20     /// Remote URL root of the external crate
21     Remote(String),
22     /// This external crate can be found in the local doc/ folder
23     Local,
24     /// The external crate could not be found.
25     Unknown,
26 }
27
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>,
33     dst: &Path,
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() {
39         return Local;
40     }
41
42     if let Some(url) = extern_url {
43         let mut url = url.to_string();
44         if !url.ends_with('/') {
45             url.push('/');
46         }
47         return Remote(url);
48     }
49
50     // Failing that, see if there's an attribute specifying where to find this
51     // external crate
52     e.attrs
53         .lists(sym::doc)
54         .filter(|a| a.has_name(sym::html_root_url))
55         .filter_map(|a| a.value_str())
56         .map(|url| {
57             let mut url = url.to_string();
58             if !url.ends_with('/') {
59                 url.push('/')
60             }
61             Remote(url)
62         })
63         .next()
64         .unwrap_or(Unknown) // Well, at least we tried.
65 }
66
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![];
72
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 {
78                 ty: item.type_(),
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)),
82                 parent: Some(did),
83                 parent_idx: None,
84                 search_type: get_index_search_type(&item, cache, tcx),
85             });
86             for alias in item.attrs.get_doc_aliases() {
87                 cache
88                     .aliases
89                     .entry(alias.to_lowercase())
90                     .or_insert(Vec::new())
91                     .push(cache.search_index.len() - 1);
92             }
93         }
94     }
95
96     let Cache { ref mut search_index, ref paths, ref mut aliases, .. } = *cache;
97
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;
102
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()
107             } else {
108                 let pathid = lastpathid;
109                 defid_to_pathid.insert(defid, pathid);
110                 lastpathid += 1;
111
112                 if let Some(&(ref fqp, short)) = paths.get(&defid) {
113                     crate_paths.push((short, fqp.last().unwrap().clone()));
114                     Some(pathid)
115                 } else {
116                     None
117                 }
118             }
119         });
120
121         // Omit the parent path if it is same to that of the prior item.
122         if lastpath == item.path {
123             item.path.clear();
124         } else {
125             lastpath = item.path.clone();
126         }
127         crate_items.push(&*item);
128     }
129
130     let crate_doc = krate
131         .module
132         .as_ref()
133         .map(|module| module.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)))
134         .unwrap_or_default();
135
136     #[derive(Serialize)]
137     struct CrateData<'a> {
138         doc: String,
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.
144         //
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>>,
149     }
150
151     // Collect the index into a string
152     format!(
153         r#""{}":{}"#,
154         krate.name,
155         serde_json::to_string(&CrateData {
156             doc: crate_doc,
157             items: crate_items,
158             paths: crate_paths,
159             aliases,
160         })
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"\\")
164         .replace("'", r"\'")
165         // We need to escape double quotes for the JSON.
166         .replace("\\\"", "\\\\\"")
167     )
168 }
169
170 crate fn get_index_search_type<'tcx>(
171     item: &clean::Item,
172     cache: &Cache,
173     tcx: TyCtxt<'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),
179         _ => return None,
180     };
181
182     let inputs = all_types
183         .iter()
184         .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty, &cache), *kind)))
185         .filter(|a| a.ty.name.is_some())
186         .collect();
187     let output = ret_types
188         .iter()
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) };
193
194     Some(IndexItemFunctionType { inputs, output })
195 }
196
197 fn get_index_type(clean_type: &clean::Type, cache: &Cache) -> RenderType {
198     RenderType {
199         ty: clean_type.def_id_full(cache),
200         idx: None,
201         name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
202         generics: get_generics(clean_type, cache),
203     }
204 }
205
206 fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<Symbol> {
207     match *clean_type {
208         clean::ResolvedPath { ref path, .. } => {
209             let segments = &path.segments;
210             let path_segment = segments.iter().last().unwrap_or_else(|| {
211                 panic!(
212                 "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
213                 clean_type, accept_generic
214             )
215             });
216             Some(path_segment.name)
217         }
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),
221         clean::Generic(_)
222         | clean::BareFunction(_)
223         | clean::Tuple(_)
224         | clean::Slice(_)
225         | clean::Array(_, _)
226         | clean::Never
227         | clean::RawPointer(_, _)
228         | clean::QPath { .. }
229         | clean::Infer
230         | clean::ImplTrait(_) => None,
231     }
232 }
233
234 fn get_generics(clean_type: &clean::Type, cache: &Cache) -> Option<Vec<Generic>> {
235     clean_type.generics().and_then(|types| {
236         let r = types
237             .iter()
238             .filter_map(|t| {
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),
242                     idx: None,
243                 })
244             })
245             .collect::<Vec<_>>();
246         if r.is_empty() { None } else { Some(r) }
247     })
248 }
249
250 /// The point of this function is to replace bounds with types.
251 ///
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>(
256     generics: &Generics,
257     arg: &Type,
258     tcx: TyCtxt<'tcx>,
259     recurse: i32,
260     cache: &Cache,
261     res: &mut FxHashSet<(Type, TypeKind)>,
262 ) -> usize {
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));
266             1
267         } else if ty.is_primitive() {
268             // This is a primitive, let's store it as such.
269             res.insert((ty, TypeKind::Primitive));
270             1
271         } else {
272             0
273         }
274     }
275
276     if recurse >= 10 {
277         // FIXME: remove this whole recurse thing when the recursion bug is fixed
278         return 0;
279     }
280     let mut nb_added = 0;
281
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(),
286             _ => false,
287         }) {
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() {
292                         if !x.is_type() {
293                             continue;
294                         }
295                         if let Some(ty) = x.get_type() {
296                             let adds = get_real_types(generics, &ty, tcx, recurse + 1, cache, res);
297                             nb_added += adds;
298                             if adds == 0 && !ty.is_full_generic() {
299                                 nb_added += insert(res, tcx, ty);
300                             }
301                         }
302                     }
303                 }
304             }
305         }
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);
310                     nb_added += adds;
311                     if adds == 0 && !ty.is_full_generic() {
312                         nb_added += insert(res, tcx, ty);
313                     }
314                 }
315             }
316         }
317     } else {
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);
323                 } else {
324                     nb_added += insert(res, tcx, (*gen).clone());
325                 }
326             }
327         }
328     }
329     nb_added
330 }
331
332 /// Return the full list of types when bounds have been resolved.
333 ///
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>(
337     generics: &Generics,
338     decl: &FnDecl,
339     tcx: TyCtxt<'tcx>,
340     cache: &Cache,
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() {
345             continue;
346         }
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);
351         } else {
352             if let Some(kind) = arg.type_.def_id().map(|did| tcx.def_kind(did).into()) {
353                 all_types.insert((arg.type_.clone(), kind));
354             }
355         }
356     }
357
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);
362             if ret.is_empty() {
363                 if let Some(kind) = return_type.def_id().map(|did| tcx.def_kind(did).into()) {
364                     ret.insert((return_type.clone(), kind));
365                 }
366             }
367             ret.into_iter().collect()
368         }
369         _ => Vec::new(),
370     };
371     (all_types.into_iter().collect(), ret_types)
372 }