• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::{borrow::Cow, rc::Rc};
2 
3 use askama::Template;
4 use rustc_data_structures::fx::FxHashSet;
5 use rustc_hir::{def::CtorKind, def_id::DefIdSet};
6 use rustc_middle::ty::{self, TyCtxt};
7 
8 use crate::{
9     clean,
10     formats::{item_type::ItemType, Impl},
11     html::{format::Buffer, markdown::IdMap},
12 };
13 
14 use super::{item_ty_to_section, Context, ItemSection};
15 
16 #[derive(Template)]
17 #[template(path = "sidebar.html")]
18 pub(super) struct Sidebar<'a> {
19     pub(super) title_prefix: &'static str,
20     pub(super) title: &'a str,
21     pub(super) is_crate: bool,
22     pub(super) version: &'a str,
23     pub(super) blocks: Vec<LinkBlock<'a>>,
24     pub(super) path: String,
25 }
26 
27 impl<'a> Sidebar<'a> {
28     /// Only create a `<section>` if there are any blocks
29     /// which should actually be rendered.
should_render_blocks(&self) -> bool30     pub fn should_render_blocks(&self) -> bool {
31         self.blocks.iter().any(LinkBlock::should_render)
32     }
33 }
34 
35 /// A sidebar section such as 'Methods'.
36 pub(crate) struct LinkBlock<'a> {
37     /// The name of this section, e.g. 'Methods'
38     /// as well as the link to it, e.g. `#implementations`.
39     /// Will be rendered inside an `<h3>` tag
40     heading: Link<'a>,
41     links: Vec<Link<'a>>,
42     /// Render the heading even if there are no links
43     force_render: bool,
44 }
45 
46 impl<'a> LinkBlock<'a> {
new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self47     pub fn new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self {
48         Self { heading, links, force_render: false }
49     }
50 
forced(heading: Link<'a>) -> Self51     pub fn forced(heading: Link<'a>) -> Self {
52         Self { heading, links: vec![], force_render: true }
53     }
54 
should_render(&self) -> bool55     pub fn should_render(&self) -> bool {
56         self.force_render || !self.links.is_empty()
57     }
58 }
59 
60 /// A link to an item. Content should not be escaped.
61 #[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
62 pub(crate) struct Link<'a> {
63     /// The content for the anchor tag
64     name: Cow<'a, str>,
65     /// The id of an anchor within the page (without a `#` prefix)
66     href: Cow<'a, str>,
67 }
68 
69 impl<'a> Link<'a> {
new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self70     pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
71         Self { href: href.into(), name: name.into() }
72     }
empty() -> Link<'static>73     pub fn empty() -> Link<'static> {
74         Link::new("", "")
75     }
76 }
77 
print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer)78 pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
79     let blocks: Vec<LinkBlock<'_>> = match *it.kind {
80         clean::StructItem(ref s) => sidebar_struct(cx, it, s),
81         clean::TraitItem(ref t) => sidebar_trait(cx, it, t),
82         clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
83         clean::UnionItem(ref u) => sidebar_union(cx, it, u),
84         clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
85         clean::TypedefItem(_) => sidebar_typedef(cx, it),
86         clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
87         clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
88         _ => vec![],
89     };
90     // The sidebar is designed to display sibling functions, modules and
91     // other miscellaneous information. since there are lots of sibling
92     // items (and that causes quadratic growth in large modules),
93     // we refactor common parts into a shared JavaScript file per module.
94     // still, we don't move everything into JS because we want to preserve
95     // as much HTML as possible in order to allow non-JS-enabled browsers
96     // to navigate the documentation (though slightly inefficiently).
97     let (title_prefix, title) = if it.is_struct()
98         || it.is_trait()
99         || it.is_primitive()
100         || it.is_union()
101         || it.is_enum()
102         || it.is_mod()
103         || it.is_typedef()
104     {
105         (
106             match *it.kind {
107                 clean::ModuleItem(..) if it.is_crate() => "Crate ",
108                 clean::ModuleItem(..) => "Module ",
109                 _ => "",
110             },
111             it.name.as_ref().unwrap().as_str(),
112         )
113     } else {
114         ("", "")
115     };
116     let version =
117         if it.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() } else { "" };
118     let path: String = if !it.is_mod() {
119         cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
120     } else {
121         "".into()
122     };
123     let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
124     sidebar.render_into(buffer).unwrap();
125 }
126 
get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>>127 fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
128     let mut fields = fields
129         .iter()
130         .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
131         .filter_map(|f| {
132             f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
133         })
134         .collect::<Vec<Link<'a>>>();
135     fields.sort();
136     fields
137 }
138 
sidebar_struct<'a>( cx: &'a Context<'_>, it: &'a clean::Item, s: &'a clean::Struct, ) -> Vec<LinkBlock<'a>>139 fn sidebar_struct<'a>(
140     cx: &'a Context<'_>,
141     it: &'a clean::Item,
142     s: &'a clean::Struct,
143 ) -> Vec<LinkBlock<'a>> {
144     let fields = get_struct_fields_name(&s.fields);
145     let field_name = match s.ctor_kind {
146         Some(CtorKind::Fn) => Some("Tuple Fields"),
147         None => Some("Fields"),
148         _ => None,
149     };
150     let mut items = vec![];
151     if let Some(name) = field_name {
152         items.push(LinkBlock::new(Link::new("fields", name), fields));
153     }
154     sidebar_assoc_items(cx, it, &mut items);
155     items
156 }
157 
sidebar_trait<'a>( cx: &'a Context<'_>, it: &'a clean::Item, t: &'a clean::Trait, ) -> Vec<LinkBlock<'a>>158 fn sidebar_trait<'a>(
159     cx: &'a Context<'_>,
160     it: &'a clean::Item,
161     t: &'a clean::Trait,
162 ) -> Vec<LinkBlock<'a>> {
163     fn filter_items<'a>(
164         items: &'a [clean::Item],
165         filt: impl Fn(&clean::Item) -> bool,
166         ty: &str,
167     ) -> Vec<Link<'a>> {
168         let mut res = items
169             .iter()
170             .filter_map(|m: &clean::Item| match m.name {
171                 Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
172                 _ => None,
173             })
174             .collect::<Vec<Link<'a>>>();
175         res.sort();
176         res
177     }
178 
179     let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype");
180     let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
181     let req_assoc_const =
182         filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant");
183     let prov_assoc_const =
184         filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
185     let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
186     let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
187     let mut foreign_impls = vec![];
188     if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
189         foreign_impls.extend(
190             implementors
191                 .iter()
192                 .filter(|i| !i.is_on_local_type(cx))
193                 .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
194                 .map(|(name, id)| Link::new(id, name)),
195         );
196         foreign_impls.sort();
197     }
198 
199     let mut blocks: Vec<LinkBlock<'_>> = [
200         ("required-associated-types", "Required Associated Types", req_assoc),
201         ("provided-associated-types", "Provided Associated Types", prov_assoc),
202         ("required-associated-consts", "Required Associated Constants", req_assoc_const),
203         ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
204         ("required-methods", "Required Methods", req_method),
205         ("provided-methods", "Provided Methods", prov_method),
206         ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
207     ]
208     .into_iter()
209     .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
210     .collect();
211     sidebar_assoc_items(cx, it, &mut blocks);
212     blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
213     if t.is_auto(cx.tcx()) {
214         blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
215     }
216     blocks
217 }
218 
sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>>219 fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
220     if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
221         let mut items = vec![];
222         sidebar_assoc_items(cx, it, &mut items);
223         items
224     } else {
225         let shared = Rc::clone(&cx.shared);
226         let (concrete, synthetic, blanket_impl) =
227             super::get_filtered_impls_for_reference(&shared, it);
228 
229         sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into()
230     }
231 }
232 
sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>>233 fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
234     let mut items = vec![];
235     sidebar_assoc_items(cx, it, &mut items);
236     items
237 }
238 
sidebar_union<'a>( cx: &'a Context<'_>, it: &'a clean::Item, u: &'a clean::Union, ) -> Vec<LinkBlock<'a>>239 fn sidebar_union<'a>(
240     cx: &'a Context<'_>,
241     it: &'a clean::Item,
242     u: &'a clean::Union,
243 ) -> Vec<LinkBlock<'a>> {
244     let fields = get_struct_fields_name(&u.fields);
245     let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
246     sidebar_assoc_items(cx, it, &mut items);
247     items
248 }
249 
250 /// Adds trait implementations into the blocks of links
sidebar_assoc_items<'a>( cx: &'a Context<'_>, it: &'a clean::Item, links: &mut Vec<LinkBlock<'a>>, )251 fn sidebar_assoc_items<'a>(
252     cx: &'a Context<'_>,
253     it: &'a clean::Item,
254     links: &mut Vec<LinkBlock<'a>>,
255 ) {
256     let did = it.item_id.expect_def_id();
257     let cache = cx.cache();
258 
259     let mut assoc_consts = Vec::new();
260     let mut methods = Vec::new();
261     if let Some(v) = cache.impls.get(&did) {
262         let mut used_links = FxHashSet::default();
263         let mut id_map = IdMap::new();
264 
265         {
266             let used_links_bor = &mut used_links;
267             assoc_consts.extend(
268                 v.iter()
269                     .filter(|i| i.inner_impl().trait_.is_none())
270                     .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)),
271             );
272             // We want links' order to be reproducible so we don't use unstable sort.
273             assoc_consts.sort();
274 
275             #[rustfmt::skip] // rustfmt makes the pipeline less readable
276             methods.extend(
277                 v.iter()
278                     .filter(|i| i.inner_impl().trait_.is_none())
279                     .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())),
280             );
281 
282             // We want links' order to be reproducible so we don't use unstable sort.
283             methods.sort();
284         }
285 
286         let mut deref_methods = Vec::new();
287         let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
288             if let Some(impl_) =
289                 v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
290             {
291                 let mut derefs = DefIdSet::default();
292                 derefs.insert(did);
293                 sidebar_deref_methods(
294                     cx,
295                     &mut deref_methods,
296                     impl_,
297                     v,
298                     &mut derefs,
299                     &mut used_links,
300                 );
301             }
302 
303             let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
304                 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
305             let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
306                 concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
307 
308             sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
309         } else {
310             std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
311         };
312 
313         let mut blocks = vec![
314             LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
315             LinkBlock::new(Link::new("implementations", "Methods"), methods),
316         ];
317         blocks.append(&mut deref_methods);
318         blocks.extend([concrete, synthetic, blanket]);
319         links.append(&mut blocks);
320     }
321 }
322 
sidebar_deref_methods<'a>( cx: &'a Context<'_>, out: &mut Vec<LinkBlock<'a>>, impl_: &Impl, v: &[Impl], derefs: &mut DefIdSet, used_links: &mut FxHashSet<String>, )323 fn sidebar_deref_methods<'a>(
324     cx: &'a Context<'_>,
325     out: &mut Vec<LinkBlock<'a>>,
326     impl_: &Impl,
327     v: &[Impl],
328     derefs: &mut DefIdSet,
329     used_links: &mut FxHashSet<String>,
330 ) {
331     let c = cx.cache();
332 
333     debug!("found Deref: {:?}", impl_);
334     if let Some((target, real_target)) =
335         impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
336             clean::AssocTypeItem(box ref t, _) => Some(match *t {
337                 clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
338                 _ => (&t.type_, &t.type_),
339             }),
340             _ => None,
341         })
342     {
343         debug!("found target, real_target: {:?} {:?}", target, real_target);
344         if let Some(did) = target.def_id(c) &&
345             let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
346             // `impl Deref<Target = S> for S`
347             (did == type_did || !derefs.insert(did))
348         {
349             // Avoid infinite cycles
350             return;
351         }
352         let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
353         let inner_impl = target
354             .def_id(c)
355             .or_else(|| {
356                 target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
357             })
358             .and_then(|did| c.impls.get(&did));
359         if let Some(impls) = inner_impl {
360             debug!("found inner_impl: {:?}", impls);
361             let mut ret = impls
362                 .iter()
363                 .filter(|i| i.inner_impl().trait_.is_none())
364                 .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
365                 .collect::<Vec<_>>();
366             if !ret.is_empty() {
367                 let id = if let Some(target_def_id) = real_target.def_id(c) {
368                     Cow::Borrowed(
369                         cx.deref_id_map
370                             .get(&target_def_id)
371                             .expect("Deref section without derived id")
372                             .as_str(),
373                     )
374                 } else {
375                     Cow::Borrowed("deref-methods")
376                 };
377                 let title = format!(
378                     "Methods from {:#}<Target={:#}>",
379                     impl_.inner_impl().trait_.as_ref().unwrap().print(cx),
380                     real_target.print(cx),
381                 );
382                 // We want links' order to be reproducible so we don't use unstable sort.
383                 ret.sort();
384                 out.push(LinkBlock::new(Link::new(id, title), ret));
385             }
386         }
387 
388         // Recurse into any further impls that might exist for `target`
389         if let Some(target_did) = target.def_id(c) &&
390             let Some(target_impls) = c.impls.get(&target_did) &&
391             let Some(target_deref_impl) = target_impls.iter().find(|i| {
392                 i.inner_impl()
393                     .trait_
394                     .as_ref()
395                     .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
396                     .unwrap_or(false)
397             })
398         {
399             sidebar_deref_methods(
400                 cx,
401                 out,
402                 target_deref_impl,
403                 target_impls,
404                 derefs,
405                 used_links,
406             );
407         }
408     }
409 }
410 
sidebar_enum<'a>( cx: &'a Context<'_>, it: &'a clean::Item, e: &'a clean::Enum, ) -> Vec<LinkBlock<'a>>411 fn sidebar_enum<'a>(
412     cx: &'a Context<'_>,
413     it: &'a clean::Item,
414     e: &'a clean::Enum,
415 ) -> Vec<LinkBlock<'a>> {
416     let mut variants = e
417         .variants()
418         .filter_map(|v| v.name)
419         .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
420         .collect::<Vec<_>>();
421     variants.sort_unstable();
422 
423     let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
424     sidebar_assoc_items(cx, it, &mut items);
425     items
426 }
427 
sidebar_module_like( item_sections_in_use: FxHashSet<ItemSection>, ) -> LinkBlock<'static>428 pub(crate) fn sidebar_module_like(
429     item_sections_in_use: FxHashSet<ItemSection>,
430 ) -> LinkBlock<'static> {
431     let item_sections = ItemSection::ALL
432         .iter()
433         .copied()
434         .filter(|sec| item_sections_in_use.contains(sec))
435         .map(|sec| Link::new(sec.id(), sec.name()))
436         .collect();
437     LinkBlock::new(Link::empty(), item_sections)
438 }
439 
sidebar_module(items: &[clean::Item]) -> LinkBlock<'static>440 fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
441     let item_sections_in_use: FxHashSet<_> = items
442         .iter()
443         .filter(|it| {
444             !it.is_stripped()
445                 && it
446                     .name
447                     .or_else(|| {
448                         if let clean::ImportItem(ref i) = *it.kind &&
449                             let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
450                     })
451                     .is_some()
452         })
453         .map(|it| item_ty_to_section(it.type_()))
454         .collect();
455 
456     sidebar_module_like(item_sections_in_use)
457 }
458 
sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>>459 fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
460     let mut items = vec![];
461     sidebar_assoc_items(cx, it, &mut items);
462     items
463 }
464 
465 /// Renders the trait implementations for this type
sidebar_render_assoc_items( cx: &Context<'_>, id_map: &mut IdMap, concrete: Vec<&Impl>, synthetic: Vec<&Impl>, blanket_impl: Vec<&Impl>, ) -> [LinkBlock<'static>; 3]466 fn sidebar_render_assoc_items(
467     cx: &Context<'_>,
468     id_map: &mut IdMap,
469     concrete: Vec<&Impl>,
470     synthetic: Vec<&Impl>,
471     blanket_impl: Vec<&Impl>,
472 ) -> [LinkBlock<'static>; 3] {
473     let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
474         let mut links = FxHashSet::default();
475 
476         let mut ret = impls
477             .iter()
478             .filter_map(|it| {
479                 let trait_ = it.inner_impl().trait_.as_ref()?;
480                 let encoded =
481                     id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
482 
483                 let prefix = match it.inner_impl().polarity {
484                     ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
485                     ty::ImplPolarity::Negative => "!",
486                 };
487                 let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx)));
488                 if links.insert(generated.clone()) { Some(generated) } else { None }
489             })
490             .collect::<Vec<Link<'static>>>();
491         ret.sort();
492         ret
493     };
494 
495     let concrete = format_impls(concrete, id_map);
496     let synthetic = format_impls(synthetic, id_map);
497     let blanket = format_impls(blanket_impl, id_map);
498     [
499         LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
500         LinkBlock::new(
501             Link::new("synthetic-implementations", "Auto Trait Implementations"),
502             synthetic,
503         ),
504         LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
505     ]
506 }
507 
get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String508 fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
509     if used_links.insert(url.clone()) {
510         return url;
511     }
512     let mut add = 1;
513     while !used_links.insert(format!("{}-{}", url, add)) {
514         add += 1;
515     }
516     format!("{}-{}", url, add)
517 }
518 
get_methods<'a>( i: &'a clean::Impl, for_deref: bool, used_links: &mut FxHashSet<String>, deref_mut: bool, tcx: TyCtxt<'_>, ) -> Vec<Link<'a>>519 fn get_methods<'a>(
520     i: &'a clean::Impl,
521     for_deref: bool,
522     used_links: &mut FxHashSet<String>,
523     deref_mut: bool,
524     tcx: TyCtxt<'_>,
525 ) -> Vec<Link<'a>> {
526     i.items
527         .iter()
528         .filter_map(|item| match item.name {
529             Some(ref name) if !name.is_empty() && item.is_method() => {
530                 if !for_deref || super::should_render_item(item, deref_mut, tcx) {
531                     Some(Link::new(
532                         get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
533                         name.as_str(),
534                     ))
535                 } else {
536                     None
537                 }
538             }
539             _ => None,
540         })
541         .collect::<Vec<_>>()
542 }
543 
get_associated_constants<'a>( i: &'a clean::Impl, used_links: &mut FxHashSet<String>, ) -> Vec<Link<'a>>544 fn get_associated_constants<'a>(
545     i: &'a clean::Impl,
546     used_links: &mut FxHashSet<String>,
547 ) -> Vec<Link<'a>> {
548     i.items
549         .iter()
550         .filter_map(|item| match item.name {
551             Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new(
552                 get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
553                 name.as_str(),
554             )),
555             _ => None,
556         })
557         .collect::<Vec<_>>()
558 }
559