rustdoc/html/render/
sidebar.rs

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