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::{self, ItemKind};
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 ItemKind::Struct(ref s) => sidebar_struct(cx, it, s, &mut blocks, &deref_id_map),
151 ItemKind::Trait(ref t) => sidebar_trait(cx, it, t, &mut blocks, &deref_id_map),
152 ItemKind::PrimitiveType(_) => sidebar_primitive(cx, it, &mut blocks, &deref_id_map),
153 ItemKind::Union(ref u) => sidebar_union(cx, it, u, &mut blocks, &deref_id_map),
154 ItemKind::Enum(ref e) => sidebar_enum(cx, it, e, &mut blocks, &deref_id_map),
155 ItemKind::TypeAlias(ref t) => sidebar_type_alias(cx, it, t, &mut blocks, &deref_id_map),
156 ItemKind::Module(ref m) => {
157 blocks.push(sidebar_module(&m.items, &mut ids, ModuleLike::from(it)))
158 }
159 ItemKind::ForeignType => 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 ItemKind::Module(..) => "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, ItemKind::StructField(..)))
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_tys = filter_items(&t.items, |m| m.is_assoc_ty_without_body(), "associatedtype");
314 let prov_assoc_tys = filter_items(&t.items, |m| m.is_assoc_ty_with_body(), "associatedtype");
315 let req_assoc_consts =
316 filter_items(&t.items, |m| m.is_assoc_const_without_body(), "associatedconstant");
317 let prov_assoc_consts =
318 filter_items(&t.items, |m| m.is_assoc_const_with_body(), "associatedconstant");
319 let req_assoc_fns = filter_items(&t.items, |m| m.is_assoc_fn_without_body(), "tymethod");
320 let prov_assoc_fns = filter_items(&t.items, |m| m.is_assoc_fn_with_body(), "method");
321
322 let mut foreign_impls = vec![];
323 if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
324 foreign_impls.extend(
325 implementors
326 .iter()
327 .filter(|i| !i.is_on_local_type(cx))
328 .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
329 .map(|(name, id)| Link::new(id, name)),
330 );
331 foreign_impls.sort();
332 }
333
334 blocks.extend(
335 [
336 ("required-associated-consts", "Required Associated Constants", req_assoc_consts),
337 ("provided-associated-consts", "Provided Associated Constants", prov_assoc_consts),
338 ("required-associated-types", "Required Associated Types", req_assoc_tys),
339 ("provided-associated-types", "Provided Associated Types", prov_assoc_tys),
340 ]
341 .into_iter()
342 .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)),
343 );
344
345 let tcx = cx.tcx();
346 let mut req_assoc_fns_link =
349 LinkBlock::new(Link::new("required-methods", "Required Methods"), "", req_assoc_fns);
350 let rustc_middle::ty::TraitDef { must_implement_one_of, .. } = tcx.trait_def(t.def_id);
351 req_assoc_fns_link.force_render = must_implement_one_of.is_some();
352 blocks.push(req_assoc_fns_link);
353
354 blocks.push(LinkBlock::new(
355 Link::new("provided-methods", "Provided Methods"),
356 "",
357 prov_assoc_fns,
358 ));
359
360 sidebar_assoc_items(cx, it, blocks, deref_id_map);
361
362 blocks.push(LinkBlock::forced(
363 Link::new("dyn-compatibility", "Dyn Compatibility"),
364 "dyn-compatibility-note",
365 ));
366
367 blocks.push(LinkBlock::new(
368 Link::new("foreign-impls", "Implementations on Foreign Types"),
369 "",
370 foreign_impls,
371 ));
372
373 blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
374 if t.is_auto(tcx) {
375 blocks.push(LinkBlock::forced(
376 Link::new("synthetic-implementors", "Auto Implementors"),
377 "impl-auto",
378 ));
379 }
380}
381
382fn sidebar_primitive<'a>(
383 cx: &'a Context<'_>,
384 it: &'a clean::Item,
385 items: &mut Vec<LinkBlock<'a>>,
386 deref_id_map: &'a DefIdMap<String>,
387) {
388 if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
389 sidebar_assoc_items(cx, it, items, deref_id_map);
390 } else {
391 let (concrete, synthetic, blanket_impl) =
392 super::get_filtered_impls_for_reference(&cx.shared, it);
393
394 sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl, items);
395 }
396}
397
398fn sidebar_type_alias<'a>(
399 cx: &'a Context<'_>,
400 it: &'a clean::Item,
401 t: &'a clean::TypeAlias,
402 items: &mut Vec<LinkBlock<'a>>,
403 deref_id_map: &'a DefIdMap<String>,
404) {
405 if let Some(inner_type) = &t.inner_type {
406 items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased Type"), "type"));
407 match inner_type {
408 clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
409 let mut variants = variants
410 .iter()
411 .filter(|i| !i.is_stripped())
412 .filter_map(|v| v.name)
413 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
414 .collect::<Vec<_>>();
415 variants.sort_unstable();
416
417 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
418 }
419 clean::TypeAliasInnerType::Union { fields }
420 | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
421 let fields = get_struct_fields_name(fields);
422 items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
423 }
424 }
425 }
426 sidebar_assoc_items(cx, it, items, deref_id_map);
427}
428
429fn sidebar_union<'a>(
430 cx: &'a Context<'_>,
431 it: &'a clean::Item,
432 u: &'a clean::Union,
433 items: &mut Vec<LinkBlock<'a>>,
434 deref_id_map: &'a DefIdMap<String>,
435) {
436 let fields = get_struct_fields_name(&u.fields);
437 items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields));
438 sidebar_assoc_items(cx, it, items, deref_id_map);
439}
440
441fn sidebar_assoc_items<'a>(
443 cx: &'a Context<'_>,
444 it: &'a clean::Item,
445 links: &mut Vec<LinkBlock<'a>>,
446 deref_id_map: &'a DefIdMap<String>,
447) {
448 let did = it.item_id.expect_def_id();
449 let cache = cx.cache();
450
451 let mut assoc_consts = Vec::new();
452 let mut assoc_types = Vec::new();
453 let mut assoc_fns = Vec::new();
454 let mut methods = Vec::new();
455 if let Some(v) = cache.impls.get(&did) {
456 let mut used_links = UsedLinks::default();
457 let mut id_map = IdMap::new();
458
459 {
460 let used_links_bor = &mut used_links;
461 for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) {
462 assoc_consts.extend(get_associated_constants(impl_, used_links_bor));
463 assoc_types.extend(get_associated_types(impl_, used_links_bor));
464 methods.extend(get_methods(
465 impl_,
466 GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns },
467 used_links_bor,
468 cx,
469 ));
470 }
471 assoc_consts.sort();
473 assoc_types.sort();
474 postprocess_method_list(&mut assoc_fns);
475 postprocess_method_list(&mut methods);
476 }
477
478 let mut blocks = vec![
479 LinkBlock::new(
480 Link::new("implementations", "Associated Constants"),
481 "associatedconstant",
482 assoc_consts,
483 ),
484 LinkBlock::new(
485 Link::new("implementations", "Associated Types"),
486 "associatedtype",
487 assoc_types,
488 ),
489 LinkBlock::new(
490 Link::new("implementations", "Associated Functions"),
491 "method",
492 assoc_fns,
493 ),
494 LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
495 ];
496
497 if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
498 if let Some(impl_) = v.iter().find(|i| {
499 i.trait_did() == cx.tcx().lang_items().deref_trait() && !i.is_negative_trait_impl()
500 }) {
501 let mut derefs = DefIdSet::default();
502 derefs.insert(did);
503 sidebar_deref_methods(
504 cx,
505 &mut blocks,
506 impl_,
507 v,
508 &mut derefs,
509 &mut used_links,
510 deref_id_map,
511 );
512 }
513
514 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
515 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
516 let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
517 concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
518
519 sidebar_render_assoc_items(
520 cx,
521 &mut id_map,
522 concrete,
523 synthetic,
524 blanket_impl,
525 &mut blocks,
526 );
527 }
528
529 links.append(&mut blocks);
530 }
531}
532
533fn sidebar_deref_methods<'a>(
534 cx: &'a Context<'_>,
535 out: &mut Vec<LinkBlock<'a>>,
536 impl_: &Impl,
537 v: &[Impl],
538 derefs: &mut DefIdSet,
539 used_links: &mut UsedLinks,
540 deref_id_map: &'a DefIdMap<String>,
541) {
542 let c = cx.cache();
543
544 debug!("found Deref: {impl_:?}");
545 if let Some((target, real_target)) =
546 impl_.inner_impl().items.iter().find_map(|item| match &item.kind {
547 ItemKind::AssocType(clean::AssocType { type_: Some(type_), .. }) => {
548 Some((type_.middle_type.as_ref().unwrap_or(&type_.type_), &type_.type_))
549 }
550 _ => None,
551 })
552 {
553 debug!("found target, real_target: {target:?} {real_target:?}");
554 if let Some(did) = target.def_id(c) &&
555 let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
556 (did == type_did || !derefs.insert(did))
558 {
559 return;
561 }
562 let tcx = cx.tcx();
563 let deref_mut = v.iter().any(|i| i.trait_did() == tcx.lang_items().deref_mut_trait());
564 let inner_impl = target
565 .def_id(c)
566 .or_else(|| {
567 target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
568 })
569 .and_then(|did| c.impls.get(&did));
570 if let Some(impls) = inner_impl {
571 debug!("found inner_impl: {impls:?}");
572 let is_deref_target_copy =
573 super::compute_if_deref_target_implements_copy(tcx, impl_.def_id());
574 let mut ret = impls
575 .iter()
576 .filter(|i| {
577 i.inner_impl().trait_.is_none()
578 && real_target.is_doc_subtype_of(&i.inner_impl().for_, c)
579 })
580 .flat_map(|i| {
581 get_methods(
582 i.inner_impl(),
583 GetMethodsMode::Deref { deref_mut, is_deref_target_copy },
584 used_links,
585 cx,
586 )
587 .collect::<Vec<_>>()
588 })
589 .collect::<Vec<_>>();
590 if !ret.is_empty() {
591 let id = if let Some(target_def_id) = real_target.def_id(c) {
592 Cow::Borrowed(
593 deref_id_map
594 .get(&target_def_id)
595 .expect("Deref section without derived id")
596 .as_str(),
597 )
598 } else {
599 Cow::Borrowed("deref-methods")
600 };
601 let title = format!(
602 "Methods from {:#}<Target={:#}>",
603 print_path(impl_.inner_impl().trait_.as_ref().unwrap(), cx),
604 print_type(real_target, cx),
605 );
606
607 postprocess_method_list(&mut ret);
608 out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
609 }
610 }
611
612 if let Some(target_did) = target.def_id(c)
614 && let Some(target_impls) = c.impls.get(&target_did)
615 && let Some(target_deref_impl) = target_impls.iter().find(|i| {
616 i.inner_impl()
617 .trait_
618 .as_ref()
619 .map(|t| Some(t.def_id()) == tcx.lang_items().deref_trait())
620 .unwrap_or(false)
621 && !i.is_negative_trait_impl()
622 })
623 {
624 sidebar_deref_methods(
625 cx,
626 out,
627 target_deref_impl,
628 target_impls,
629 derefs,
630 used_links,
631 deref_id_map,
632 );
633 }
634 }
635}
636
637fn sidebar_enum<'a>(
638 cx: &'a Context<'_>,
639 it: &'a clean::Item,
640 e: &'a clean::Enum,
641 items: &mut Vec<LinkBlock<'a>>,
642 deref_id_map: &'a DefIdMap<String>,
643) {
644 let mut variants = e
645 .non_stripped_variants()
646 .filter_map(|v| v.name)
647 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
648 .collect::<Vec<_>>();
649 variants.sort_unstable();
650
651 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
652 sidebar_assoc_items(cx, it, items, deref_id_map);
653}
654
655pub(crate) fn sidebar_module_like(
656 item_sections_in_use: FxHashSet<ItemSection>,
657 ids: &mut IdMap,
658 module_like: ModuleLike,
659) -> LinkBlock<'static> {
660 let item_sections: Vec<Link<'_>> = ItemSection::ALL
661 .iter()
662 .copied()
663 .filter(|sec| item_sections_in_use.contains(sec))
664 .map(|sec| Link::new(ids.derive(sec.id()), sec.name()))
665 .collect();
666 let header = if let Some(first_section) = item_sections.first() {
667 Link::new(
668 first_section.href.clone(),
669 if module_like.is_crate() { "Crate Items" } else { "Module Items" },
670 )
671 } else {
672 Link::empty()
673 };
674 LinkBlock::new(header, "", item_sections)
675}
676
677fn sidebar_module(
678 items: &[clean::Item],
679 ids: &mut IdMap,
680 module_like: ModuleLike,
681) -> LinkBlock<'static> {
682 let mut item_sections_in_use: FxHashSet<_> = Default::default();
683
684 for item in items.iter().filter(|it| {
685 !it.is_stripped()
686 && it
687 .name
688 .or_else(|| {
689 if let ItemKind::Import(ref i) = it.kind
690 && let clean::ImportKind::Simple(s) = i.kind
691 {
692 Some(s)
693 } else {
694 None
695 }
696 })
697 .is_some()
698 }) {
699 for type_ in item.types() {
700 item_sections_in_use.insert(item_ty_to_section(type_));
701 }
702 }
703
704 sidebar_module_like(item_sections_in_use, ids, module_like)
705}
706
707fn sidebar_foreign_type<'a>(
708 cx: &'a Context<'_>,
709 it: &'a clean::Item,
710 items: &mut Vec<LinkBlock<'a>>,
711 deref_id_map: &'a DefIdMap<String>,
712) {
713 sidebar_assoc_items(cx, it, items, deref_id_map);
714}
715
716fn sidebar_render_assoc_items(
718 cx: &Context<'_>,
719 id_map: &mut IdMap,
720 concrete: Vec<&Impl>,
721 synthetic: Vec<&Impl>,
722 blanket_impl: Vec<&Impl>,
723 items: &mut Vec<LinkBlock<'_>>,
724) {
725 let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
726 let mut links = FxHashSet::default();
727
728 let mut ret = impls
729 .iter()
730 .filter_map(|i| {
731 let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), i.impl_item.item_id));
732 let generated = Link::new(encoded, impl_trait_key(cx, i)?);
733 if links.insert(generated.clone()) { Some(generated) } else { None }
734 })
735 .collect::<Vec<Link<'static>>>();
736 ret.sort();
737 ret
738 };
739
740 let concrete = format_impls(concrete, id_map);
741 let synthetic = format_impls(synthetic, id_map);
742 let blanket = format_impls(blanket_impl, id_map);
743 items.extend([
744 LinkBlock::new(
745 Link::new("trait-implementations", "Trait Implementations"),
746 "trait-implementation",
747 concrete,
748 ),
749 LinkBlock::new(
750 Link::new("synthetic-implementations", "Auto Trait Implementations"),
751 "synthetic-implementation",
752 synthetic,
753 ),
754 LinkBlock::new(
755 Link::new("blanket-implementations", "Blanket Implementations"),
756 "blanket-implementation",
757 blanket,
758 ),
759 ]);
760}
761
762type UsedLinks = FxHashMap<String, usize>;
764
765fn get_next_url(used_links: &mut UsedLinks, url: String) -> String {
766 let count = used_links.entry(url.clone()).or_insert(0);
767 let res = if *count == 0 { url } else { format!("{url}-{count}") };
768 *count += 1;
769 res
770}
771
772enum GetMethodsMode<'r, 'l> {
773 Deref { deref_mut: bool, is_deref_target_copy: bool },
774 AlsoCollectAssocFns { assoc_fns: &'r mut Vec<Link<'l>> },
775}
776
777fn get_methods<'a>(
778 i: &'a clean::Impl,
779 mut mode: GetMethodsMode<'_, 'a>,
780 used_links: &mut UsedLinks,
781 cx: &Context<'_>,
782) -> impl Iterator<Item = Link<'a>> {
783 i.items.iter().filter_map(move |item| {
784 if let Some(ref name) = item.name
785 && item.is_assoc_fn_with_body()
786 {
787 let mut build_link = || {
788 Link::new(
789 get_next_url(
790 used_links,
791 format!("{typ}.{name}", typ = ItemType::AssocFnWithBody),
792 ),
793 format!("{} ({:#})", name.as_str(), print_type(&i.for_, cx)),
794 )
795 };
796 match &mut mode {
797 &mut GetMethodsMode::Deref { deref_mut, is_deref_target_copy } => {
798 if super::should_render_item_for_deref(
799 item,
800 deref_mut,
801 cx.tcx(),
802 is_deref_target_copy,
803 ) {
804 Some(build_link())
805 } else {
806 None
807 }
808 }
809 GetMethodsMode::AlsoCollectAssocFns { assoc_fns } => {
810 if item.has_self_param() {
811 Some(build_link())
812 } else {
813 assoc_fns.push(build_link());
814 None
815 }
816 }
817 }
818 } else {
819 None
820 }
821 })
822}
823
824fn postprocess_method_list(methods: &mut [Link<'_>]) {
825 methods.sort();
827 let trunc_to = |dst: &mut Cow<'_, str>, len: usize| match dst {
828 Cow::Borrowed(s) => *s = &s[..len],
829 Cow::Owned(s) => s.truncate(len),
830 };
831 fn get_short_name<'a>(itm: Option<&'a Link<'a>>) -> Option<&'a str> {
832 Some(itm?.name.split_once(' ')?.0)
833 }
834 for i in 0..methods.len() {
835 let short_name = get_short_name(methods.get(i));
836 if get_short_name(methods.get(i.wrapping_sub(1))) != short_name
837 && short_name != get_short_name(methods.get(i + 1))
838 {
839 let shortened_len = short_name.unwrap().len();
840 trunc_to(&mut methods[i].name, shortened_len);
841 }
842 }
843}
844
845fn get_associated_constants<'a>(
846 i: &'a clean::Impl,
847 used_links: &mut UsedLinks,
848) -> impl Iterator<Item = Link<'a>> {
849 i.items.iter().filter_map(|item| {
850 if let Some(ref name) = item.name
851 && item.is_assoc_const_with_body()
852 {
853 Some(Link::new(
854 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)),
855 name.as_str(),
856 ))
857 } else {
858 None
859 }
860 })
861}
862
863fn get_associated_types<'a>(
864 i: &'a clean::Impl,
865 used_links: &mut UsedLinks,
866) -> impl Iterator<Item = Link<'a>> {
867 i.items.iter().filter_map(|item| {
868 if let Some(ref name) = item.name
869 && item.is_assoc_ty_with_body()
870 {
871 Some(Link::new(
872 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)),
873 name.as_str(),
874 ))
875 } else {
876 None
877 }
878 })
879}