1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt;
4
5use askama::Template;
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_hir::def::CtorKind;
8use rustc_hir::def_id::{DefIdMap, DefIdSet};
9use tracing::debug;
10
11use super::{Context, ItemSection, impl_trait_key, item_ty_to_section};
12use crate::clean;
13use crate::formats::Impl;
14use crate::formats::item_type::ItemType;
15use crate::html::format::{print_path, print_type};
16use crate::html::markdown::{IdMap, MarkdownWithToc};
17use crate::html::render::print_item::compare_names;
18
19#[derive(Clone, Copy)]
20pub(crate) enum ModuleLike {
21 Module,
22 Crate,
23}
24
25impl ModuleLike {
26 pub(crate) fn is_crate(self) -> bool {
27 matches!(self, ModuleLike::Crate)
28 }
29}
30impl<'a> From<&'a clean::Item> for ModuleLike {
31 fn from(it: &'a clean::Item) -> ModuleLike {
32 if it.is_crate() { ModuleLike::Crate } else { ModuleLike::Module }
33 }
34}
35
36#[derive(Template)]
37#[template(path = "sidebar.html")]
38pub(super) struct Sidebar<'a> {
39 pub(super) title_prefix: &'static str,
40 pub(super) title: &'a str,
41 pub(super) is_crate: bool,
42 pub(super) parent_is_crate: bool,
43 pub(super) is_mod: bool,
44 pub(super) blocks: Vec<LinkBlock<'a>>,
45 pub(super) path: String,
46}
47
48impl Sidebar<'_> {
49 pub fn should_render_blocks(&self) -> bool {
52 self.blocks.iter().any(LinkBlock::should_render)
53 }
54}
55
56pub(crate) struct LinkBlock<'a> {
58 heading: Link<'a>,
62 class: &'static str,
63 links: Vec<Link<'a>>,
64 force_render: bool,
66}
67
68impl<'a> LinkBlock<'a> {
69 pub fn new(heading: Link<'a>, class: &'static str, links: Vec<Link<'a>>) -> Self {
70 Self { heading, links, class, force_render: false }
71 }
72
73 pub fn forced(heading: Link<'a>, class: &'static str) -> Self {
74 Self { heading, links: vec![], class, force_render: true }
75 }
76
77 pub fn should_render(&self) -> bool {
78 self.force_render || !self.links.is_empty()
79 }
80}
81
82#[derive(PartialEq, Eq, Hash, Clone)]
84pub(crate) struct Link<'a> {
85 name: Cow<'a, str>,
87 name_html: Option<Cow<'a, str>>,
89 href: Cow<'a, str>,
91 children: Vec<Link<'a>>,
93}
94
95impl Ord for Link<'_> {
96 fn cmp(&self, other: &Self) -> Ordering {
97 match compare_names(&self.name, &other.name) {
98 Ordering::Equal => {}
99 result => return result,
100 }
101 (&self.name_html, &self.href, &self.children).cmp(&(
102 &other.name_html,
103 &other.href,
104 &other.children,
105 ))
106 }
107}
108
109impl PartialOrd for Link<'_> {
110 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
111 Some(self.cmp(other))
112 }
113}
114
115impl<'a> Link<'a> {
116 pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
117 Self { href: href.into(), name: name.into(), children: vec![], name_html: None }
118 }
119 pub fn empty() -> Link<'static> {
120 Link::new("", "")
121 }
122}
123
124pub(crate) mod filters {
125 use std::fmt::{self, Display};
126
127 use askama::filters::Safe;
128
129 use crate::html::escape::EscapeBodyTextWithWbr;
130
131 #[askama::filter_fn]
132 pub(crate) fn wrapped(
133 v: impl Display,
134 _: &dyn askama::Values,
135 ) -> askama::Result<Safe<impl Display>> {
136 let string = v.to_string();
137 Ok(Safe(fmt::from_fn(move |f| EscapeBodyTextWithWbr(&string).fmt(f))))
138 }
139}
140
141pub(super) fn print_sidebar(
142 cx: &Context<'_>,
143 it: &clean::Item,
144 mut buffer: impl fmt::Write,
145) -> fmt::Result {
146 let mut ids = IdMap::new();
147 let mut blocks: Vec<LinkBlock<'_>> = docblock_toc(cx, it, &mut ids).into_iter().collect();
148 let deref_id_map = cx.deref_id_map.borrow();
149 match it.kind {
150 clean::StructItem(ref s) => sidebar_struct(cx, it, s, &mut blocks, &deref_id_map),
151 clean::TraitItem(ref t) => sidebar_trait(cx, it, t, &mut blocks, &deref_id_map),
152 clean::PrimitiveItem(_) => sidebar_primitive(cx, it, &mut blocks, &deref_id_map),
153 clean::UnionItem(ref u) => sidebar_union(cx, it, u, &mut blocks, &deref_id_map),
154 clean::EnumItem(ref e) => sidebar_enum(cx, it, e, &mut blocks, &deref_id_map),
155 clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t, &mut blocks, &deref_id_map),
156 clean::ModuleItem(ref m) => {
157 blocks.push(sidebar_module(&m.items, &mut ids, ModuleLike::from(it)))
158 }
159 clean::ForeignTypeItem => sidebar_foreign_type(cx, it, &mut blocks, &deref_id_map),
160 _ => {}
161 }
162 let (title_prefix, title) = if !blocks.is_empty() && !it.is_crate() {
172 (
173 match it.kind {
174 clean::ModuleItem(..) => "Module ",
175 _ => "",
176 },
177 it.name.as_ref().unwrap().as_str(),
178 )
179 } else {
180 ("", "")
181 };
182 let sidebar_path =
189 if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
190 let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
191 let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
192 if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
193 } else {
194 "".into()
195 };
196 let sidebar = Sidebar {
197 title_prefix,
198 title,
199 is_mod: it.is_mod(),
200 is_crate: it.is_crate(),
201 parent_is_crate: sidebar_path.len() == 1,
202 blocks,
203 path,
204 };
205 sidebar.render_into(&mut buffer)?;
206 Ok(())
207}
208
209fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
210 let mut fields = fields
211 .iter()
212 .filter(|f| matches!(f.kind, clean::StructFieldItem(..)))
213 .filter_map(|f| {
214 f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
215 })
216 .collect::<Vec<Link<'a>>>();
217 fields.sort();
218 fields
219}
220
221fn docblock_toc<'a>(
222 cx: &'a Context<'_>,
223 it: &'a clean::Item,
224 ids: &mut IdMap,
225) -> Option<LinkBlock<'a>> {
226 let (toc, _) = MarkdownWithToc {
227 content: &it.doc_value(),
228 links: &it.links(cx),
229 ids,
230 error_codes: cx.shared.codes,
231 edition: cx.shared.edition(),
232 playground: &cx.shared.playground,
233 }
234 .into_parts();
235 let links: Vec<Link<'_>> = toc
236 .entries
237 .into_iter()
238 .map(|entry| {
239 Link {
240 name_html: if entry.html == entry.name { None } else { Some(entry.html.into()) },
241 name: entry.name.into(),
242 href: entry.id.into(),
243 children: entry
244 .children
245 .entries
246 .into_iter()
247 .map(|entry| Link {
248 name_html: if entry.html == entry.name {
249 None
250 } else {
251 Some(entry.html.into())
252 },
253 name: entry.name.into(),
254 href: entry.id.into(),
255 children: vec![],
259 })
260 .collect(),
261 }
262 })
263 .collect();
264 if links.is_empty() {
265 None
266 } else {
267 Some(LinkBlock::new(Link::new("", "Sections"), "top-toc", links))
268 }
269}
270
271fn sidebar_struct<'a>(
272 cx: &'a Context<'_>,
273 it: &'a clean::Item,
274 s: &'a clean::Struct,
275 items: &mut Vec<LinkBlock<'a>>,
276 deref_id_map: &'a DefIdMap<String>,
277) {
278 let fields = get_struct_fields_name(&s.fields);
279 let field_name = match s.ctor_kind {
280 Some(CtorKind::Fn) => Some("Tuple Fields"),
281 None => Some("Fields"),
282 _ => None,
283 };
284 if let Some(name) = field_name {
285 items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields));
286 }
287 sidebar_assoc_items(cx, it, items, deref_id_map);
288}
289
290fn sidebar_trait<'a>(
291 cx: &'a Context<'_>,
292 it: &'a clean::Item,
293 t: &'a clean::Trait,
294 blocks: &mut Vec<LinkBlock<'a>>,
295 deref_id_map: &'a DefIdMap<String>,
296) {
297 fn filter_items<'a>(
298 items: &'a [clean::Item],
299 filt: impl Fn(&clean::Item) -> bool,
300 ty: &str,
301 ) -> Vec<Link<'a>> {
302 let mut res = items
303 .iter()
304 .filter_map(|m: &clean::Item| match m.name {
305 Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
306 _ => None,
307 })
308 .collect::<Vec<Link<'a>>>();
309 res.sort();
310 res
311 }
312
313 let req_assoc = filter_items(&t.items, |m| m.is_required_associated_type(), "associatedtype");
314 let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
315 let req_assoc_const =
316 filter_items(&t.items, |m| m.is_required_associated_const(), "associatedconstant");
317 let prov_assoc_const =
318 filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
319 let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
320 let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
321 let mut foreign_impls = vec![];
322 if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
323 foreign_impls.extend(
324 implementors
325 .iter()
326 .filter(|i| !i.is_on_local_type(cx))
327 .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
328 .map(|(name, id)| Link::new(id, name)),
329 );
330 foreign_impls.sort();
331 }
332
333 blocks.extend(
334 [
335 ("required-associated-consts", "Required Associated Constants", req_assoc_const),
336 ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
337 ("required-associated-types", "Required Associated Types", req_assoc),
338 ("provided-associated-types", "Provided Associated Types", prov_assoc),
339 ]
340 .into_iter()
341 .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)),
342 );
343
344 let tcx = cx.tcx();
345 let mut req_method_link =
348 LinkBlock::new(Link::new("required-methods", "Required Methods"), "", req_method);
349 let rustc_middle::ty::TraitDef { must_implement_one_of, .. } = tcx.trait_def(t.def_id);
350 req_method_link.force_render = must_implement_one_of.is_some();
351 blocks.push(req_method_link);
352
353 blocks.push(LinkBlock::new(Link::new("provided-methods", "Provided Methods"), "", prov_method));
354
355 sidebar_assoc_items(cx, it, blocks, deref_id_map);
356
357 if !t.is_dyn_compatible(tcx) {
358 blocks.push(LinkBlock::forced(
359 Link::new("dyn-compatibility", "Dyn Compatibility"),
360 "dyn-compatibility-note",
361 ));
362 }
363
364 blocks.push(LinkBlock::new(
365 Link::new("foreign-impls", "Implementations on Foreign Types"),
366 "",
367 foreign_impls,
368 ));
369
370 blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
371 if t.is_auto(tcx) {
372 blocks.push(LinkBlock::forced(
373 Link::new("synthetic-implementors", "Auto Implementors"),
374 "impl-auto",
375 ));
376 }
377}
378
379fn sidebar_primitive<'a>(
380 cx: &'a Context<'_>,
381 it: &'a clean::Item,
382 items: &mut Vec<LinkBlock<'a>>,
383 deref_id_map: &'a DefIdMap<String>,
384) {
385 if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
386 sidebar_assoc_items(cx, it, items, deref_id_map);
387 } else {
388 let (concrete, synthetic, blanket_impl) =
389 super::get_filtered_impls_for_reference(&cx.shared, it);
390
391 sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl, items);
392 }
393}
394
395fn sidebar_type_alias<'a>(
396 cx: &'a Context<'_>,
397 it: &'a clean::Item,
398 t: &'a clean::TypeAlias,
399 items: &mut Vec<LinkBlock<'a>>,
400 deref_id_map: &'a DefIdMap<String>,
401) {
402 if let Some(inner_type) = &t.inner_type {
403 items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased Type"), "type"));
404 match inner_type {
405 clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
406 let mut variants = variants
407 .iter()
408 .filter(|i| !i.is_stripped())
409 .filter_map(|v| v.name)
410 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
411 .collect::<Vec<_>>();
412 variants.sort_unstable();
413
414 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
415 }
416 clean::TypeAliasInnerType::Union { fields }
417 | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
418 let fields = get_struct_fields_name(fields);
419 items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
420 }
421 }
422 }
423 sidebar_assoc_items(cx, it, items, deref_id_map);
424}
425
426fn sidebar_union<'a>(
427 cx: &'a Context<'_>,
428 it: &'a clean::Item,
429 u: &'a clean::Union,
430 items: &mut Vec<LinkBlock<'a>>,
431 deref_id_map: &'a DefIdMap<String>,
432) {
433 let fields = get_struct_fields_name(&u.fields);
434 items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields));
435 sidebar_assoc_items(cx, it, items, deref_id_map);
436}
437
438fn sidebar_assoc_items<'a>(
440 cx: &'a Context<'_>,
441 it: &'a clean::Item,
442 links: &mut Vec<LinkBlock<'a>>,
443 deref_id_map: &'a DefIdMap<String>,
444) {
445 let did = it.item_id.expect_def_id();
446 let cache = cx.cache();
447
448 let mut assoc_consts = Vec::new();
449 let mut assoc_types = Vec::new();
450 let mut assoc_fns = Vec::new();
451 let mut methods = Vec::new();
452 if let Some(v) = cache.impls.get(&did) {
453 let mut used_links = UsedLinks::default();
454 let mut id_map = IdMap::new();
455
456 {
457 let used_links_bor = &mut used_links;
458 for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) {
459 assoc_consts.extend(get_associated_constants(impl_, used_links_bor));
460 assoc_types.extend(get_associated_types(impl_, used_links_bor));
461 methods.extend(get_methods(
462 impl_,
463 GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns },
464 used_links_bor,
465 cx,
466 ));
467 }
468 assoc_consts.sort();
470 assoc_types.sort();
471 postprocess_method_list(&mut assoc_fns);
472 postprocess_method_list(&mut methods);
473 }
474
475 let mut blocks = vec![
476 LinkBlock::new(
477 Link::new("implementations", "Associated Constants"),
478 "associatedconstant",
479 assoc_consts,
480 ),
481 LinkBlock::new(
482 Link::new("implementations", "Associated Types"),
483 "associatedtype",
484 assoc_types,
485 ),
486 LinkBlock::new(
487 Link::new("implementations", "Associated Functions"),
488 "method",
489 assoc_fns,
490 ),
491 LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
492 ];
493
494 if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
495 if let Some(impl_) = v.iter().find(|i| {
496 i.trait_did() == cx.tcx().lang_items().deref_trait() && !i.is_negative_trait_impl()
497 }) {
498 let mut derefs = DefIdSet::default();
499 derefs.insert(did);
500 sidebar_deref_methods(
501 cx,
502 &mut blocks,
503 impl_,
504 v,
505 &mut derefs,
506 &mut used_links,
507 deref_id_map,
508 );
509 }
510
511 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
512 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
513 let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
514 concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
515
516 sidebar_render_assoc_items(
517 cx,
518 &mut id_map,
519 concrete,
520 synthetic,
521 blanket_impl,
522 &mut blocks,
523 );
524 }
525
526 links.append(&mut blocks);
527 }
528}
529
530fn sidebar_deref_methods<'a>(
531 cx: &'a Context<'_>,
532 out: &mut Vec<LinkBlock<'a>>,
533 impl_: &Impl,
534 v: &[Impl],
535 derefs: &mut DefIdSet,
536 used_links: &mut UsedLinks,
537 deref_id_map: &'a DefIdMap<String>,
538) {
539 let c = cx.cache();
540
541 debug!("found Deref: {impl_:?}");
542 if let Some((target, real_target)) =
543 impl_.inner_impl().items.iter().find_map(|item| match item.kind {
544 clean::AssocTypeItem(ref t, _) => Some(match *t {
545 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
546 _ => (&t.type_, &t.type_),
547 }),
548 _ => None,
549 })
550 {
551 debug!("found target, real_target: {target:?} {real_target:?}");
552 if let Some(did) = target.def_id(c) &&
553 let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
554 (did == type_did || !derefs.insert(did))
556 {
557 return;
559 }
560 let tcx = cx.tcx();
561 let deref_mut = v.iter().any(|i| i.trait_did() == tcx.lang_items().deref_mut_trait());
562 let inner_impl = target
563 .def_id(c)
564 .or_else(|| {
565 target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
566 })
567 .and_then(|did| c.impls.get(&did));
568 if let Some(impls) = inner_impl {
569 debug!("found inner_impl: {impls:?}");
570 let is_deref_target_copy =
571 super::compute_if_deref_target_implements_copy(tcx, impl_.def_id());
572 let mut ret = impls
573 .iter()
574 .filter(|i| {
575 i.inner_impl().trait_.is_none()
576 && real_target.is_doc_subtype_of(&i.inner_impl().for_, c)
577 })
578 .flat_map(|i| {
579 get_methods(
580 i.inner_impl(),
581 GetMethodsMode::Deref { deref_mut, is_deref_target_copy },
582 used_links,
583 cx,
584 )
585 .collect::<Vec<_>>()
586 })
587 .collect::<Vec<_>>();
588 if !ret.is_empty() {
589 let id = if let Some(target_def_id) = real_target.def_id(c) {
590 Cow::Borrowed(
591 deref_id_map
592 .get(&target_def_id)
593 .expect("Deref section without derived id")
594 .as_str(),
595 )
596 } else {
597 Cow::Borrowed("deref-methods")
598 };
599 let title = format!(
600 "Methods from {:#}<Target={:#}>",
601 print_path(impl_.inner_impl().trait_.as_ref().unwrap(), cx),
602 print_type(real_target, cx),
603 );
604
605 postprocess_method_list(&mut ret);
606 out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
607 }
608 }
609
610 if let Some(target_did) = target.def_id(c)
612 && let Some(target_impls) = c.impls.get(&target_did)
613 && let Some(target_deref_impl) = target_impls.iter().find(|i| {
614 i.inner_impl()
615 .trait_
616 .as_ref()
617 .map(|t| Some(t.def_id()) == tcx.lang_items().deref_trait())
618 .unwrap_or(false)
619 && !i.is_negative_trait_impl()
620 })
621 {
622 sidebar_deref_methods(
623 cx,
624 out,
625 target_deref_impl,
626 target_impls,
627 derefs,
628 used_links,
629 deref_id_map,
630 );
631 }
632 }
633}
634
635fn sidebar_enum<'a>(
636 cx: &'a Context<'_>,
637 it: &'a clean::Item,
638 e: &'a clean::Enum,
639 items: &mut Vec<LinkBlock<'a>>,
640 deref_id_map: &'a DefIdMap<String>,
641) {
642 let mut variants = e
643 .non_stripped_variants()
644 .filter_map(|v| v.name)
645 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
646 .collect::<Vec<_>>();
647 variants.sort_unstable();
648
649 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
650 sidebar_assoc_items(cx, it, items, deref_id_map);
651}
652
653pub(crate) fn sidebar_module_like(
654 item_sections_in_use: FxHashSet<ItemSection>,
655 ids: &mut IdMap,
656 module_like: ModuleLike,
657) -> LinkBlock<'static> {
658 let item_sections: Vec<Link<'_>> = ItemSection::ALL
659 .iter()
660 .copied()
661 .filter(|sec| item_sections_in_use.contains(sec))
662 .map(|sec| Link::new(ids.derive(sec.id()), sec.name()))
663 .collect();
664 let header = if let Some(first_section) = item_sections.first() {
665 Link::new(
666 first_section.href.clone(),
667 if module_like.is_crate() { "Crate Items" } else { "Module Items" },
668 )
669 } else {
670 Link::empty()
671 };
672 LinkBlock::new(header, "", item_sections)
673}
674
675fn sidebar_module(
676 items: &[clean::Item],
677 ids: &mut IdMap,
678 module_like: ModuleLike,
679) -> LinkBlock<'static> {
680 let mut item_sections_in_use: FxHashSet<_> = Default::default();
681
682 for item in items.iter().filter(|it| {
683 !it.is_stripped()
684 && it
685 .name
686 .or_else(|| {
687 if let clean::ImportItem(ref i) = it.kind
688 && let clean::ImportKind::Simple(s) = i.kind
689 {
690 Some(s)
691 } else {
692 None
693 }
694 })
695 .is_some()
696 }) {
697 for type_ in item.types() {
698 item_sections_in_use.insert(item_ty_to_section(type_));
699 }
700 }
701
702 sidebar_module_like(item_sections_in_use, ids, module_like)
703}
704
705fn sidebar_foreign_type<'a>(
706 cx: &'a Context<'_>,
707 it: &'a clean::Item,
708 items: &mut Vec<LinkBlock<'a>>,
709 deref_id_map: &'a DefIdMap<String>,
710) {
711 sidebar_assoc_items(cx, it, items, deref_id_map);
712}
713
714fn sidebar_render_assoc_items(
716 cx: &Context<'_>,
717 id_map: &mut IdMap,
718 concrete: Vec<&Impl>,
719 synthetic: Vec<&Impl>,
720 blanket_impl: Vec<&Impl>,
721 items: &mut Vec<LinkBlock<'_>>,
722) {
723 let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
724 let mut links = FxHashSet::default();
725
726 let mut ret = impls
727 .iter()
728 .filter_map(|i| {
729 let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), i.impl_item.item_id));
730 let generated = Link::new(encoded, impl_trait_key(cx, i)?);
731 if links.insert(generated.clone()) { Some(generated) } else { None }
732 })
733 .collect::<Vec<Link<'static>>>();
734 ret.sort();
735 ret
736 };
737
738 let concrete = format_impls(concrete, id_map);
739 let synthetic = format_impls(synthetic, id_map);
740 let blanket = format_impls(blanket_impl, id_map);
741 items.extend([
742 LinkBlock::new(
743 Link::new("trait-implementations", "Trait Implementations"),
744 "trait-implementation",
745 concrete,
746 ),
747 LinkBlock::new(
748 Link::new("synthetic-implementations", "Auto Trait Implementations"),
749 "synthetic-implementation",
750 synthetic,
751 ),
752 LinkBlock::new(
753 Link::new("blanket-implementations", "Blanket Implementations"),
754 "blanket-implementation",
755 blanket,
756 ),
757 ]);
758}
759
760type UsedLinks = FxHashMap<String, usize>;
762
763fn get_next_url(used_links: &mut UsedLinks, url: String) -> String {
764 let count = used_links.entry(url.clone()).or_insert(0);
765 let res = if *count == 0 { url } else { format!("{url}-{count}") };
766 *count += 1;
767 res
768}
769
770enum GetMethodsMode<'r, 'l> {
771 Deref { deref_mut: bool, is_deref_target_copy: bool },
772 AlsoCollectAssocFns { assoc_fns: &'r mut Vec<Link<'l>> },
773}
774
775fn get_methods<'a>(
776 i: &'a clean::Impl,
777 mut mode: GetMethodsMode<'_, 'a>,
778 used_links: &mut UsedLinks,
779 cx: &Context<'_>,
780) -> impl Iterator<Item = Link<'a>> {
781 i.items.iter().filter_map(move |item| {
782 if let Some(ref name) = item.name
783 && item.is_method()
784 {
785 let mut build_link = || {
786 Link::new(
787 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)),
788 format!("{} ({:#})", name.as_str(), print_type(&i.for_, cx)),
789 )
790 };
791 match &mut mode {
792 &mut GetMethodsMode::Deref { deref_mut, is_deref_target_copy } => {
793 if super::should_render_item(item, deref_mut, cx.tcx(), is_deref_target_copy) {
794 Some(build_link())
795 } else {
796 None
797 }
798 }
799 GetMethodsMode::AlsoCollectAssocFns { assoc_fns } => {
800 if item.has_self_param() {
801 Some(build_link())
802 } else {
803 assoc_fns.push(build_link());
804 None
805 }
806 }
807 }
808 } else {
809 None
810 }
811 })
812}
813
814fn postprocess_method_list(methods: &mut [Link<'_>]) {
815 methods.sort();
817 let trunc_to = |dst: &mut Cow<'_, str>, len: usize| match dst {
818 Cow::Borrowed(s) => *s = &s[..len],
819 Cow::Owned(s) => s.truncate(len),
820 };
821 fn get_short_name<'a>(itm: Option<&'a Link<'a>>) -> Option<&'a str> {
822 Some(itm?.name.split_once(' ')?.0)
823 }
824 for i in 0..methods.len() {
825 let short_name = get_short_name(methods.get(i));
826 if get_short_name(methods.get(i.wrapping_sub(1))) != short_name
827 && short_name != get_short_name(methods.get(i + 1))
828 {
829 let shortened_len = short_name.unwrap().len();
830 trunc_to(&mut methods[i].name, shortened_len);
831 }
832 }
833}
834
835fn get_associated_constants<'a>(
836 i: &'a clean::Impl,
837 used_links: &mut UsedLinks,
838) -> impl Iterator<Item = Link<'a>> {
839 i.items.iter().filter_map(|item| {
840 if let Some(ref name) = item.name
841 && item.is_associated_const()
842 {
843 Some(Link::new(
844 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)),
845 name.as_str(),
846 ))
847 } else {
848 None
849 }
850 })
851}
852
853fn get_associated_types<'a>(
854 i: &'a clean::Impl,
855 used_links: &mut UsedLinks,
856) -> impl Iterator<Item = Link<'a>> {
857 i.items.iter().filter_map(|item| {
858 if let Some(ref name) = item.name
859 && item.is_associated_type()
860 {
861 Some(Link::new(
862 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)),
863 name.as_str(),
864 ))
865 } else {
866 None
867 }
868 })
869}