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