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