| use std::borrow::Cow; |
| use std::rc::Rc; |
| |
| use rinja::Template; |
| use rustc_data_structures::fx::FxHashSet; |
| use rustc_hir::def::CtorKind; |
| use rustc_hir::def_id::DefIdSet; |
| use rustc_middle::ty::{self, TyCtxt}; |
| use tracing::debug; |
| |
| use super::{Context, ItemSection, item_ty_to_section}; |
| use crate::clean; |
| use crate::formats::Impl; |
| use crate::formats::item_type::ItemType; |
| use crate::html::format::Buffer; |
| use crate::html::markdown::{IdMap, MarkdownWithToc}; |
| |
| #[derive(Clone, Copy)] |
| pub(crate) enum ModuleLike { |
| Module, |
| Crate, |
| } |
| |
| impl ModuleLike { |
| pub(crate) fn is_crate(self) -> bool { |
| matches!(self, ModuleLike::Crate) |
| } |
| } |
| impl<'a> From<&'a clean::Item> for ModuleLike { |
| fn from(it: &'a clean::Item) -> ModuleLike { |
| if it.is_crate() { ModuleLike::Crate } else { ModuleLike::Module } |
| } |
| } |
| |
| #[derive(Template)] |
| #[template(path = "sidebar.html")] |
| pub(super) struct Sidebar<'a> { |
| pub(super) title_prefix: &'static str, |
| pub(super) title: &'a str, |
| pub(super) is_crate: bool, |
| pub(super) parent_is_crate: bool, |
| pub(super) is_mod: bool, |
| pub(super) blocks: Vec<LinkBlock<'a>>, |
| pub(super) path: String, |
| } |
| |
| impl<'a> Sidebar<'a> { |
| /// Only create a `<section>` if there are any blocks |
| /// which should actually be rendered. |
| pub fn should_render_blocks(&self) -> bool { |
| self.blocks.iter().any(LinkBlock::should_render) |
| } |
| } |
| |
| /// A sidebar section such as 'Methods'. |
| pub(crate) struct LinkBlock<'a> { |
| /// The name of this section, e.g. 'Methods' |
| /// as well as the link to it, e.g. `#implementations`. |
| /// Will be rendered inside an `<h3>` tag |
| heading: Link<'a>, |
| class: &'static str, |
| links: Vec<Link<'a>>, |
| /// Render the heading even if there are no links |
| force_render: bool, |
| } |
| |
| impl<'a> LinkBlock<'a> { |
| pub fn new(heading: Link<'a>, class: &'static str, links: Vec<Link<'a>>) -> Self { |
| Self { heading, links, class, force_render: false } |
| } |
| |
| pub fn forced(heading: Link<'a>, class: &'static str) -> Self { |
| Self { heading, links: vec![], class, force_render: true } |
| } |
| |
| pub fn should_render(&self) -> bool { |
| self.force_render || !self.links.is_empty() |
| } |
| } |
| |
| /// A link to an item. Content should not be escaped. |
| #[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] |
| pub(crate) struct Link<'a> { |
| /// The content for the anchor tag and title attr |
| name: Cow<'a, str>, |
| /// The content for the anchor tag (if different from name) |
| name_html: Option<Cow<'a, str>>, |
| /// The id of an anchor within the page (without a `#` prefix) |
| href: Cow<'a, str>, |
| /// Nested list of links (used only in top-toc) |
| children: Vec<Link<'a>>, |
| } |
| |
| impl<'a> Link<'a> { |
| pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self { |
| Self { href: href.into(), name: name.into(), children: vec![], name_html: None } |
| } |
| pub fn empty() -> Link<'static> { |
| Link::new("", "") |
| } |
| } |
| |
| pub(crate) mod filters { |
| use std::fmt::Display; |
| |
| use rinja::filters::Safe; |
| |
| use crate::html::escape::EscapeBodyTextWithWbr; |
| use crate::html::render::display_fn; |
| pub(crate) fn wrapped<T>(v: T) -> rinja::Result<Safe<impl Display>> |
| where |
| T: Display, |
| { |
| let string = v.to_string(); |
| Ok(Safe(display_fn(move |f| EscapeBodyTextWithWbr(&string).fmt(f)))) |
| } |
| } |
| |
| pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { |
| let mut ids = IdMap::new(); |
| let mut blocks: Vec<LinkBlock<'_>> = docblock_toc(cx, it, &mut ids).into_iter().collect(); |
| match it.kind { |
| clean::StructItem(ref s) => sidebar_struct(cx, it, s, &mut blocks), |
| clean::TraitItem(ref t) => sidebar_trait(cx, it, t, &mut blocks), |
| clean::PrimitiveItem(_) => sidebar_primitive(cx, it, &mut blocks), |
| clean::UnionItem(ref u) => sidebar_union(cx, it, u, &mut blocks), |
| clean::EnumItem(ref e) => sidebar_enum(cx, it, e, &mut blocks), |
| clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t, &mut blocks), |
| clean::ModuleItem(ref m) => { |
| blocks.push(sidebar_module(&m.items, &mut ids, ModuleLike::from(it))) |
| } |
| clean::ForeignTypeItem => sidebar_foreign_type(cx, it, &mut blocks), |
| _ => {} |
| } |
| // The sidebar is designed to display sibling functions, modules and |
| // other miscellaneous information. since there are lots of sibling |
| // items (and that causes quadratic growth in large modules), |
| // we refactor common parts into a shared JavaScript file per module. |
| // still, we don't move everything into JS because we want to preserve |
| // as much HTML as possible in order to allow non-JS-enabled browsers |
| // to navigate the documentation (though slightly inefficiently). |
| // |
| // crate title is displayed as part of logo lockup |
| let (title_prefix, title) = if !blocks.is_empty() && !it.is_crate() { |
| ( |
| match it.kind { |
| clean::ModuleItem(..) => "Module ", |
| _ => "", |
| }, |
| it.name.as_ref().unwrap().as_str(), |
| ) |
| } else { |
| ("", "") |
| }; |
| // need to show parent path header if: |
| // - it's a child module, instead of the crate root |
| // - there's a sidebar section for the item itself |
| // |
| // otherwise, the parent path header is redundant with the big crate |
| // branding area at the top of the sidebar |
| let sidebar_path = |
| if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] }; |
| let path: String = if sidebar_path.len() > 1 || !title.is_empty() { |
| let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect(); |
| if sidebar_path.len() == 1 { format!("crate {path}") } else { path } |
| } else { |
| "".into() |
| }; |
| let sidebar = Sidebar { |
| title_prefix, |
| title, |
| is_mod: it.is_mod(), |
| is_crate: it.is_crate(), |
| parent_is_crate: sidebar_path.len() == 1, |
| blocks, |
| path, |
| }; |
| sidebar.render_into(buffer).unwrap(); |
| } |
| |
| fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> { |
| let mut fields = fields |
| .iter() |
| .filter(|f| matches!(f.kind, clean::StructFieldItem(..))) |
| .filter_map(|f| { |
| f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str())) |
| }) |
| .collect::<Vec<Link<'a>>>(); |
| fields.sort(); |
| fields |
| } |
| |
| fn docblock_toc<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| ids: &mut IdMap, |
| ) -> Option<LinkBlock<'a>> { |
| let (toc, _) = MarkdownWithToc { |
| content: &it.doc_value(), |
| links: &it.links(cx), |
| ids, |
| error_codes: cx.shared.codes, |
| edition: cx.shared.edition(), |
| playground: &cx.shared.playground, |
| } |
| .into_parts(); |
| let links: Vec<Link<'_>> = toc |
| .entries |
| .into_iter() |
| .map(|entry| { |
| Link { |
| name_html: if entry.html == entry.name { None } else { Some(entry.html.into()) }, |
| name: entry.name.into(), |
| href: entry.id.into(), |
| children: entry |
| .children |
| .entries |
| .into_iter() |
| .map(|entry| Link { |
| name_html: if entry.html == entry.name { |
| None |
| } else { |
| Some(entry.html.into()) |
| }, |
| name: entry.name.into(), |
| href: entry.id.into(), |
| // Only a single level of nesting is shown here. |
| // Going the full six could break the layout, |
| // so we have to cut it off somewhere. |
| children: vec![], |
| }) |
| .collect(), |
| } |
| }) |
| .collect(); |
| if links.is_empty() { |
| None |
| } else { |
| Some(LinkBlock::new(Link::new("", "Sections"), "top-toc", links)) |
| } |
| } |
| |
| fn sidebar_struct<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| s: &'a clean::Struct, |
| items: &mut Vec<LinkBlock<'a>>, |
| ) { |
| let fields = get_struct_fields_name(&s.fields); |
| let field_name = match s.ctor_kind { |
| Some(CtorKind::Fn) => Some("Tuple Fields"), |
| None => Some("Fields"), |
| _ => None, |
| }; |
| if let Some(name) = field_name { |
| items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields)); |
| } |
| sidebar_assoc_items(cx, it, items); |
| } |
| |
| fn sidebar_trait<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| t: &'a clean::Trait, |
| blocks: &mut Vec<LinkBlock<'a>>, |
| ) { |
| fn filter_items<'a>( |
| items: &'a [clean::Item], |
| filt: impl Fn(&clean::Item) -> bool, |
| ty: &str, |
| ) -> Vec<Link<'a>> { |
| let mut res = items |
| .iter() |
| .filter_map(|m: &clean::Item| match m.name { |
| Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())), |
| _ => None, |
| }) |
| .collect::<Vec<Link<'a>>>(); |
| res.sort(); |
| res |
| } |
| |
| let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype"); |
| let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype"); |
| let req_assoc_const = |
| filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant"); |
| let prov_assoc_const = |
| filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant"); |
| let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod"); |
| let prov_method = filter_items(&t.items, |m| m.is_method(), "method"); |
| let mut foreign_impls = vec![]; |
| if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) { |
| foreign_impls.extend( |
| implementors |
| .iter() |
| .filter(|i| !i.is_on_local_type(cx)) |
| .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx)) |
| .map(|(name, id)| Link::new(id, name)), |
| ); |
| foreign_impls.sort(); |
| } |
| |
| blocks.extend( |
| [ |
| ("required-associated-consts", "Required Associated Constants", req_assoc_const), |
| ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const), |
| ("required-associated-types", "Required Associated Types", req_assoc), |
| ("provided-associated-types", "Provided Associated Types", prov_assoc), |
| ("required-methods", "Required Methods", req_method), |
| ("provided-methods", "Provided Methods", prov_method), |
| ("foreign-impls", "Implementations on Foreign Types", foreign_impls), |
| ] |
| .into_iter() |
| .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)), |
| ); |
| sidebar_assoc_items(cx, it, blocks); |
| |
| if !t.is_object_safe(cx.tcx()) { |
| blocks.push(LinkBlock::forced( |
| Link::new("object-safety", "Object Safety"), |
| "object-safety-note", |
| )); |
| } |
| |
| blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl")); |
| if t.is_auto(cx.tcx()) { |
| blocks.push(LinkBlock::forced( |
| Link::new("synthetic-implementors", "Auto Implementors"), |
| "impl-auto", |
| )); |
| } |
| } |
| |
| fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item, items: &mut Vec<LinkBlock<'a>>) { |
| if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { |
| sidebar_assoc_items(cx, it, items); |
| } else { |
| let shared = Rc::clone(&cx.shared); |
| let (concrete, synthetic, blanket_impl) = |
| super::get_filtered_impls_for_reference(&shared, it); |
| |
| sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl, items); |
| } |
| } |
| |
| fn sidebar_type_alias<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| t: &'a clean::TypeAlias, |
| items: &mut Vec<LinkBlock<'a>>, |
| ) { |
| if let Some(inner_type) = &t.inner_type { |
| items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type")); |
| match inner_type { |
| clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => { |
| let mut variants = variants |
| .iter() |
| .filter(|i| !i.is_stripped()) |
| .filter_map(|v| v.name) |
| .map(|name| Link::new(format!("variant.{name}"), name.to_string())) |
| .collect::<Vec<_>>(); |
| variants.sort_unstable(); |
| |
| items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)); |
| } |
| clean::TypeAliasInnerType::Union { fields } |
| | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => { |
| let fields = get_struct_fields_name(fields); |
| items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields)); |
| } |
| } |
| } |
| sidebar_assoc_items(cx, it, items); |
| } |
| |
| fn sidebar_union<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| u: &'a clean::Union, |
| items: &mut Vec<LinkBlock<'a>>, |
| ) { |
| let fields = get_struct_fields_name(&u.fields); |
| items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)); |
| sidebar_assoc_items(cx, it, items); |
| } |
| |
| /// Adds trait implementations into the blocks of links |
| fn sidebar_assoc_items<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| links: &mut Vec<LinkBlock<'a>>, |
| ) { |
| let did = it.item_id.expect_def_id(); |
| let cache = cx.cache(); |
| |
| let mut assoc_consts = Vec::new(); |
| let mut assoc_types = Vec::new(); |
| let mut methods = Vec::new(); |
| if let Some(v) = cache.impls.get(&did) { |
| let mut used_links = FxHashSet::default(); |
| let mut id_map = IdMap::new(); |
| |
| { |
| let used_links_bor = &mut used_links; |
| for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) { |
| assoc_consts.extend(get_associated_constants(impl_, used_links_bor)); |
| assoc_types.extend(get_associated_types(impl_, used_links_bor)); |
| methods.extend(get_methods(impl_, false, used_links_bor, false, cx.tcx())); |
| } |
| // We want links' order to be reproducible so we don't use unstable sort. |
| assoc_consts.sort(); |
| assoc_types.sort(); |
| methods.sort(); |
| } |
| |
| let mut blocks = vec![ |
| LinkBlock::new( |
| Link::new("implementations", "Associated Constants"), |
| "associatedconstant", |
| assoc_consts, |
| ), |
| LinkBlock::new( |
| Link::new("implementations", "Associated Types"), |
| "associatedtype", |
| assoc_types, |
| ), |
| LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), |
| ]; |
| |
| if v.iter().any(|i| i.inner_impl().trait_.is_some()) { |
| if let Some(impl_) = |
| v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) |
| { |
| let mut derefs = DefIdSet::default(); |
| derefs.insert(did); |
| sidebar_deref_methods(cx, &mut blocks, impl_, v, &mut derefs, &mut used_links); |
| } |
| |
| let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = |
| v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto()); |
| let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = |
| concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket()); |
| |
| sidebar_render_assoc_items( |
| cx, |
| &mut id_map, |
| concrete, |
| synthetic, |
| blanket_impl, |
| &mut blocks, |
| ); |
| } |
| |
| links.append(&mut blocks); |
| } |
| } |
| |
| fn sidebar_deref_methods<'a>( |
| cx: &'a Context<'_>, |
| out: &mut Vec<LinkBlock<'a>>, |
| impl_: &Impl, |
| v: &[Impl], |
| derefs: &mut DefIdSet, |
| used_links: &mut FxHashSet<String>, |
| ) { |
| let c = cx.cache(); |
| |
| debug!("found Deref: {impl_:?}"); |
| if let Some((target, real_target)) = |
| impl_.inner_impl().items.iter().find_map(|item| match item.kind { |
| clean::AssocTypeItem(box ref t, _) => Some(match *t { |
| clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_), |
| _ => (&t.type_, &t.type_), |
| }), |
| _ => None, |
| }) |
| { |
| debug!("found target, real_target: {target:?} {real_target:?}"); |
| if let Some(did) = target.def_id(c) && |
| let Some(type_did) = impl_.inner_impl().for_.def_id(c) && |
| // `impl Deref<Target = S> for S` |
| (did == type_did || !derefs.insert(did)) |
| { |
| // Avoid infinite cycles |
| return; |
| } |
| let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); |
| let inner_impl = target |
| .def_id(c) |
| .or_else(|| { |
| target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned()) |
| }) |
| .and_then(|did| c.impls.get(&did)); |
| if let Some(impls) = inner_impl { |
| debug!("found inner_impl: {impls:?}"); |
| let mut ret = impls |
| .iter() |
| .filter(|i| i.inner_impl().trait_.is_none()) |
| .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) |
| .collect::<Vec<_>>(); |
| if !ret.is_empty() { |
| let id = if let Some(target_def_id) = real_target.def_id(c) { |
| Cow::Borrowed( |
| cx.deref_id_map |
| .get(&target_def_id) |
| .expect("Deref section without derived id") |
| .as_str(), |
| ) |
| } else { |
| Cow::Borrowed("deref-methods") |
| }; |
| let title = format!( |
| "Methods from {:#}<Target={:#}>", |
| impl_.inner_impl().trait_.as_ref().unwrap().print(cx), |
| real_target.print(cx), |
| ); |
| // We want links' order to be reproducible so we don't use unstable sort. |
| ret.sort(); |
| out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret)); |
| } |
| } |
| |
| // Recurse into any further impls that might exist for `target` |
| if let Some(target_did) = target.def_id(c) |
| && let Some(target_impls) = c.impls.get(&target_did) |
| && let Some(target_deref_impl) = target_impls.iter().find(|i| { |
| i.inner_impl() |
| .trait_ |
| .as_ref() |
| .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) |
| .unwrap_or(false) |
| }) |
| { |
| sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs, used_links); |
| } |
| } |
| } |
| |
| fn sidebar_enum<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| e: &'a clean::Enum, |
| items: &mut Vec<LinkBlock<'a>>, |
| ) { |
| let mut variants = e |
| .variants() |
| .filter_map(|v| v.name) |
| .map(|name| Link::new(format!("variant.{name}"), name.to_string())) |
| .collect::<Vec<_>>(); |
| variants.sort_unstable(); |
| |
| items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)); |
| sidebar_assoc_items(cx, it, items); |
| } |
| |
| pub(crate) fn sidebar_module_like( |
| item_sections_in_use: FxHashSet<ItemSection>, |
| ids: &mut IdMap, |
| module_like: ModuleLike, |
| ) -> LinkBlock<'static> { |
| let item_sections: Vec<Link<'_>> = ItemSection::ALL |
| .iter() |
| .copied() |
| .filter(|sec| item_sections_in_use.contains(sec)) |
| .map(|sec| Link::new(ids.derive(sec.id()), sec.name())) |
| .collect(); |
| let header = if let Some(first_section) = item_sections.get(0) { |
| Link::new( |
| first_section.href.to_owned(), |
| if module_like.is_crate() { "Crate Items" } else { "Module Items" }, |
| ) |
| } else { |
| Link::empty() |
| }; |
| LinkBlock::new(header, "", item_sections) |
| } |
| |
| fn sidebar_module( |
| items: &[clean::Item], |
| ids: &mut IdMap, |
| module_like: ModuleLike, |
| ) -> LinkBlock<'static> { |
| let item_sections_in_use: FxHashSet<_> = items |
| .iter() |
| .filter(|it| { |
| !it.is_stripped() |
| && it |
| .name |
| .or_else(|| { |
| if let clean::ImportItem(ref i) = it.kind |
| && let clean::ImportKind::Simple(s) = i.kind |
| { |
| Some(s) |
| } else { |
| None |
| } |
| }) |
| .is_some() |
| }) |
| .map(|it| item_ty_to_section(it.type_())) |
| .collect(); |
| |
| sidebar_module_like(item_sections_in_use, ids, module_like) |
| } |
| |
| fn sidebar_foreign_type<'a>( |
| cx: &'a Context<'_>, |
| it: &'a clean::Item, |
| items: &mut Vec<LinkBlock<'a>>, |
| ) { |
| sidebar_assoc_items(cx, it, items); |
| } |
| |
| /// Renders the trait implementations for this type |
| fn sidebar_render_assoc_items( |
| cx: &Context<'_>, |
| id_map: &mut IdMap, |
| concrete: Vec<&Impl>, |
| synthetic: Vec<&Impl>, |
| blanket_impl: Vec<&Impl>, |
| items: &mut Vec<LinkBlock<'_>>, |
| ) { |
| let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { |
| let mut links = FxHashSet::default(); |
| |
| let mut ret = impls |
| .iter() |
| .filter_map(|it| { |
| let trait_ = it.inner_impl().trait_.as_ref()?; |
| let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id)); |
| |
| let prefix = match it.inner_impl().polarity { |
| ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", |
| ty::ImplPolarity::Negative => "!", |
| }; |
| let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx))); |
| if links.insert(generated.clone()) { Some(generated) } else { None } |
| }) |
| .collect::<Vec<Link<'static>>>(); |
| ret.sort(); |
| ret |
| }; |
| |
| let concrete = format_impls(concrete, id_map); |
| let synthetic = format_impls(synthetic, id_map); |
| let blanket = format_impls(blanket_impl, id_map); |
| items.extend([ |
| LinkBlock::new( |
| Link::new("trait-implementations", "Trait Implementations"), |
| "trait-implementation", |
| concrete, |
| ), |
| LinkBlock::new( |
| Link::new("synthetic-implementations", "Auto Trait Implementations"), |
| "synthetic-implementation", |
| synthetic, |
| ), |
| LinkBlock::new( |
| Link::new("blanket-implementations", "Blanket Implementations"), |
| "blanket-implementation", |
| blanket, |
| ), |
| ]); |
| } |
| |
| fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String { |
| if used_links.insert(url.clone()) { |
| return url; |
| } |
| let mut add = 1; |
| while !used_links.insert(format!("{url}-{add}")) { |
| add += 1; |
| } |
| format!("{url}-{add}") |
| } |
| |
| fn get_methods<'a>( |
| i: &'a clean::Impl, |
| for_deref: bool, |
| used_links: &mut FxHashSet<String>, |
| deref_mut: bool, |
| tcx: TyCtxt<'_>, |
| ) -> Vec<Link<'a>> { |
| i.items |
| .iter() |
| .filter_map(|item| match item.name { |
| Some(ref name) if !name.is_empty() && item.is_method() => { |
| if !for_deref || super::should_render_item(item, deref_mut, tcx) { |
| Some(Link::new( |
| get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)), |
| name.as_str(), |
| )) |
| } else { |
| None |
| } |
| } |
| _ => None, |
| }) |
| .collect::<Vec<_>>() |
| } |
| |
| fn get_associated_constants<'a>( |
| i: &'a clean::Impl, |
| used_links: &mut FxHashSet<String>, |
| ) -> Vec<Link<'a>> { |
| i.items |
| .iter() |
| .filter_map(|item| match item.name { |
| Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new( |
| get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)), |
| name.as_str(), |
| )), |
| _ => None, |
| }) |
| .collect::<Vec<_>>() |
| } |
| |
| fn get_associated_types<'a>( |
| i: &'a clean::Impl, |
| used_links: &mut FxHashSet<String>, |
| ) -> Vec<Link<'a>> { |
| i.items |
| .iter() |
| .filter_map(|item| match item.name { |
| Some(ref name) if !name.is_empty() && item.is_associated_type() => Some(Link::new( |
| get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)), |
| name.as_str(), |
| )), |
| _ => None, |
| }) |
| .collect::<Vec<_>>() |
| } |