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::{self, TyCtxt};
10use tracing::debug;
11
12use super::{Context, ItemSection, item_ty_to_section};
13use crate::clean;
14use crate::formats::Impl;
15use crate::formats::item_type::ItemType;
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 pub(crate) fn wrapped<T, V: askama::Values>(v: T, _: V) -> askama::Result<Safe<impl Display>>
131 where
132 T: Display,
133 {
134 let string = v.to_string();
135 Ok(Safe(fmt::from_fn(move |f| EscapeBodyTextWithWbr(&string).fmt(f))))
136 }
137}
138
139pub(super) fn print_sidebar(
140 cx: &Context<'_>,
141 it: &clean::Item,
142 mut buffer: impl fmt::Write,
143) -> fmt::Result {
144 let mut ids = IdMap::new();
145 let mut blocks: Vec<LinkBlock<'_>> = docblock_toc(cx, it, &mut ids).into_iter().collect();
146 let deref_id_map = cx.deref_id_map.borrow();
147 match it.kind {
148 clean::StructItem(ref s) => sidebar_struct(cx, it, s, &mut blocks, &deref_id_map),
149 clean::TraitItem(ref t) => sidebar_trait(cx, it, t, &mut blocks, &deref_id_map),
150 clean::PrimitiveItem(_) => sidebar_primitive(cx, it, &mut blocks, &deref_id_map),
151 clean::UnionItem(ref u) => sidebar_union(cx, it, u, &mut blocks, &deref_id_map),
152 clean::EnumItem(ref e) => sidebar_enum(cx, it, e, &mut blocks, &deref_id_map),
153 clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t, &mut blocks, &deref_id_map),
154 clean::ModuleItem(ref m) => {
155 blocks.push(sidebar_module(&m.items, &mut ids, ModuleLike::from(it)))
156 }
157 clean::ForeignTypeItem => sidebar_foreign_type(cx, it, &mut blocks, &deref_id_map),
158 _ => {}
159 }
160 let (title_prefix, title) = if !blocks.is_empty() && !it.is_crate() {
170 (
171 match it.kind {
172 clean::ModuleItem(..) => "Module ",
173 _ => "",
174 },
175 it.name.as_ref().unwrap().as_str(),
176 )
177 } else {
178 ("", "")
179 };
180 let sidebar_path =
187 if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
188 let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
189 let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
190 if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
191 } else {
192 "".into()
193 };
194 let sidebar = Sidebar {
195 title_prefix,
196 title,
197 is_mod: it.is_mod(),
198 is_crate: it.is_crate(),
199 parent_is_crate: sidebar_path.len() == 1,
200 blocks,
201 path,
202 };
203 sidebar.render_into(&mut buffer)?;
204 Ok(())
205}
206
207fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
208 let mut fields = fields
209 .iter()
210 .filter(|f| matches!(f.kind, clean::StructFieldItem(..)))
211 .filter_map(|f| {
212 f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
213 })
214 .collect::<Vec<Link<'a>>>();
215 fields.sort();
216 fields
217}
218
219fn docblock_toc<'a>(
220 cx: &'a Context<'_>,
221 it: &'a clean::Item,
222 ids: &mut IdMap,
223) -> Option<LinkBlock<'a>> {
224 let (toc, _) = MarkdownWithToc {
225 content: &it.doc_value(),
226 links: &it.links(cx),
227 ids,
228 error_codes: cx.shared.codes,
229 edition: cx.shared.edition(),
230 playground: &cx.shared.playground,
231 }
232 .into_parts();
233 let links: Vec<Link<'_>> = toc
234 .entries
235 .into_iter()
236 .map(|entry| {
237 Link {
238 name_html: if entry.html == entry.name { None } else { Some(entry.html.into()) },
239 name: entry.name.into(),
240 href: entry.id.into(),
241 children: entry
242 .children
243 .entries
244 .into_iter()
245 .map(|entry| Link {
246 name_html: if entry.html == entry.name {
247 None
248 } else {
249 Some(entry.html.into())
250 },
251 name: entry.name.into(),
252 href: entry.id.into(),
253 children: vec![],
257 })
258 .collect(),
259 }
260 })
261 .collect();
262 if links.is_empty() {
263 None
264 } else {
265 Some(LinkBlock::new(Link::new("", "Sections"), "top-toc", links))
266 }
267}
268
269fn sidebar_struct<'a>(
270 cx: &'a Context<'_>,
271 it: &'a clean::Item,
272 s: &'a clean::Struct,
273 items: &mut Vec<LinkBlock<'a>>,
274 deref_id_map: &'a DefIdMap<String>,
275) {
276 let fields = get_struct_fields_name(&s.fields);
277 let field_name = match s.ctor_kind {
278 Some(CtorKind::Fn) => Some("Tuple Fields"),
279 None => Some("Fields"),
280 _ => None,
281 };
282 if let Some(name) = field_name {
283 items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields));
284 }
285 sidebar_assoc_items(cx, it, items, deref_id_map);
286}
287
288fn sidebar_trait<'a>(
289 cx: &'a Context<'_>,
290 it: &'a clean::Item,
291 t: &'a clean::Trait,
292 blocks: &mut Vec<LinkBlock<'a>>,
293 deref_id_map: &'a DefIdMap<String>,
294) {
295 fn filter_items<'a>(
296 items: &'a [clean::Item],
297 filt: impl Fn(&clean::Item) -> bool,
298 ty: &str,
299 ) -> Vec<Link<'a>> {
300 let mut res = items
301 .iter()
302 .filter_map(|m: &clean::Item| match m.name {
303 Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
304 _ => None,
305 })
306 .collect::<Vec<Link<'a>>>();
307 res.sort();
308 res
309 }
310
311 let req_assoc = filter_items(&t.items, |m| m.is_required_associated_type(), "associatedtype");
312 let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
313 let req_assoc_const =
314 filter_items(&t.items, |m| m.is_required_associated_const(), "associatedconstant");
315 let prov_assoc_const =
316 filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
317 let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
318 let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
319 let mut foreign_impls = vec![];
320 if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
321 foreign_impls.extend(
322 implementors
323 .iter()
324 .filter(|i| !i.is_on_local_type(cx))
325 .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
326 .map(|(name, id)| Link::new(id, name)),
327 );
328 foreign_impls.sort();
329 }
330
331 blocks.extend(
332 [
333 ("required-associated-consts", "Required Associated Constants", req_assoc_const),
334 ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
335 ("required-associated-types", "Required Associated Types", req_assoc),
336 ("provided-associated-types", "Provided Associated Types", prov_assoc),
337 ("required-methods", "Required Methods", req_method),
338 ("provided-methods", "Provided Methods", prov_method),
339 ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
340 ]
341 .into_iter()
342 .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)),
343 );
344 sidebar_assoc_items(cx, it, blocks, deref_id_map);
345
346 if !t.is_dyn_compatible(cx.tcx()) {
347 blocks.push(LinkBlock::forced(
348 Link::new("dyn-compatibility", "Dyn Compatibility"),
349 "dyn-compatibility-note",
350 ));
351 }
352
353 blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
354 if t.is_auto(cx.tcx()) {
355 blocks.push(LinkBlock::forced(
356 Link::new("synthetic-implementors", "Auto Implementors"),
357 "impl-auto",
358 ));
359 }
360}
361
362fn sidebar_primitive<'a>(
363 cx: &'a Context<'_>,
364 it: &'a clean::Item,
365 items: &mut Vec<LinkBlock<'a>>,
366 deref_id_map: &'a DefIdMap<String>,
367) {
368 if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
369 sidebar_assoc_items(cx, it, items, deref_id_map);
370 } else {
371 let (concrete, synthetic, blanket_impl) =
372 super::get_filtered_impls_for_reference(&cx.shared, it);
373
374 sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl, items);
375 }
376}
377
378fn sidebar_type_alias<'a>(
379 cx: &'a Context<'_>,
380 it: &'a clean::Item,
381 t: &'a clean::TypeAlias,
382 items: &mut Vec<LinkBlock<'a>>,
383 deref_id_map: &'a DefIdMap<String>,
384) {
385 if let Some(inner_type) = &t.inner_type {
386 items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased Type"), "type"));
387 match inner_type {
388 clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
389 let mut variants = variants
390 .iter()
391 .filter(|i| !i.is_stripped())
392 .filter_map(|v| v.name)
393 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
394 .collect::<Vec<_>>();
395 variants.sort_unstable();
396
397 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
398 }
399 clean::TypeAliasInnerType::Union { fields }
400 | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
401 let fields = get_struct_fields_name(fields);
402 items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
403 }
404 }
405 }
406 sidebar_assoc_items(cx, it, items, deref_id_map);
407}
408
409fn sidebar_union<'a>(
410 cx: &'a Context<'_>,
411 it: &'a clean::Item,
412 u: &'a clean::Union,
413 items: &mut Vec<LinkBlock<'a>>,
414 deref_id_map: &'a DefIdMap<String>,
415) {
416 let fields = get_struct_fields_name(&u.fields);
417 items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields));
418 sidebar_assoc_items(cx, it, items, deref_id_map);
419}
420
421fn sidebar_assoc_items<'a>(
423 cx: &'a Context<'_>,
424 it: &'a clean::Item,
425 links: &mut Vec<LinkBlock<'a>>,
426 deref_id_map: &'a DefIdMap<String>,
427) {
428 let did = it.item_id.expect_def_id();
429 let cache = cx.cache();
430
431 let mut assoc_consts = Vec::new();
432 let mut assoc_types = Vec::new();
433 let mut methods = Vec::new();
434 if let Some(v) = cache.impls.get(&did) {
435 let mut used_links = FxHashSet::default();
436 let mut id_map = IdMap::new();
437
438 {
439 let used_links_bor = &mut used_links;
440 for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) {
441 assoc_consts.extend(get_associated_constants(impl_, used_links_bor));
442 assoc_types.extend(get_associated_types(impl_, used_links_bor));
443 methods.extend(get_methods(impl_, false, used_links_bor, false, cx.tcx()));
444 }
445 assoc_consts.sort();
447 assoc_types.sort();
448 methods.sort();
449 }
450
451 let mut blocks = vec![
452 LinkBlock::new(
453 Link::new("implementations", "Associated Constants"),
454 "associatedconstant",
455 assoc_consts,
456 ),
457 LinkBlock::new(
458 Link::new("implementations", "Associated Types"),
459 "associatedtype",
460 assoc_types,
461 ),
462 LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
463 ];
464
465 if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
466 if let Some(impl_) =
467 v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
468 {
469 let mut derefs = DefIdSet::default();
470 derefs.insert(did);
471 sidebar_deref_methods(
472 cx,
473 &mut blocks,
474 impl_,
475 v,
476 &mut derefs,
477 &mut used_links,
478 deref_id_map,
479 );
480 }
481
482 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
483 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
484 let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
485 concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
486
487 sidebar_render_assoc_items(
488 cx,
489 &mut id_map,
490 concrete,
491 synthetic,
492 blanket_impl,
493 &mut blocks,
494 );
495 }
496
497 links.append(&mut blocks);
498 }
499}
500
501fn sidebar_deref_methods<'a>(
502 cx: &'a Context<'_>,
503 out: &mut Vec<LinkBlock<'a>>,
504 impl_: &Impl,
505 v: &[Impl],
506 derefs: &mut DefIdSet,
507 used_links: &mut FxHashSet<String>,
508 deref_id_map: &'a DefIdMap<String>,
509) {
510 let c = cx.cache();
511
512 debug!("found Deref: {impl_:?}");
513 if let Some((target, real_target)) =
514 impl_.inner_impl().items.iter().find_map(|item| match item.kind {
515 clean::AssocTypeItem(box ref t, _) => Some(match *t {
516 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
517 _ => (&t.type_, &t.type_),
518 }),
519 _ => None,
520 })
521 {
522 debug!("found target, real_target: {target:?} {real_target:?}");
523 if let Some(did) = target.def_id(c) &&
524 let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
525 (did == type_did || !derefs.insert(did))
527 {
528 return;
530 }
531 let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
532 let inner_impl = target
533 .def_id(c)
534 .or_else(|| {
535 target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
536 })
537 .and_then(|did| c.impls.get(&did));
538 if let Some(impls) = inner_impl {
539 debug!("found inner_impl: {impls:?}");
540 let mut ret = impls
541 .iter()
542 .filter(|i| {
543 i.inner_impl().trait_.is_none()
544 && real_target.is_doc_subtype_of(&i.inner_impl().for_, &c)
545 })
546 .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
547 .collect::<Vec<_>>();
548 if !ret.is_empty() {
549 let id = if let Some(target_def_id) = real_target.def_id(c) {
550 Cow::Borrowed(
551 deref_id_map
552 .get(&target_def_id)
553 .expect("Deref section without derived id")
554 .as_str(),
555 )
556 } else {
557 Cow::Borrowed("deref-methods")
558 };
559 let title = format!(
560 "Methods from {:#}<Target={:#}>",
561 impl_.inner_impl().trait_.as_ref().unwrap().print(cx),
562 real_target.print(cx),
563 );
564 ret.sort();
566 out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
567 }
568 }
569
570 if let Some(target_did) = target.def_id(c)
572 && let Some(target_impls) = c.impls.get(&target_did)
573 && let Some(target_deref_impl) = target_impls.iter().find(|i| {
574 i.inner_impl()
575 .trait_
576 .as_ref()
577 .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
578 .unwrap_or(false)
579 })
580 {
581 sidebar_deref_methods(
582 cx,
583 out,
584 target_deref_impl,
585 target_impls,
586 derefs,
587 used_links,
588 deref_id_map,
589 );
590 }
591 }
592}
593
594fn sidebar_enum<'a>(
595 cx: &'a Context<'_>,
596 it: &'a clean::Item,
597 e: &'a clean::Enum,
598 items: &mut Vec<LinkBlock<'a>>,
599 deref_id_map: &'a DefIdMap<String>,
600) {
601 let mut variants = e
602 .non_stripped_variants()
603 .filter_map(|v| v.name)
604 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
605 .collect::<Vec<_>>();
606 variants.sort_unstable();
607
608 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
609 sidebar_assoc_items(cx, it, items, deref_id_map);
610}
611
612pub(crate) fn sidebar_module_like(
613 item_sections_in_use: FxHashSet<ItemSection>,
614 ids: &mut IdMap,
615 module_like: ModuleLike,
616) -> LinkBlock<'static> {
617 let item_sections: Vec<Link<'_>> = ItemSection::ALL
618 .iter()
619 .copied()
620 .filter(|sec| item_sections_in_use.contains(sec))
621 .map(|sec| Link::new(ids.derive(sec.id()), sec.name()))
622 .collect();
623 let header = if let Some(first_section) = item_sections.first() {
624 Link::new(
625 first_section.href.clone(),
626 if module_like.is_crate() { "Crate Items" } else { "Module Items" },
627 )
628 } else {
629 Link::empty()
630 };
631 LinkBlock::new(header, "", item_sections)
632}
633
634fn sidebar_module(
635 items: &[clean::Item],
636 ids: &mut IdMap,
637 module_like: ModuleLike,
638) -> LinkBlock<'static> {
639 let item_sections_in_use: FxHashSet<_> = items
640 .iter()
641 .filter(|it| {
642 !it.is_stripped()
643 && it
644 .name
645 .or_else(|| {
646 if let clean::ImportItem(ref i) = it.kind
647 && let clean::ImportKind::Simple(s) = i.kind
648 {
649 Some(s)
650 } else {
651 None
652 }
653 })
654 .is_some()
655 })
656 .map(|it| item_ty_to_section(it.type_()))
657 .collect();
658
659 sidebar_module_like(item_sections_in_use, ids, module_like)
660}
661
662fn sidebar_foreign_type<'a>(
663 cx: &'a Context<'_>,
664 it: &'a clean::Item,
665 items: &mut Vec<LinkBlock<'a>>,
666 deref_id_map: &'a DefIdMap<String>,
667) {
668 sidebar_assoc_items(cx, it, items, deref_id_map);
669}
670
671fn sidebar_render_assoc_items(
673 cx: &Context<'_>,
674 id_map: &mut IdMap,
675 concrete: Vec<&Impl>,
676 synthetic: Vec<&Impl>,
677 blanket_impl: Vec<&Impl>,
678 items: &mut Vec<LinkBlock<'_>>,
679) {
680 let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
681 let mut links = FxHashSet::default();
682
683 let mut ret = impls
684 .iter()
685 .filter_map(|it| {
686 let trait_ = it.inner_impl().trait_.as_ref()?;
687 let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id));
688
689 let prefix = match it.inner_impl().polarity {
690 ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
691 ty::ImplPolarity::Negative => "!",
692 };
693 let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx)));
694 if links.insert(generated.clone()) { Some(generated) } else { None }
695 })
696 .collect::<Vec<Link<'static>>>();
697 ret.sort();
698 ret
699 };
700
701 let concrete = format_impls(concrete, id_map);
702 let synthetic = format_impls(synthetic, id_map);
703 let blanket = format_impls(blanket_impl, id_map);
704 items.extend([
705 LinkBlock::new(
706 Link::new("trait-implementations", "Trait Implementations"),
707 "trait-implementation",
708 concrete,
709 ),
710 LinkBlock::new(
711 Link::new("synthetic-implementations", "Auto Trait Implementations"),
712 "synthetic-implementation",
713 synthetic,
714 ),
715 LinkBlock::new(
716 Link::new("blanket-implementations", "Blanket Implementations"),
717 "blanket-implementation",
718 blanket,
719 ),
720 ]);
721}
722
723fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
724 if used_links.insert(url.clone()) {
725 return url;
726 }
727 let mut add = 1;
728 while !used_links.insert(format!("{url}-{add}")) {
729 add += 1;
730 }
731 format!("{url}-{add}")
732}
733
734fn get_methods<'a>(
735 i: &'a clean::Impl,
736 for_deref: bool,
737 used_links: &mut FxHashSet<String>,
738 deref_mut: bool,
739 tcx: TyCtxt<'_>,
740) -> Vec<Link<'a>> {
741 i.items
742 .iter()
743 .filter_map(|item| {
744 if let Some(ref name) = item.name
745 && item.is_method()
746 && (!for_deref || super::should_render_item(item, deref_mut, tcx))
747 {
748 Some(Link::new(
749 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)),
750 name.as_str(),
751 ))
752 } else {
753 None
754 }
755 })
756 .collect()
757}
758
759fn get_associated_constants<'a>(
760 i: &'a clean::Impl,
761 used_links: &mut FxHashSet<String>,
762) -> Vec<Link<'a>> {
763 i.items
764 .iter()
765 .filter_map(|item| {
766 if let Some(ref name) = item.name
767 && item.is_associated_const()
768 {
769 Some(Link::new(
770 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)),
771 name.as_str(),
772 ))
773 } else {
774 None
775 }
776 })
777 .collect()
778}
779
780fn get_associated_types<'a>(
781 i: &'a clean::Impl,
782 used_links: &mut FxHashSet<String>,
783) -> Vec<Link<'a>> {
784 i.items
785 .iter()
786 .filter_map(|item| {
787 if let Some(ref name) = item.name
788 && item.is_associated_type()
789 {
790 Some(Link::new(
791 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)),
792 name.as_str(),
793 ))
794 } else {
795 None
796 }
797 })
798 .collect()
799}