rustdoc/html/render/
sidebar.rs

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    /// Only create a `<section>` if there are any blocks
50    /// which should actually be rendered.
51    pub fn should_render_blocks(&self) -> bool {
52        self.blocks.iter().any(LinkBlock::should_render)
53    }
54}
55
56/// A sidebar section such as 'Methods'.
57pub(crate) struct LinkBlock<'a> {
58    /// The name of this section, e.g. 'Methods'
59    /// as well as the link to it, e.g. `#implementations`.
60    /// Will be rendered inside an `<h3>` tag
61    heading: Link<'a>,
62    class: &'static str,
63    links: Vec<Link<'a>>,
64    /// Render the heading even if there are no links
65    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/// A link to an item. Content should not be escaped.
83#[derive(PartialEq, Eq, Hash, Clone)]
84pub(crate) struct Link<'a> {
85    /// The content for the anchor tag and title attr
86    name: Cow<'a, str>,
87    /// The content for the anchor tag (if different from name)
88    name_html: Option<Cow<'a, str>>,
89    /// The id of an anchor within the page (without a `#` prefix)
90    href: Cow<'a, str>,
91    /// Nested list of links (used only in top-toc)
92    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    // The sidebar is designed to display sibling functions, modules and
161    // other miscellaneous information. since there are lots of sibling
162    // items (and that causes quadratic growth in large modules),
163    // we refactor common parts into a shared JavaScript file per module.
164    // still, we don't move everything into JS because we want to preserve
165    // as much HTML as possible in order to allow non-JS-enabled browsers
166    // to navigate the documentation (though slightly inefficiently).
167    //
168    // crate title is displayed as part of logo lockup
169    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    // need to show parent path header if:
181    //   - it's a child module, instead of the crate root
182    //   - there's a sidebar section for the item itself
183    //
184    // otherwise, the parent path header is redundant with the big crate
185    // branding area at the top of the sidebar
186    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                        // Only a single level of nesting is shown here.
254                        // Going the full six could break the layout,
255                        // so we have to cut it off somewhere.
256                        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
421/// Adds trait implementations into the blocks of links
422fn 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            // We want links' order to be reproducible so we don't use unstable sort.
446            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            // `impl Deref<Target = S> for S`
526            (did == type_did || !derefs.insert(did))
527        {
528            // Avoid infinite cycles
529            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                // We want links' order to be reproducible so we don't use unstable sort.
565                ret.sort();
566                out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
567            }
568        }
569
570        // Recurse into any further impls that might exist for `target`
571        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
671/// Renders the trait implementations for this type
672fn 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}