1 use std
::{borrow::Cow, rc::Rc}
;
4 use rustc_data_structures
::fx
::FxHashSet
;
5 use rustc_hir
::{def::CtorKind, def_id::DefIdSet}
;
6 use rustc_middle
::ty
::{self, TyCtxt}
;
10 formats
::{item_type::ItemType, Impl}
,
11 html
::{format::Buffer, markdown::IdMap}
,
14 use super::{item_ty_to_section, Context, ItemSection}
;
17 #[template(path = "sidebar.html")]
18 pub(super) struct Sidebar
<'a
> {
19 pub(super) title_prefix
: &'
static str,
20 pub(super) title
: &'a
str,
21 pub(super) is_crate
: bool
,
22 pub(super) version
: &'a
str,
23 pub(super) blocks
: Vec
<LinkBlock
<'a
>>,
24 pub(super) path
: String
,
27 impl<'a
> Sidebar
<'a
> {
28 /// Only create a `<section>` if there are any blocks
29 /// which should actually be rendered.
30 pub fn should_render_blocks(&self) -> bool
{
31 self.blocks
.iter().any(LinkBlock
::should_render
)
35 /// A sidebar section such as 'Methods'.
36 pub(crate) struct LinkBlock
<'a
> {
37 /// The name of this section, e.g. 'Methods'
38 /// as well as the link to it, e.g. `#implementations`.
39 /// Will be rendered inside an `<h3>` tag
42 /// Render the heading even if there are no links
46 impl<'a
> LinkBlock
<'a
> {
47 pub fn new(heading
: Link
<'a
>, links
: Vec
<Link
<'a
>>) -> Self {
48 Self { heading, links, force_render: false }
51 pub fn forced(heading
: Link
<'a
>) -> Self {
52 Self { heading, links: vec![], force_render: true }
55 pub fn should_render(&self) -> bool
{
56 self.force_render
|| !self.links
.is_empty()
60 /// A link to an item. Content should not be escaped.
61 #[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
62 pub(crate) struct Link
<'a
> {
63 /// The content for the anchor tag
65 /// The id of an anchor within the page (without a `#` prefix)
70 pub fn new(href
: impl Into
<Cow
<'a
, str>>, name
: impl Into
<Cow
<'a
, str>>) -> Self {
71 Self { href: href.into(), name: name.into() }
73 pub fn empty() -> Link
<'
static> {
78 pub(super) fn print_sidebar(cx
: &Context
<'_
>, it
: &clean
::Item
, buffer
: &mut Buffer
) {
79 let blocks
: Vec
<LinkBlock
<'_
>> = match *it
.kind
{
80 clean
::StructItem(ref s
) => sidebar_struct(cx
, it
, s
),
81 clean
::TraitItem(ref t
) => sidebar_trait(cx
, it
, t
),
82 clean
::PrimitiveItem(_
) => sidebar_primitive(cx
, it
),
83 clean
::UnionItem(ref u
) => sidebar_union(cx
, it
, u
),
84 clean
::EnumItem(ref e
) => sidebar_enum(cx
, it
, e
),
85 clean
::TypeAliasItem(ref t
) => sidebar_type_alias(cx
, it
, t
),
86 clean
::ModuleItem(ref m
) => vec
![sidebar_module(&m
.items
)],
87 clean
::ForeignTypeItem
=> sidebar_foreign_type(cx
, it
),
90 // The sidebar is designed to display sibling functions, modules and
91 // other miscellaneous information. since there are lots of sibling
92 // items (and that causes quadratic growth in large modules),
93 // we refactor common parts into a shared JavaScript file per module.
94 // still, we don't move everything into JS because we want to preserve
95 // as much HTML as possible in order to allow non-JS-enabled browsers
96 // to navigate the documentation (though slightly inefficiently).
97 let (title_prefix
, title
) = if it
.is_struct()
103 || it
.is_type_alias()
107 clean
::ModuleItem(..) if it
.is_crate() => "Crate ",
108 clean
::ModuleItem(..) => "Module ",
111 it
.name
.as_ref().unwrap().as_str(),
117 if it
.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() }
else { "" }
;
118 let path
: String
= if !it
.is_mod() {
119 cx
.current
.iter().map(|s
| s
.as_str()).intersperse("::").collect()
123 let sidebar
= Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path }
;
124 sidebar
.render_into(buffer
).unwrap();
127 fn get_struct_fields_name
<'a
>(fields
: &'a
[clean
::Item
]) -> Vec
<Link
<'a
>> {
128 let mut fields
= fields
130 .filter(|f
| matches
!(*f
.kind
, clean
::StructFieldItem(..)))
132 f
.name
.as_ref().map(|name
| Link
::new(format
!("structfield.{name}"), name
.as_str()))
134 .collect
::<Vec
<Link
<'a
>>>();
139 fn sidebar_struct
<'a
>(
142 s
: &'a clean
::Struct
,
143 ) -> Vec
<LinkBlock
<'a
>> {
144 let fields
= get_struct_fields_name(&s
.fields
);
145 let field_name
= match s
.ctor_kind
{
146 Some(CtorKind
::Fn
) => Some("Tuple Fields"),
147 None
=> Some("Fields"),
150 let mut items
= vec
![];
151 if let Some(name
) = field_name
{
152 items
.push(LinkBlock
::new(Link
::new("fields", name
), fields
));
154 sidebar_assoc_items(cx
, it
, &mut items
);
158 fn sidebar_trait
<'a
>(
162 ) -> Vec
<LinkBlock
<'a
>> {
164 items
: &'a
[clean
::Item
],
165 filt
: impl Fn(&clean
::Item
) -> bool
,
170 .filter_map(|m
: &clean
::Item
| match m
.name
{
171 Some(ref name
) if filt(m
) => Some(Link
::new(format
!("{ty}.{name}"), name
.as_str())),
174 .collect
::<Vec
<Link
<'a
>>>();
179 let req_assoc
= filter_items(&t
.items
, |m
| m
.is_ty_associated_type(), "associatedtype");
180 let prov_assoc
= filter_items(&t
.items
, |m
| m
.is_associated_type(), "associatedtype");
181 let req_assoc_const
=
182 filter_items(&t
.items
, |m
| m
.is_ty_associated_const(), "associatedconstant");
183 let prov_assoc_const
=
184 filter_items(&t
.items
, |m
| m
.is_associated_const(), "associatedconstant");
185 let req_method
= filter_items(&t
.items
, |m
| m
.is_ty_method(), "tymethod");
186 let prov_method
= filter_items(&t
.items
, |m
| m
.is_method(), "method");
187 let mut foreign_impls
= vec
![];
188 if let Some(implementors
) = cx
.cache().implementors
.get(&it
.item_id
.expect_def_id()) {
189 foreign_impls
.extend(
192 .filter(|i
| !i
.is_on_local_type(cx
))
193 .filter_map(|i
| super::extract_for_impl_name(&i
.impl_item
, cx
))
194 .map(|(name
, id
)| Link
::new(id
, name
)),
196 foreign_impls
.sort();
199 let mut blocks
: Vec
<LinkBlock
<'_
>> = [
200 ("required-associated-types", "Required Associated Types", req_assoc
),
201 ("provided-associated-types", "Provided Associated Types", prov_assoc
),
202 ("required-associated-consts", "Required Associated Constants", req_assoc_const
),
203 ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const
),
204 ("required-methods", "Required Methods", req_method
),
205 ("provided-methods", "Provided Methods", prov_method
),
206 ("foreign-impls", "Implementations on Foreign Types", foreign_impls
),
209 .map(|(id
, title
, items
)| LinkBlock
::new(Link
::new(id
, title
), items
))
211 sidebar_assoc_items(cx
, it
, &mut blocks
);
212 blocks
.push(LinkBlock
::forced(Link
::new("implementors", "Implementors")));
213 if t
.is_auto(cx
.tcx()) {
214 blocks
.push(LinkBlock
::forced(Link
::new("synthetic-implementors", "Auto Implementors")));
219 fn sidebar_primitive
<'a
>(cx
: &'a Context
<'_
>, it
: &'a clean
::Item
) -> Vec
<LinkBlock
<'a
>> {
220 if it
.name
.map(|n
| n
.as_str() != "reference").unwrap_or(false) {
221 let mut items
= vec
![];
222 sidebar_assoc_items(cx
, it
, &mut items
);
225 let shared
= Rc
::clone(&cx
.shared
);
226 let (concrete
, synthetic
, blanket_impl
) =
227 super::get_filtered_impls_for_reference(&shared
, it
);
229 sidebar_render_assoc_items(cx
, &mut IdMap
::new(), concrete
, synthetic
, blanket_impl
).into()
233 fn sidebar_type_alias
<'a
>(
236 t
: &'a clean
::TypeAlias
,
237 ) -> Vec
<LinkBlock
<'a
>> {
238 let mut items
= vec
![];
239 if let Some(inner_type
) = &t
.inner_type
{
240 items
.push(LinkBlock
::forced(Link
::new("aliased-type", "Aliased type")));
242 clean
::TypeAliasInnerType
::Enum { variants, is_non_exhaustive: _ }
=> {
243 let mut variants
= variants
245 .filter(|i
| !i
.is_stripped())
246 .filter_map(|v
| v
.name
)
247 .map(|name
| Link
::new(format
!("variant.{name}"), name
.to_string()))
248 .collect
::<Vec
<_
>>();
249 variants
.sort_unstable();
251 items
.push(LinkBlock
::new(Link
::new("variants", "Variants"), variants
));
253 clean
::TypeAliasInnerType
::Union { fields }
254 | clean
::TypeAliasInnerType
::Struct { ctor_kind: _, fields }
=> {
255 let fields
= get_struct_fields_name(fields
);
256 items
.push(LinkBlock
::new(Link
::new("fields", "Fields"), fields
));
260 sidebar_assoc_items(cx
, it
, &mut items
);
264 fn sidebar_union
<'a
>(
268 ) -> Vec
<LinkBlock
<'a
>> {
269 let fields
= get_struct_fields_name(&u
.fields
);
270 let mut items
= vec
![LinkBlock
::new(Link
::new("fields", "Fields"), fields
)];
271 sidebar_assoc_items(cx
, it
, &mut items
);
275 /// Adds trait implementations into the blocks of links
276 fn sidebar_assoc_items
<'a
>(
279 links
: &mut Vec
<LinkBlock
<'a
>>,
281 let did
= it
.item_id
.expect_def_id();
282 let v
= cx
.shared
.all_impls_for_item(it
, it
.item_id
.expect_def_id());
283 let v
= v
.as_slice();
285 let mut assoc_consts
= Vec
::new();
286 let mut methods
= Vec
::new();
288 let mut used_links
= FxHashSet
::default();
289 let mut id_map
= IdMap
::new();
292 let used_links_bor
= &mut used_links
;
295 .filter(|i
| i
.inner_impl().trait_
.is_none())
296 .flat_map(|i
| get_associated_constants(i
.inner_impl(), used_links_bor
)),
298 // We want links' order to be reproducible so we don't use unstable sort.
301 #[rustfmt::skip] // rustfmt makes the pipeline less readable
304 .filter(|i
| i
.inner_impl().trait_
.is_none())
305 .flat_map(|i
| get_methods(i
.inner_impl(), false, used_links_bor
, false, cx
.tcx())),
308 // We want links' order to be reproducible so we don't use unstable sort.
312 let mut deref_methods
= Vec
::new();
313 let [concrete
, synthetic
, blanket
] = if v
.iter().any(|i
| i
.inner_impl().trait_
.is_some()) {
315 v
.iter().find(|i
| i
.trait_did() == cx
.tcx().lang_items().deref_trait())
317 let mut derefs
= DefIdSet
::default();
319 sidebar_deref_methods(
329 let (synthetic
, concrete
): (Vec
<&Impl
>, Vec
<&Impl
>) =
330 v
.iter().partition
::<Vec
<_
>, _
>(|i
| i
.inner_impl().kind
.is_auto());
331 let (blanket_impl
, concrete
): (Vec
<&Impl
>, Vec
<&Impl
>) =
332 concrete
.into_iter().partition
::<Vec
<_
>, _
>(|i
| i
.inner_impl().kind
.is_blanket());
334 sidebar_render_assoc_items(cx
, &mut id_map
, concrete
, synthetic
, blanket_impl
)
336 std
::array
::from_fn(|_
| LinkBlock
::new(Link
::empty(), vec
![]))
339 let mut blocks
= vec
![
340 LinkBlock
::new(Link
::new("implementations", "Associated Constants"), assoc_consts
),
341 LinkBlock
::new(Link
::new("implementations", "Methods"), methods
),
343 blocks
.append(&mut deref_methods
);
344 blocks
.extend([concrete
, synthetic
, blanket
]);
345 links
.append(&mut blocks
);
349 fn sidebar_deref_methods
<'a
>(
351 out
: &mut Vec
<LinkBlock
<'a
>>,
353 v
: impl Iterator
<Item
= &'a Impl
>,
354 derefs
: &mut DefIdSet
,
355 used_links
: &mut FxHashSet
<String
>,
359 debug
!("found Deref: {impl_:?}");
360 if let Some((target
, real_target
)) =
361 impl_
.inner_impl().items
.iter().find_map(|item
| match *item
.kind
{
362 clean
::AssocTypeItem(box ref t
, _
) => Some(match *t
{
363 clean
::TypeAlias { item_type: Some(ref type_), .. }
=> (type_
, &t
.type_
),
364 _
=> (&t
.type_
, &t
.type_
),
369 debug
!("found target, real_target: {target:?} {real_target:?}");
370 if let Some(did
) = target
.def_id(c
) &&
371 let Some(type_did
) = impl_
.inner_impl().for_
.def_id(c
) &&
372 // `impl Deref<Target = S> for S`
373 (did
== type_did
|| !derefs
.insert(did
))
375 // Avoid infinite cycles
378 let deref_mut
= { v }
.any(|i
| i
.trait_did() == cx
.tcx().lang_items().deref_mut_trait());
379 let inner_impl
= target
382 target
.primitive_type().and_then(|prim
| c
.primitive_locations
.get(&prim
).cloned())
384 .and_then(|did
| c
.impls
.get(&did
));
385 if let Some(impls
) = inner_impl
{
386 debug
!("found inner_impl: {impls:?}");
389 .filter(|i
| i
.inner_impl().trait_
.is_none())
390 .flat_map(|i
| get_methods(i
.inner_impl(), true, used_links
, deref_mut
, cx
.tcx()))
391 .collect
::<Vec
<_
>>();
393 let id
= if let Some(target_def_id
) = real_target
.def_id(c
) {
397 .expect("Deref section without derived id")
401 Cow
::Borrowed("deref-methods")
404 "Methods from {:#}<Target={:#}>",
405 impl_
.inner_impl().trait_
.as_ref().unwrap().print(cx
),
406 real_target
.print(cx
),
408 // We want links' order to be reproducible so we don't use unstable sort.
410 out
.push(LinkBlock
::new(Link
::new(id
, title
), ret
));
414 // Recurse into any further impls that might exist for `target`
415 if let Some(target_did
) = target
.def_id(c
) &&
416 let Some(target_impls
) = c
.impls
.get(&target_did
) &&
417 let Some(target_deref_impl
) = target_impls
.iter().find(|i
| {
421 .map(|t
| Some(t
.def_id()) == cx
.tcx().lang_items().deref_trait())
425 sidebar_deref_methods(
441 ) -> Vec
<LinkBlock
<'a
>> {
444 .filter_map(|v
| v
.name
)
445 .map(|name
| Link
::new(format
!("variant.{name}"), name
.to_string()))
446 .collect
::<Vec
<_
>>();
447 variants
.sort_unstable();
449 let mut items
= vec
![LinkBlock
::new(Link
::new("variants", "Variants"), variants
)];
450 sidebar_assoc_items(cx
, it
, &mut items
);
454 pub(crate) fn sidebar_module_like(
455 item_sections_in_use
: FxHashSet
<ItemSection
>,
456 ) -> LinkBlock
<'
static> {
457 let item_sections
= ItemSection
::ALL
460 .filter(|sec
| item_sections_in_use
.contains(sec
))
461 .map(|sec
| Link
::new(sec
.id(), sec
.name()))
463 LinkBlock
::new(Link
::empty(), item_sections
)
466 fn sidebar_module(items
: &[clean
::Item
]) -> LinkBlock
<'
static> {
467 let item_sections_in_use
: FxHashSet
<_
> = items
474 if let clean
::ImportItem(ref i
) = *it
.kind
&&
475 let clean
::ImportKind
::Simple(s
) = i
.kind { Some(s) }
else { None }
479 .map(|it
| item_ty_to_section(it
.type_()))
482 sidebar_module_like(item_sections_in_use
)
485 fn sidebar_foreign_type
<'a
>(cx
: &'a Context
<'_
>, it
: &'a clean
::Item
) -> Vec
<LinkBlock
<'a
>> {
486 let mut items
= vec
![];
487 sidebar_assoc_items(cx
, it
, &mut items
);
491 /// Renders the trait implementations for this type
492 fn sidebar_render_assoc_items(
495 concrete
: Vec
<&Impl
>,
496 synthetic
: Vec
<&Impl
>,
497 blanket_impl
: Vec
<&Impl
>,
498 ) -> [LinkBlock
<'
static>; 3] {
499 let format_impls
= |impls
: Vec
<&Impl
>, id_map
: &mut IdMap
| {
500 let mut links
= FxHashSet
::default();
505 let trait_
= it
.inner_impl().trait_
.as_ref()?
;
507 id_map
.derive(super::get_id_for_impl(&it
.inner_impl().for_
, Some(trait_
), cx
));
509 let prefix
= match it
.inner_impl().polarity
{
510 ty
::ImplPolarity
::Positive
| ty
::ImplPolarity
::Reservation
=> "",
511 ty
::ImplPolarity
::Negative
=> "!",
513 let generated
= Link
::new(encoded
, format
!("{prefix}{:#}", trait_
.print(cx
)));
514 if links
.insert(generated
.clone()) { Some(generated) }
else { None }
516 .collect
::<Vec
<Link
<'
static>>>();
521 let concrete
= format_impls(concrete
, id_map
);
522 let synthetic
= format_impls(synthetic
, id_map
);
523 let blanket
= format_impls(blanket_impl
, id_map
);
525 LinkBlock
::new(Link
::new("trait-implementations", "Trait Implementations"), concrete
),
527 Link
::new("synthetic-implementations", "Auto Trait Implementations"),
530 LinkBlock
::new(Link
::new("blanket-implementations", "Blanket Implementations"), blanket
),
534 fn get_next_url(used_links
: &mut FxHashSet
<String
>, url
: String
) -> String
{
535 if used_links
.insert(url
.clone()) {
539 while !used_links
.insert(format
!("{url}-{add}")) {
542 format
!("{url}-{add}")
548 used_links
: &mut FxHashSet
<String
>,
554 .filter_map(|item
| match item
.name
{
555 Some(ref name
) if !name
.is_empty() && item
.is_method() => {
556 if !for_deref
|| super::should_render_item(item
, deref_mut
, tcx
) {
558 get_next_url(used_links
, format
!("{typ}.{name}", typ
= ItemType
::Method
)),
570 fn get_associated_constants
<'a
>(
572 used_links
: &mut FxHashSet
<String
>,
576 .filter_map(|item
| match item
.name
{
577 Some(ref name
) if !name
.is_empty() && item
.is_associated_const() => Some(Link
::new(
578 get_next_url(used_links
, format
!("{typ}.{name}", typ
= ItemType
::AssocConst
)),