]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/render/cache.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / src / librustdoc / html / render / cache.rs
CommitLineData
dfeec247 1use std::collections::BTreeMap;
3dfed10e 2use std::path::Path;
60c5eb7d 3
6a06907d
XL
4use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5use rustc_middle::ty::TyCtxt;
fc512014 6use rustc_span::symbol::{sym, Symbol};
6a06907d 7use serde::ser::{Serialize, SerializeStruct, Serializer};
e74abb32 8
6a06907d
XL
9use crate::clean::types::{
10 FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, TypeKind, WherePredicate,
11};
3dfed10e
XL
12use crate::clean::{self, AttributesExt};
13use crate::formats::cache::Cache;
14use crate::formats::item_type::ItemType;
fc512014 15use crate::html::markdown::short_markdown_summary;
3dfed10e 16use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
e74abb32
XL
17
18/// Indicates where an external crate can be found.
fc512014 19crate enum ExternalLocation {
e74abb32
XL
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
e74abb32
XL
28/// Attempts to find where an external crate is located, given that we're
29/// rendering in to the specified source destination.
fc512014 30crate fn extern_location(
dfeec247
XL
31 e: &clean::ExternalCrate,
32 extern_url: Option<&str>,
33 dst: &Path,
34) -> ExternalLocation {
e74abb32
XL
35 use ExternalLocation::*;
36 // See if there's documentation generated into the local directory
fc512014 37 let local_location = dst.join(&*e.name.as_str());
e74abb32
XL
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();
74b04a01 44 if !url.ends_with('/') {
e74abb32
XL
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
dfeec247
XL
52 e.attrs
53 .lists(sym::doc)
3dfed10e 54 .filter(|a| a.has_name(sym::html_root_url))
dfeec247
XL
55 .filter_map(|a| a.value_str())
56 .map(|url| {
57 let mut url = url.to_string();
74b04a01 58 if !url.ends_with('/') {
dfeec247
XL
59 url.push('/')
60 }
61 Remote(url)
62 })
63 .next()
64 .unwrap_or(Unknown) // Well, at least we tried.
e74abb32
XL
65}
66
67/// Builds the search index from the collected metadata
6a06907d 68crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>) -> String {
74b04a01 69 let mut defid_to_pathid = FxHashMap::default();
e74abb32 70 let mut crate_items = Vec::with_capacity(cache.search_index.len());
60c5eb7d 71 let mut crate_paths = vec![];
e74abb32 72
e74abb32
XL
73 // Attach all orphan items to the type's definition if the type
74 // has since been learned.
5869c6ff
XL
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 {
e74abb32 78 ty: item.type_(),
fc512014 79 name: item.name.unwrap().to_string(),
e74abb32 80 path: fqp[..fqp.len() - 1].join("::"),
5869c6ff 81 desc: item.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)),
e74abb32
XL
82 parent: Some(did),
83 parent_idx: None,
6a06907d 84 search_type: get_index_search_type(&item, cache, tcx),
e74abb32 85 });
f9f354fc 86 for alias in item.attrs.get_doc_aliases() {
5869c6ff
XL
87 cache
88 .aliases
f9f354fc
XL
89 .entry(alias.to_lowercase())
90 .or_insert(Vec::new())
5869c6ff 91 .push(cache.search_index.len() - 1);
f9f354fc 92 }
e74abb32
XL
93 }
94 }
95
5869c6ff
XL
96 let Cache { ref mut search_index, ref paths, ref mut aliases, .. } = *cache;
97
74b04a01 98 // Reduce `DefId` in paths into smaller sequential numbers,
e74abb32
XL
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 {
ba9703b0 104 item.parent_idx = item.parent.and_then(|defid| {
74b04a01 105 if defid_to_pathid.contains_key(&defid) {
ba9703b0 106 defid_to_pathid.get(&defid).copied()
e74abb32
XL
107 } else {
108 let pathid = lastpathid;
74b04a01 109 defid_to_pathid.insert(defid, pathid);
e74abb32
XL
110 lastpathid += 1;
111
ba9703b0
XL
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 }
e74abb32
XL
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 }
60c5eb7d 127 crate_items.push(&*item);
e74abb32
XL
128 }
129
dfeec247
XL
130 let crate_doc = krate
131 .module
132 .as_ref()
5869c6ff 133 .map(|module| module.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)))
29967ef6 134 .unwrap_or_default();
e74abb32 135
60c5eb7d
XL
136 struct CrateData<'a> {
137 doc: String,
60c5eb7d 138 items: Vec<&'a IndexItem>,
60c5eb7d 139 paths: Vec<(ItemType, String)>,
f9f354fc
XL
140 // The String is alias name and the vec is the list of the elements with this alias.
141 //
142 // To be noted: the `usize` elements are indexes to `items`.
f9f354fc 143 aliases: &'a BTreeMap<String, Vec<usize>>,
60c5eb7d 144 }
e74abb32 145
6a06907d
XL
146 impl<'a> Serialize for CrateData<'a> {
147 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
148 where
149 S: Serializer,
150 {
151 let has_aliases = !self.aliases.is_empty();
152 let mut crate_data =
153 serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
154 crate_data.serialize_field("doc", &self.doc)?;
155 crate_data.serialize_field(
156 "t",
157 &self.items.iter().map(|item| &item.ty).collect::<Vec<_>>(),
158 )?;
159 crate_data.serialize_field(
160 "n",
161 &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
162 )?;
163 crate_data.serialize_field(
164 "q",
165 &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
166 )?;
167 crate_data.serialize_field(
168 "d",
169 &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
170 )?;
171 crate_data.serialize_field(
172 "i",
173 &self
174 .items
175 .iter()
176 .map(|item| {
177 assert_eq!(
178 item.parent.is_some(),
179 item.parent_idx.is_some(),
180 "`{}` is missing idx",
181 item.name
182 );
183 item.parent_idx.map(|x| x + 1).unwrap_or(0)
184 })
185 .collect::<Vec<_>>(),
186 )?;
187 crate_data.serialize_field(
188 "f",
189 &self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(),
190 )?;
191 crate_data.serialize_field("p", &self.paths)?;
192 if has_aliases {
193 crate_data.serialize_field("a", &self.aliases)?;
194 }
195 crate_data.end()
196 }
197 }
198
e74abb32 199 // Collect the index into a string
60c5eb7d 200 format!(
ba9703b0 201 r#""{}":{}"#,
60c5eb7d
XL
202 krate.name,
203 serde_json::to_string(&CrateData {
204 doc: crate_doc,
205 items: crate_items,
206 paths: crate_paths,
f9f354fc 207 aliases,
60c5eb7d 208 })
dfeec247 209 .expect("failed serde conversion")
ba9703b0
XL
210 // All these `replace` calls are because we have to go through JS string for JSON content.
211 .replace(r"\", r"\\")
212 .replace("'", r"\'")
213 // We need to escape double quotes for the JSON.
214 .replace("\\\"", "\\\\\"")
60c5eb7d 215 )
e74abb32
XL
216}
217
6a06907d 218crate fn get_index_search_type<'tcx>(
5869c6ff 219 item: &clean::Item,
6a06907d
XL
220 cache: &Cache,
221 tcx: TyCtxt<'tcx>,
5869c6ff
XL
222) -> Option<IndexItemFunctionType> {
223 let (all_types, ret_types) = match *item.kind {
6a06907d
XL
224 clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx),
225 clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx),
226 clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx),
e74abb32
XL
227 _ => return None,
228 };
229
ba9703b0
XL
230 let inputs = all_types
231 .iter()
5869c6ff 232 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty, &cache), *kind)))
ba9703b0
XL
233 .filter(|a| a.ty.name.is_some())
234 .collect();
dfeec247
XL
235 let output = ret_types
236 .iter()
5869c6ff 237 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty, &cache), *kind)))
ba9703b0 238 .filter(|a| a.ty.name.is_some())
dfeec247
XL
239 .collect::<Vec<_>>();
240 let output = if output.is_empty() { None } else { Some(output) };
e74abb32
XL
241
242 Some(IndexItemFunctionType { inputs, output })
243}
244
6a06907d 245fn get_index_type(clean_type: &clean::Type, cache: &Cache) -> RenderType {
ba9703b0 246 RenderType {
6a06907d 247 ty: clean_type.def_id_full(cache),
ba9703b0 248 idx: None,
fc512014 249 name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
5869c6ff 250 generics: get_generics(clean_type, cache),
ba9703b0 251 }
e74abb32
XL
252}
253
fc512014 254fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<Symbol> {
e74abb32
XL
255 match *clean_type {
256 clean::ResolvedPath { ref path, .. } => {
257 let segments = &path.segments;
3dfed10e
XL
258 let path_segment = segments.iter().last().unwrap_or_else(|| {
259 panic!(
e74abb32
XL
260 "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
261 clean_type, accept_generic
3dfed10e
XL
262 )
263 });
fc512014 264 Some(path_segment.name)
e74abb32 265 }
fc512014
XL
266 clean::Generic(s) if accept_generic => Some(s),
267 clean::Primitive(ref p) => Some(p.as_sym()),
e74abb32 268 clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
5869c6ff
XL
269 clean::Generic(_)
270 | clean::BareFunction(_)
271 | clean::Tuple(_)
272 | clean::Slice(_)
273 | clean::Array(_, _)
274 | clean::Never
275 | clean::RawPointer(_, _)
276 | clean::QPath { .. }
277 | clean::Infer
278 | clean::ImplTrait(_) => None,
e74abb32
XL
279 }
280}
281
6a06907d 282fn get_generics(clean_type: &clean::Type, cache: &Cache) -> Option<Vec<Generic>> {
dfeec247
XL
283 clean_type.generics().and_then(|types| {
284 let r = types
285 .iter()
ba9703b0
XL
286 .filter_map(|t| {
287 get_index_type_name(t, false).map(|name| Generic {
fc512014 288 name: name.as_str().to_ascii_lowercase(),
6a06907d 289 defid: t.def_id_full(cache),
ba9703b0
XL
290 idx: None,
291 })
292 })
dfeec247
XL
293 .collect::<Vec<_>>();
294 if r.is_empty() { None } else { Some(r) }
295 })
e74abb32 296}
6a06907d
XL
297
298/// The point of this function is to replace bounds with types.
299///
300/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
301/// `[Display, Option]` (we just returns the list of the types, we don't care about the
302/// wrapped types in here).
303crate fn get_real_types<'tcx>(
304 generics: &Generics,
305 arg: &Type,
306 tcx: TyCtxt<'tcx>,
307 recurse: i32,
308 res: &mut FxHashSet<(Type, TypeKind)>,
309) -> usize {
310 fn insert(res: &mut FxHashSet<(Type, TypeKind)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
311 if let Some(kind) = ty.def_id().map(|did| tcx.def_kind(did).into()) {
312 res.insert((ty, kind));
313 1
314 } else if ty.is_primitive() {
315 // This is a primitive, let's store it as such.
316 res.insert((ty, TypeKind::Primitive));
317 1
318 } else {
319 0
320 }
321 }
322
323 if recurse >= 10 {
324 // FIXME: remove this whole recurse thing when the recursion bug is fixed
325 return 0;
326 }
327 let mut nb_added = 0;
328
329 if let &Type::Generic(arg_s) = arg {
330 if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
331 WherePredicate::BoundPredicate { ty, .. } => ty.def_id() == arg.def_id(),
332 _ => false,
333 }) {
334 let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
335 for bound in bounds.iter() {
336 if let GenericBound::TraitBound(poly_trait, _) = bound {
337 for x in poly_trait.generic_params.iter() {
338 if !x.is_type() {
339 continue;
340 }
341 if let Some(ty) = x.get_type() {
342 let adds = get_real_types(generics, &ty, tcx, recurse + 1, res);
343 nb_added += adds;
344 if adds == 0 && !ty.is_full_generic() {
345 nb_added += insert(res, tcx, ty);
346 }
347 }
348 }
349 }
350 }
351 }
352 if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
353 for bound in bound.get_bounds().unwrap_or(&[]) {
354 if let Some(ty) = bound.get_trait_type() {
355 let adds = get_real_types(generics, &ty, tcx, recurse + 1, res);
356 nb_added += adds;
357 if adds == 0 && !ty.is_full_generic() {
358 nb_added += insert(res, tcx, ty);
359 }
360 }
361 }
362 }
363 } else {
364 nb_added += insert(res, tcx, arg.clone());
365 if let Some(gens) = arg.generics() {
366 for gen in gens.iter() {
367 if gen.is_full_generic() {
368 nb_added += get_real_types(generics, gen, tcx, recurse + 1, res);
369 } else {
370 nb_added += insert(res, tcx, (*gen).clone());
371 }
372 }
373 }
374 }
375 nb_added
376}
377
378/// Return the full list of types when bounds have been resolved.
379///
380/// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
381/// `[u32, Display, Option]`.
382crate fn get_all_types<'tcx>(
383 generics: &Generics,
384 decl: &FnDecl,
385 tcx: TyCtxt<'tcx>,
386) -> (Vec<(Type, TypeKind)>, Vec<(Type, TypeKind)>) {
387 let mut all_types = FxHashSet::default();
388 for arg in decl.inputs.values.iter() {
389 if arg.type_.is_self_type() {
390 continue;
391 }
392 let mut args = FxHashSet::default();
393 get_real_types(generics, &arg.type_, tcx, 0, &mut args);
394 if !args.is_empty() {
395 all_types.extend(args);
396 } else {
397 if let Some(kind) = arg.type_.def_id().map(|did| tcx.def_kind(did).into()) {
398 all_types.insert((arg.type_.clone(), kind));
399 }
400 }
401 }
402
403 let ret_types = match decl.output {
404 FnRetTy::Return(ref return_type) => {
405 let mut ret = FxHashSet::default();
406 get_real_types(generics, &return_type, tcx, 0, &mut ret);
407 if ret.is_empty() {
408 if let Some(kind) = return_type.def_id().map(|did| tcx.def_kind(did).into()) {
409 ret.insert((return_type.clone(), kind));
410 }
411 }
412 ret.into_iter().collect()
413 }
414 _ => Vec::new(),
415 };
416 (all_types.into_iter().collect(), ret_types)
417}