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::{self, ItemKind};
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        ItemKind::Struct(ref s) => sidebar_struct(cx, it, s, &mut blocks, &deref_id_map),
151        ItemKind::Trait(ref t) => sidebar_trait(cx, it, t, &mut blocks, &deref_id_map),
152        ItemKind::PrimitiveType(_) => sidebar_primitive(cx, it, &mut blocks, &deref_id_map),
153        ItemKind::Union(ref u) => sidebar_union(cx, it, u, &mut blocks, &deref_id_map),
154        ItemKind::Enum(ref e) => sidebar_enum(cx, it, e, &mut blocks, &deref_id_map),
155        ItemKind::TypeAlias(ref t) => sidebar_type_alias(cx, it, t, &mut blocks, &deref_id_map),
156        ItemKind::Module(ref m) => {
157            blocks.push(sidebar_module(&m.items, &mut ids, ModuleLike::from(it)))
158        }
159        ItemKind::ForeignType => 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                ItemKind::Module(..) => "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, ItemKind::StructField(..)))
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_tys = filter_items(&t.items, |m| m.is_assoc_ty_without_body(), "associatedtype");
314    let prov_assoc_tys = filter_items(&t.items, |m| m.is_assoc_ty_with_body(), "associatedtype");
315    let req_assoc_consts =
316        filter_items(&t.items, |m| m.is_assoc_const_without_body(), "associatedconstant");
317    let prov_assoc_consts =
318        filter_items(&t.items, |m| m.is_assoc_const_with_body(), "associatedconstant");
319    let req_assoc_fns = filter_items(&t.items, |m| m.is_assoc_fn_without_body(), "tymethod");
320    let prov_assoc_fns = filter_items(&t.items, |m| m.is_assoc_fn_with_body(), "method");
321
322    let mut foreign_impls = vec![];
323    if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
324        foreign_impls.extend(
325            implementors
326                .iter()
327                .filter(|i| !i.is_on_local_type(cx))
328                .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
329                .map(|(name, id)| Link::new(id, name)),
330        );
331        foreign_impls.sort();
332    }
333
334    blocks.extend(
335        [
336            ("required-associated-consts", "Required Associated Constants", req_assoc_consts),
337            ("provided-associated-consts", "Provided Associated Constants", prov_assoc_consts),
338            ("required-associated-types", "Required Associated Types", req_assoc_tys),
339            ("provided-associated-types", "Provided Associated Types", prov_assoc_tys),
340        ]
341        .into_iter()
342        .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)),
343    );
344
345    let tcx = cx.tcx();
346    // Because of `rustc_must_implement_one_of`, we might have a trait with no "required" assoc fns
347    // but still want the link in the sidebar, so we handle it on its own.
348    let mut req_assoc_fns_link =
349        LinkBlock::new(Link::new("required-methods", "Required Methods"), "", req_assoc_fns);
350    let rustc_middle::ty::TraitDef { must_implement_one_of, .. } = tcx.trait_def(t.def_id);
351    req_assoc_fns_link.force_render = must_implement_one_of.is_some();
352    blocks.push(req_assoc_fns_link);
353
354    blocks.push(LinkBlock::new(
355        Link::new("provided-methods", "Provided Methods"),
356        "",
357        prov_assoc_fns,
358    ));
359
360    sidebar_assoc_items(cx, it, blocks, deref_id_map);
361
362    blocks.push(LinkBlock::forced(
363        Link::new("dyn-compatibility", "Dyn Compatibility"),
364        "dyn-compatibility-note",
365    ));
366
367    blocks.push(LinkBlock::new(
368        Link::new("foreign-impls", "Implementations on Foreign Types"),
369        "",
370        foreign_impls,
371    ));
372
373    blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
374    if t.is_auto(tcx) {
375        blocks.push(LinkBlock::forced(
376            Link::new("synthetic-implementors", "Auto Implementors"),
377            "impl-auto",
378        ));
379    }
380}
381
382fn sidebar_primitive<'a>(
383    cx: &'a Context<'_>,
384    it: &'a clean::Item,
385    items: &mut Vec<LinkBlock<'a>>,
386    deref_id_map: &'a DefIdMap<String>,
387) {
388    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
389        sidebar_assoc_items(cx, it, items, deref_id_map);
390    } else {
391        let (concrete, synthetic, blanket_impl) =
392            super::get_filtered_impls_for_reference(&cx.shared, it);
393
394        sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl, items);
395    }
396}
397
398fn sidebar_type_alias<'a>(
399    cx: &'a Context<'_>,
400    it: &'a clean::Item,
401    t: &'a clean::TypeAlias,
402    items: &mut Vec<LinkBlock<'a>>,
403    deref_id_map: &'a DefIdMap<String>,
404) {
405    if let Some(inner_type) = &t.inner_type {
406        items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased Type"), "type"));
407        match inner_type {
408            clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
409                let mut variants = variants
410                    .iter()
411                    .filter(|i| !i.is_stripped())
412                    .filter_map(|v| v.name)
413                    .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
414                    .collect::<Vec<_>>();
415                variants.sort_unstable();
416
417                items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
418            }
419            clean::TypeAliasInnerType::Union { fields }
420            | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
421                let fields = get_struct_fields_name(fields);
422                items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
423            }
424        }
425    }
426    sidebar_assoc_items(cx, it, items, deref_id_map);
427}
428
429fn sidebar_union<'a>(
430    cx: &'a Context<'_>,
431    it: &'a clean::Item,
432    u: &'a clean::Union,
433    items: &mut Vec<LinkBlock<'a>>,
434    deref_id_map: &'a DefIdMap<String>,
435) {
436    let fields = get_struct_fields_name(&u.fields);
437    items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields));
438    sidebar_assoc_items(cx, it, items, deref_id_map);
439}
440
441/// Adds trait implementations into the blocks of links
442fn sidebar_assoc_items<'a>(
443    cx: &'a Context<'_>,
444    it: &'a clean::Item,
445    links: &mut Vec<LinkBlock<'a>>,
446    deref_id_map: &'a DefIdMap<String>,
447) {
448    let did = it.item_id.expect_def_id();
449    let cache = cx.cache();
450
451    let mut assoc_consts = Vec::new();
452    let mut assoc_types = Vec::new();
453    let mut assoc_fns = Vec::new();
454    let mut methods = Vec::new();
455    if let Some(v) = cache.impls.get(&did) {
456        let mut used_links = UsedLinks::default();
457        let mut id_map = IdMap::new();
458
459        {
460            let used_links_bor = &mut used_links;
461            for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) {
462                assoc_consts.extend(get_associated_constants(impl_, used_links_bor));
463                assoc_types.extend(get_associated_types(impl_, used_links_bor));
464                methods.extend(get_methods(
465                    impl_,
466                    GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns },
467                    used_links_bor,
468                    cx,
469                ));
470            }
471            // We want links' order to be reproducible so we don't use unstable sort.
472            assoc_consts.sort();
473            assoc_types.sort();
474            postprocess_method_list(&mut assoc_fns);
475            postprocess_method_list(&mut methods);
476        }
477
478        let mut blocks = vec![
479            LinkBlock::new(
480                Link::new("implementations", "Associated Constants"),
481                "associatedconstant",
482                assoc_consts,
483            ),
484            LinkBlock::new(
485                Link::new("implementations", "Associated Types"),
486                "associatedtype",
487                assoc_types,
488            ),
489            LinkBlock::new(
490                Link::new("implementations", "Associated Functions"),
491                "method",
492                assoc_fns,
493            ),
494            LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
495        ];
496
497        if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
498            if let Some(impl_) = v.iter().find(|i| {
499                i.trait_did() == cx.tcx().lang_items().deref_trait() && !i.is_negative_trait_impl()
500            }) {
501                let mut derefs = DefIdSet::default();
502                derefs.insert(did);
503                sidebar_deref_methods(
504                    cx,
505                    &mut blocks,
506                    impl_,
507                    v,
508                    &mut derefs,
509                    &mut used_links,
510                    deref_id_map,
511                );
512            }
513
514            let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
515                v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
516            let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
517                concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
518
519            sidebar_render_assoc_items(
520                cx,
521                &mut id_map,
522                concrete,
523                synthetic,
524                blanket_impl,
525                &mut blocks,
526            );
527        }
528
529        links.append(&mut blocks);
530    }
531}
532
533fn sidebar_deref_methods<'a>(
534    cx: &'a Context<'_>,
535    out: &mut Vec<LinkBlock<'a>>,
536    impl_: &Impl,
537    v: &[Impl],
538    derefs: &mut DefIdSet,
539    used_links: &mut UsedLinks,
540    deref_id_map: &'a DefIdMap<String>,
541) {
542    let c = cx.cache();
543
544    debug!("found Deref: {impl_:?}");
545    if let Some((target, real_target)) =
546        impl_.inner_impl().items.iter().find_map(|item| match &item.kind {
547            ItemKind::AssocType(clean::AssocType { type_: Some(type_), .. }) => {
548                Some((type_.middle_type.as_ref().unwrap_or(&type_.type_), &type_.type_))
549            }
550            _ => None,
551        })
552    {
553        debug!("found target, real_target: {target:?} {real_target:?}");
554        if let Some(did) = target.def_id(c) &&
555            let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
556            // `impl Deref<Target = S> for S`
557            (did == type_did || !derefs.insert(did))
558        {
559            // Avoid infinite cycles
560            return;
561        }
562        let tcx = cx.tcx();
563        let deref_mut = v.iter().any(|i| i.trait_did() == tcx.lang_items().deref_mut_trait());
564        let inner_impl = target
565            .def_id(c)
566            .or_else(|| {
567                target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
568            })
569            .and_then(|did| c.impls.get(&did));
570        if let Some(impls) = inner_impl {
571            debug!("found inner_impl: {impls:?}");
572            let is_deref_target_copy =
573                super::compute_if_deref_target_implements_copy(tcx, impl_.def_id());
574            let mut ret = impls
575                .iter()
576                .filter(|i| {
577                    i.inner_impl().trait_.is_none()
578                        && real_target.is_doc_subtype_of(&i.inner_impl().for_, c)
579                })
580                .flat_map(|i| {
581                    get_methods(
582                        i.inner_impl(),
583                        GetMethodsMode::Deref { deref_mut, is_deref_target_copy },
584                        used_links,
585                        cx,
586                    )
587                    .collect::<Vec<_>>()
588                })
589                .collect::<Vec<_>>();
590            if !ret.is_empty() {
591                let id = if let Some(target_def_id) = real_target.def_id(c) {
592                    Cow::Borrowed(
593                        deref_id_map
594                            .get(&target_def_id)
595                            .expect("Deref section without derived id")
596                            .as_str(),
597                    )
598                } else {
599                    Cow::Borrowed("deref-methods")
600                };
601                let title = format!(
602                    "Methods from {:#}<Target={:#}>",
603                    print_path(impl_.inner_impl().trait_.as_ref().unwrap(), cx),
604                    print_type(real_target, cx),
605                );
606
607                postprocess_method_list(&mut ret);
608                out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
609            }
610        }
611
612        // Recurse into any further impls that might exist for `target`
613        if let Some(target_did) = target.def_id(c)
614            && let Some(target_impls) = c.impls.get(&target_did)
615            && let Some(target_deref_impl) = target_impls.iter().find(|i| {
616                i.inner_impl()
617                    .trait_
618                    .as_ref()
619                    .map(|t| Some(t.def_id()) == tcx.lang_items().deref_trait())
620                    .unwrap_or(false)
621                    && !i.is_negative_trait_impl()
622            })
623        {
624            sidebar_deref_methods(
625                cx,
626                out,
627                target_deref_impl,
628                target_impls,
629                derefs,
630                used_links,
631                deref_id_map,
632            );
633        }
634    }
635}
636
637fn sidebar_enum<'a>(
638    cx: &'a Context<'_>,
639    it: &'a clean::Item,
640    e: &'a clean::Enum,
641    items: &mut Vec<LinkBlock<'a>>,
642    deref_id_map: &'a DefIdMap<String>,
643) {
644    let mut variants = e
645        .non_stripped_variants()
646        .filter_map(|v| v.name)
647        .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
648        .collect::<Vec<_>>();
649    variants.sort_unstable();
650
651    items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
652    sidebar_assoc_items(cx, it, items, deref_id_map);
653}
654
655pub(crate) fn sidebar_module_like(
656    item_sections_in_use: FxHashSet<ItemSection>,
657    ids: &mut IdMap,
658    module_like: ModuleLike,
659) -> LinkBlock<'static> {
660    let item_sections: Vec<Link<'_>> = ItemSection::ALL
661        .iter()
662        .copied()
663        .filter(|sec| item_sections_in_use.contains(sec))
664        .map(|sec| Link::new(ids.derive(sec.id()), sec.name()))
665        .collect();
666    let header = if let Some(first_section) = item_sections.first() {
667        Link::new(
668            first_section.href.clone(),
669            if module_like.is_crate() { "Crate Items" } else { "Module Items" },
670        )
671    } else {
672        Link::empty()
673    };
674    LinkBlock::new(header, "", item_sections)
675}
676
677fn sidebar_module(
678    items: &[clean::Item],
679    ids: &mut IdMap,
680    module_like: ModuleLike,
681) -> LinkBlock<'static> {
682    let mut item_sections_in_use: FxHashSet<_> = Default::default();
683
684    for item in items.iter().filter(|it| {
685        !it.is_stripped()
686            && it
687                .name
688                .or_else(|| {
689                    if let ItemKind::Import(ref i) = it.kind
690                        && let clean::ImportKind::Simple(s) = i.kind
691                    {
692                        Some(s)
693                    } else {
694                        None
695                    }
696                })
697                .is_some()
698    }) {
699        for type_ in item.types() {
700            item_sections_in_use.insert(item_ty_to_section(type_));
701        }
702    }
703
704    sidebar_module_like(item_sections_in_use, ids, module_like)
705}
706
707fn sidebar_foreign_type<'a>(
708    cx: &'a Context<'_>,
709    it: &'a clean::Item,
710    items: &mut Vec<LinkBlock<'a>>,
711    deref_id_map: &'a DefIdMap<String>,
712) {
713    sidebar_assoc_items(cx, it, items, deref_id_map);
714}
715
716/// Renders the trait implementations for this type
717fn sidebar_render_assoc_items(
718    cx: &Context<'_>,
719    id_map: &mut IdMap,
720    concrete: Vec<&Impl>,
721    synthetic: Vec<&Impl>,
722    blanket_impl: Vec<&Impl>,
723    items: &mut Vec<LinkBlock<'_>>,
724) {
725    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
726        let mut links = FxHashSet::default();
727
728        let mut ret = impls
729            .iter()
730            .filter_map(|i| {
731                let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), i.impl_item.item_id));
732                let generated = Link::new(encoded, impl_trait_key(cx, i)?);
733                if links.insert(generated.clone()) { Some(generated) } else { None }
734            })
735            .collect::<Vec<Link<'static>>>();
736        ret.sort();
737        ret
738    };
739
740    let concrete = format_impls(concrete, id_map);
741    let synthetic = format_impls(synthetic, id_map);
742    let blanket = format_impls(blanket_impl, id_map);
743    items.extend([
744        LinkBlock::new(
745            Link::new("trait-implementations", "Trait Implementations"),
746            "trait-implementation",
747            concrete,
748        ),
749        LinkBlock::new(
750            Link::new("synthetic-implementations", "Auto Trait Implementations"),
751            "synthetic-implementation",
752            synthetic,
753        ),
754        LinkBlock::new(
755            Link::new("blanket-implementations", "Blanket Implementations"),
756            "blanket-implementation",
757            blanket,
758        ),
759    ]);
760}
761
762/// Tracks sidebar link anchors so duplicates get unique names.
763type UsedLinks = FxHashMap<String, usize>;
764
765fn get_next_url(used_links: &mut UsedLinks, url: String) -> String {
766    let count = used_links.entry(url.clone()).or_insert(0);
767    let res = if *count == 0 { url } else { format!("{url}-{count}") };
768    *count += 1;
769    res
770}
771
772enum GetMethodsMode<'r, 'l> {
773    Deref { deref_mut: bool, is_deref_target_copy: bool },
774    AlsoCollectAssocFns { assoc_fns: &'r mut Vec<Link<'l>> },
775}
776
777fn get_methods<'a>(
778    i: &'a clean::Impl,
779    mut mode: GetMethodsMode<'_, 'a>,
780    used_links: &mut UsedLinks,
781    cx: &Context<'_>,
782) -> impl Iterator<Item = Link<'a>> {
783    i.items.iter().filter_map(move |item| {
784        if let Some(ref name) = item.name
785            && item.is_assoc_fn_with_body()
786        {
787            let mut build_link = || {
788                Link::new(
789                    get_next_url(
790                        used_links,
791                        format!("{typ}.{name}", typ = ItemType::AssocFnWithBody),
792                    ),
793                    format!("{} ({:#})", name.as_str(), print_type(&i.for_, cx)),
794                )
795            };
796            match &mut mode {
797                &mut GetMethodsMode::Deref { deref_mut, is_deref_target_copy } => {
798                    if super::should_render_item_for_deref(
799                        item,
800                        deref_mut,
801                        cx.tcx(),
802                        is_deref_target_copy,
803                    ) {
804                        Some(build_link())
805                    } else {
806                        None
807                    }
808                }
809                GetMethodsMode::AlsoCollectAssocFns { assoc_fns } => {
810                    if item.has_self_param() {
811                        Some(build_link())
812                    } else {
813                        assoc_fns.push(build_link());
814                        None
815                    }
816                }
817            }
818        } else {
819            None
820        }
821    })
822}
823
824fn postprocess_method_list(methods: &mut [Link<'_>]) {
825    // We want links' order to be reproducible so we don't use unstable sort.
826    methods.sort();
827    let trunc_to = |dst: &mut Cow<'_, str>, len: usize| match dst {
828        Cow::Borrowed(s) => *s = &s[..len],
829        Cow::Owned(s) => s.truncate(len),
830    };
831    fn get_short_name<'a>(itm: Option<&'a Link<'a>>) -> Option<&'a str> {
832        Some(itm?.name.split_once(' ')?.0)
833    }
834    for i in 0..methods.len() {
835        let short_name = get_short_name(methods.get(i));
836        if get_short_name(methods.get(i.wrapping_sub(1))) != short_name
837            && short_name != get_short_name(methods.get(i + 1))
838        {
839            let shortened_len = short_name.unwrap().len();
840            trunc_to(&mut methods[i].name, shortened_len);
841        }
842    }
843}
844
845fn get_associated_constants<'a>(
846    i: &'a clean::Impl,
847    used_links: &mut UsedLinks,
848) -> impl Iterator<Item = Link<'a>> {
849    i.items.iter().filter_map(|item| {
850        if let Some(ref name) = item.name
851            && item.is_assoc_const_with_body()
852        {
853            Some(Link::new(
854                get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)),
855                name.as_str(),
856            ))
857        } else {
858            None
859        }
860    })
861}
862
863fn get_associated_types<'a>(
864    i: &'a clean::Impl,
865    used_links: &mut UsedLinks,
866) -> impl Iterator<Item = Link<'a>> {
867    i.items.iter().filter_map(|item| {
868        if let Some(ref name) = item.name
869            && item.is_assoc_ty_with_body()
870        {
871            Some(Link::new(
872                get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)),
873                name.as_str(),
874            ))
875        } else {
876            None
877        }
878    })
879}