rustdoc/html/render/
sidebar.rs

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