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