Skip to main content

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