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