about summary refs log tree commit diff
path: root/src/librustdoc/html/render
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/render')
-rw-r--r--src/librustdoc/html/render/context.rs53
-rw-r--r--src/librustdoc/html/render/mod.rs25
-rw-r--r--src/librustdoc/html/render/print_item.rs439
-rw-r--r--src/librustdoc/html/render/sidebar.rs42
4 files changed, 395 insertions, 164 deletions
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index bb1c186668c..0bc10c5e10f 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,8 +7,10 @@ use std::sync::mpsc::{channel, Receiver};
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
+use rustc_span::def_id::DefId;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::FileName;
 use rustc_span::{sym, Symbol};
@@ -22,13 +24,13 @@ use super::{
     sidebar::{sidebar_module_like, Sidebar},
     AllTypes, LinkFromSrc, StylePath,
 };
-use crate::clean::{self, types::ExternalLocation, ExternalCrate};
+use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
 use crate::config::{ModuleSorting, RenderOptions};
 use crate::docfs::{DocFS, PathError};
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::formats::FormatRenderer;
+use crate::formats::{self, FormatRenderer};
 use crate::html::escape::Escape;
 use crate::html::format::{join_with_double_colon, Buffer};
 use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
@@ -147,6 +149,53 @@ impl SharedContext<'_> {
     pub(crate) fn edition(&self) -> Edition {
         self.tcx.sess.edition()
     }
+
+    /// Returns a list of impls on the given type, and, if it's a type alias,
+    /// other types that it aliases.
+    pub(crate) fn all_impls_for_item<'a>(
+        &'a self,
+        it: &clean::Item,
+        did: DefId,
+    ) -> Vec<&'a formats::Impl> {
+        let tcx = self.tcx;
+        let cache = &self.cache;
+        let mut saw_impls = FxHashSet::default();
+        let mut v: Vec<&formats::Impl> = cache
+            .impls
+            .get(&did)
+            .map(Vec::as_slice)
+            .unwrap_or(&[])
+            .iter()
+            .filter(|i| saw_impls.insert(i.def_id()))
+            .collect();
+        if let TypeAliasItem(ait) = &*it.kind &&
+            let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
+            let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
+            let Some(av) = cache.impls.get(&aliased_type_defid) &&
+            let Some(alias_def_id) = it.item_id.as_def_id()
+        {
+            // This branch of the compiler compares types structually, but does
+            // not check trait bounds. That's probably fine, since type aliases
+            // don't normally constrain on them anyway.
+            // https://github.com/rust-lang/rust/issues/21903
+            //
+            // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
+            // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
+            let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
+            let reject_cx = DeepRejectCtxt {
+                treat_obligation_params: TreatParams::AsCandidateKey,
+            };
+            v.extend(av.iter().filter(|impl_| {
+                if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
+                    reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
+                        && saw_impls.insert(impl_def_id)
+                } else {
+                    false
+                }
+            }));
+        }
+        v
+    }
 }
 
 impl<'tcx> Context<'tcx> {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index aef8f1a74fb..5adbecd6d04 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1117,13 +1117,13 @@ pub(crate) fn render_all_impls(
 fn render_assoc_items<'a, 'cx: 'a>(
     cx: &'a mut Context<'cx>,
     containing_item: &'a clean::Item,
-    it: DefId,
+    did: DefId,
     what: AssocItemRender<'a>,
 ) -> impl fmt::Display + 'a + Captures<'cx> {
     let mut derefs = DefIdSet::default();
-    derefs.insert(it);
+    derefs.insert(did);
     display_fn(move |f| {
-        render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
+        render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
         Ok(())
     })
 }
@@ -1132,15 +1132,17 @@ fn render_assoc_items_inner(
     mut w: &mut dyn fmt::Write,
     cx: &mut Context<'_>,
     containing_item: &clean::Item,
-    it: DefId,
+    did: DefId,
     what: AssocItemRender<'_>,
     derefs: &mut DefIdSet,
 ) {
     info!("Documenting associated items of {:?}", containing_item.name);
     let shared = Rc::clone(&cx.shared);
-    let cache = &shared.cache;
-    let Some(v) = cache.impls.get(&it) else { return };
-    let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
+    let v = shared.all_impls_for_item(containing_item, did);
+    let v = v.as_slice();
+    let (non_trait, traits): (Vec<&Impl>, _) =
+        v.iter().partition(|i| i.inner_impl().trait_.is_none());
+    let mut saw_impls = FxHashSet::default();
     if !non_trait.is_empty() {
         let mut tmp_buf = Buffer::html();
         let (render_mode, id, class_html) = match what {
@@ -1169,6 +1171,9 @@ fn render_assoc_items_inner(
         };
         let mut impls_buf = Buffer::html();
         for i in &non_trait {
+            if !saw_impls.insert(i.def_id()) {
+                continue;
+            }
             render_impl(
                 &mut impls_buf,
                 cx,
@@ -1214,8 +1219,10 @@ fn render_assoc_items_inner(
 
         let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
             traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
-        let (blanket_impl, concrete): (Vec<&Impl>, _) =
-            concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
+        let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
+            .into_iter()
+            .filter(|t| saw_impls.insert(t.def_id()))
+            .partition(|t| t.inner_impl().kind.is_blanket());
 
         render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
     }
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index cb78f903462..c6751c9585e 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1237,6 +1237,75 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
 
     write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
+    if let Some(inner_type) = &t.inner_type {
+        write!(
+            w,
+            "<h2 id=\"aliased-type\" class=\"small-section-header\">\
+                Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
+        );
+
+        match inner_type {
+            clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
+                let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+                wrap_item(w, |w| {
+                    let variants_len = variants.len();
+                    let variants_count = variants_iter().count();
+                    let has_stripped_entries = variants_len != variants_count;
+
+                    write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
+                    render_enum_fields(
+                        w,
+                        cx,
+                        Some(&t.generics),
+                        variants_iter(),
+                        variants_count,
+                        has_stripped_entries,
+                        *is_non_exhaustive,
+                    )
+                });
+                item_variants(w, cx, it, variants_iter());
+            }
+            clean::TypeAliasInnerType::Union { fields } => {
+                wrap_item(w, |w| {
+                    let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+                    let has_stripped_fields = fields.len() != fields_count;
+
+                    write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
+                    render_struct_fields(
+                        w,
+                        Some(&t.generics),
+                        None,
+                        fields,
+                        "",
+                        true,
+                        has_stripped_fields,
+                        cx,
+                    );
+                });
+                item_fields(w, cx, it, fields, None);
+            }
+            clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
+                wrap_item(w, |w| {
+                    let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+                    let has_stripped_fields = fields.len() != fields_count;
+
+                    write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
+                    render_struct_fields(
+                        w,
+                        Some(&t.generics),
+                        *ctor_kind,
+                        fields,
+                        "",
+                        true,
+                        has_stripped_fields,
+                        cx,
+                    );
+                });
+                item_fields(w, cx, it, fields, None);
+            }
+        }
+    }
+
     let def_id = it.item_id.expect_def_id();
     // Render any items associated directly to this alias, as otherwise they
     // won't be visible anywhere in the docs. It would be nice to also show
@@ -1315,6 +1384,12 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
     s: &'a [clean::Item],
 ) -> impl fmt::Display + 'a + Captures<'cx> {
     display_fn(|f| {
+        if s.iter()
+            .all(|field| matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))))
+        {
+            return f.write_str("/* private fields */");
+        }
+
         for (i, ty) in s.iter().enumerate() {
             if i > 0 {
                 f.write_str(", ")?;
@@ -1332,7 +1407,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
 fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
     let tcx = cx.tcx();
     let count_variants = e.variants().count();
-    wrap_item(w, |mut w| {
+    wrap_item(w, |w| {
         render_attributes_in_code(w, it, tcx);
         write!(
             w,
@@ -1341,148 +1416,179 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
             it.name.unwrap(),
             e.generics.print(cx),
         );
-        if !print_where_clause_and_check(w, &e.generics, cx) {
-            // If there wasn't a `where` clause, we add a whitespace.
-            w.write_str(" ");
-        }
+        render_enum_fields(
+            w,
+            cx,
+            Some(&e.generics),
+            e.variants(),
+            count_variants,
+            e.has_stripped_entries(),
+            it.is_non_exhaustive(),
+        );
+    });
 
-        let variants_stripped = e.has_stripped_entries();
-        if count_variants == 0 && !variants_stripped {
-            w.write_str("{}");
-        } else {
-            w.write_str("{\n");
-            let toggle = should_hide_fields(count_variants);
-            if toggle {
-                toggle_open(&mut w, format_args!("{count_variants} variants"));
-            }
-            for v in e.variants() {
-                w.write_str("    ");
-                let name = v.name.unwrap();
-                match *v.kind {
-                    // FIXME(#101337): Show discriminant
-                    clean::VariantItem(ref var) => match var.kind {
-                        clean::VariantKind::CLike => w.write_str(name.as_str()),
-                        clean::VariantKind::Tuple(ref s) => {
-                            write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
-                        }
-                        clean::VariantKind::Struct(ref s) => {
-                            render_struct(w, v, None, None, &s.fields, "    ", false, cx);
-                        }
-                    },
-                    _ => unreachable!(),
-                }
-                w.write_str(",\n");
-            }
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
-            if variants_stripped && !it.is_non_exhaustive() {
-                w.write_str("    // some variants omitted\n");
-            }
-            if toggle {
-                toggle_close(&mut w);
+    if count_variants != 0 {
+        item_variants(w, cx, it, e.variants());
+    }
+    let def_id = it.item_id.expect_def_id();
+    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+    write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn render_enum_fields<'a>(
+    mut w: &mut Buffer,
+    cx: &mut Context<'_>,
+    g: Option<&clean::Generics>,
+    variants: impl Iterator<Item = &'a clean::Item>,
+    count_variants: usize,
+    has_stripped_entries: bool,
+    is_non_exhaustive: bool,
+) {
+    if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
+        // If there wasn't a `where` clause, we add a whitespace.
+        w.write_str(" ");
+    }
+
+    let variants_stripped = has_stripped_entries;
+    if count_variants == 0 && !variants_stripped {
+        w.write_str("{}");
+    } else {
+        w.write_str("{\n");
+        let toggle = should_hide_fields(count_variants);
+        if toggle {
+            toggle_open(&mut w, format_args!("{count_variants} variants"));
+        }
+        const TAB: &str = "    ";
+        for v in variants {
+            w.write_str(TAB);
+            let name = v.name.unwrap();
+            match *v.kind {
+                // FIXME(#101337): Show discriminant
+                clean::VariantItem(ref var) => match var.kind {
+                    clean::VariantKind::CLike => w.write_str(name.as_str()),
+                    clean::VariantKind::Tuple(ref s) => {
+                        write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+                    }
+                    clean::VariantKind::Struct(ref s) => {
+                        render_struct(w, v, None, None, &s.fields, TAB, false, cx);
+                    }
+                },
+                _ => unreachable!(),
             }
-            w.write_str("}");
+            w.write_str(",\n");
         }
-    });
 
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+        if variants_stripped && !is_non_exhaustive {
+            w.write_str("    // some variants omitted\n");
+        }
+        if toggle {
+            toggle_close(&mut w);
+        }
+        w.write_str("}");
+    }
+}
 
-    if count_variants != 0 {
+fn item_variants<'a>(
+    w: &mut Buffer,
+    cx: &mut Context<'_>,
+    it: &clean::Item,
+    variants: impl Iterator<Item = &'a clean::Item>,
+) {
+    let tcx = cx.tcx();
+    write!(
+        w,
+        "<h2 id=\"variants\" class=\"variants small-section-header\">\
+            Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
+        </h2>\
+        {}\
+        <div class=\"variants\">",
+        document_non_exhaustive_header(it),
+        document_non_exhaustive(it)
+    );
+    for variant in variants {
+        let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
         write!(
             w,
-            "<h2 id=\"variants\" class=\"variants small-section-header\">\
-                Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
-            </h2>\
-            {}\
-            <div class=\"variants\">",
-            document_non_exhaustive_header(it),
-            document_non_exhaustive(it)
+            "<section id=\"{id}\" class=\"variant\">\
+                <a href=\"#{id}\" class=\"anchor\">§</a>",
         );
-        for variant in e.variants() {
-            let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
-            write!(
-                w,
-                "<section id=\"{id}\" class=\"variant\">\
-                    <a href=\"#{id}\" class=\"anchor\">§</a>",
-            );
-            render_stability_since_raw_with_extra(
-                w,
-                variant.stable_since(tcx),
-                variant.const_stability(tcx),
-                it.stable_since(tcx),
-                it.const_stable_since(tcx),
-                " rightside",
-            );
-            write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+        render_stability_since_raw_with_extra(
+            w,
+            variant.stable_since(tcx),
+            variant.const_stability(tcx),
+            it.stable_since(tcx),
+            it.const_stable_since(tcx),
+            " rightside",
+        );
+        write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
 
-            let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
+        let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
 
-            if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
-                write!(w, "({})", print_tuple_struct_fields(cx, s));
-            }
-            w.write_str("</h3></section>");
-
-            let heading_and_fields = match &variant_data.kind {
-                clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
-                clean::VariantKind::Tuple(fields) => {
-                    // Documentation on tuple variant fields is rare, so to reduce noise we only emit
-                    // the section if at least one field is documented.
-                    if fields.iter().any(|f| !f.doc_value().is_empty()) {
-                        Some(("Tuple Fields", fields))
-                    } else {
-                        None
-                    }
+        if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
+            write!(w, "({})", print_tuple_struct_fields(cx, s));
+        }
+        w.write_str("</h3></section>");
+
+        let heading_and_fields = match &variant_data.kind {
+            clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
+            clean::VariantKind::Tuple(fields) => {
+                // Documentation on tuple variant fields is rare, so to reduce noise we only emit
+                // the section if at least one field is documented.
+                if fields.iter().any(|f| !f.doc_value().is_empty()) {
+                    Some(("Tuple Fields", fields))
+                } else {
+                    None
                 }
-                clean::VariantKind::CLike => None,
-            };
+            }
+            clean::VariantKind::CLike => None,
+        };
 
-            if let Some((heading, fields)) = heading_and_fields {
-                let variant_id =
-                    cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
-                write!(
-                    w,
-                    "<div class=\"sub-variant\" id=\"{variant_id}\">\
-                        <h4>{heading}</h4>\
-                        {}",
-                    document_non_exhaustive(variant)
-                );
-                for field in fields {
-                    match *field.kind {
-                        clean::StrippedItem(box clean::StructFieldItem(_)) => {}
-                        clean::StructFieldItem(ref ty) => {
-                            let id = cx.derive_id(format!(
-                                "variant.{}.field.{}",
-                                variant.name.unwrap(),
-                                field.name.unwrap()
-                            ));
-                            write!(
-                                w,
-                                "<div class=\"sub-variant-field\">\
+        if let Some((heading, fields)) = heading_and_fields {
+            let variant_id =
+                cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
+            write!(
+                w,
+                "<div class=\"sub-variant\" id=\"{variant_id}\">\
+                    <h4>{heading}</h4>\
+                    {}",
+                document_non_exhaustive(variant)
+            );
+            for field in fields {
+                match *field.kind {
+                    clean::StrippedItem(box clean::StructFieldItem(_)) => {}
+                    clean::StructFieldItem(ref ty) => {
+                        let id = cx.derive_id(format!(
+                            "variant.{}.field.{}",
+                            variant.name.unwrap(),
+                            field.name.unwrap()
+                        ));
+                        write!(
+                            w,
+                            "<div class=\"sub-variant-field\">\
                                  <span id=\"{id}\" class=\"small-section-header\">\
                                      <a href=\"#{id}\" class=\"anchor field\">§</a>\
                                      <code>{f}: {t}</code>\
                                  </span>",
-                                f = field.name.unwrap(),
-                                t = ty.print(cx),
-                            );
-                            write!(
-                                w,
-                                "{}</div>",
-                                document(cx, field, Some(variant), HeadingOffset::H5)
-                            );
-                        }
-                        _ => unreachable!(),
+                            f = field.name.unwrap(),
+                            t = ty.print(cx),
+                        );
+                        write!(
+                            w,
+                            "{}</div>",
+                            document(cx, field, Some(variant), HeadingOffset::H5)
+                        );
                     }
+                    _ => unreachable!(),
                 }
-                w.write_str("</div>");
             }
-
-            write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
+            w.write_str("</div>");
         }
-        write!(w, "</div>");
+
+        write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
     }
-    let def_id = it.item_id.expect_def_id();
-    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
-    write!(w, "{}", document_type_layout(cx, def_id));
+    write!(w, "</div>");
 }
 
 fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
@@ -1593,15 +1699,28 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
 
     write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
-    let mut fields = s
-        .fields
+    item_fields(w, cx, it, &s.fields, s.ctor_kind);
+
+    let def_id = it.item_id.expect_def_id();
+    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+    write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn item_fields(
+    w: &mut Buffer,
+    cx: &mut Context<'_>,
+    it: &clean::Item,
+    fields: &Vec<clean::Item>,
+    ctor_kind: Option<CtorKind>,
+) {
+    let mut fields = fields
         .iter()
         .filter_map(|f| match *f.kind {
             clean::StructFieldItem(ref ty) => Some((f, ty)),
             _ => None,
         })
         .peekable();
-    if let None | Some(CtorKind::Fn) = s.ctor_kind {
+    if let None | Some(CtorKind::Fn) = ctor_kind {
         if fields.peek().is_some() {
             write!(
                 w,
@@ -1609,7 +1728,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
                      {}{}<a href=\"#fields\" class=\"anchor\">§</a>\
                  </h2>\
                  {}",
-                if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
+                if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
                 document_non_exhaustive_header(it),
                 document_non_exhaustive(it)
             );
@@ -1630,9 +1749,6 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
             }
         }
     }
-    let def_id = it.item_id.expect_def_id();
-    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
-    write!(w, "{}", document_type_layout(cx, def_id));
 }
 
 fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1871,7 +1987,7 @@ fn render_union<'a, 'cx: 'a>(
 }
 
 fn render_struct(
-    mut w: &mut Buffer,
+    w: &mut Buffer,
     it: &clean::Item,
     g: Option<&clean::Generics>,
     ty: Option<CtorKind>,
@@ -1891,6 +2007,29 @@ fn render_struct(
     if let Some(g) = g {
         write!(w, "{}", g.print(cx))
     }
+    render_struct_fields(
+        w,
+        g,
+        ty,
+        fields,
+        tab,
+        structhead,
+        it.has_stripped_entries().unwrap_or(false),
+        cx,
+    )
+}
+
+fn render_struct_fields(
+    mut w: &mut Buffer,
+    g: Option<&clean::Generics>,
+    ty: Option<CtorKind>,
+    fields: &[clean::Item],
+    tab: &str,
+    structhead: bool,
+    has_stripped_entries: bool,
+    cx: &Context<'_>,
+) {
+    let tcx = cx.tcx();
     match ty {
         None => {
             let where_displayed =
@@ -1922,11 +2061,11 @@ fn render_struct(
             }
 
             if has_visible_fields {
-                if it.has_stripped_entries().unwrap() {
+                if has_stripped_entries {
                     write!(w, "\n{tab}    /* private fields */");
                 }
                 write!(w, "\n{tab}");
-            } else if it.has_stripped_entries().unwrap() {
+            } else if has_stripped_entries {
                 write!(w, " /* private fields */ ");
             }
             if toggle {
@@ -1936,21 +2075,31 @@ fn render_struct(
         }
         Some(CtorKind::Fn) => {
             w.write_str("(");
-            for (i, field) in fields.iter().enumerate() {
-                if i > 0 {
-                    w.write_str(", ");
-                }
-                match *field.kind {
-                    clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
-                    clean::StructFieldItem(ref ty) => {
-                        write!(
-                            w,
-                            "{}{}",
-                            visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
-                            ty.print(cx),
-                        )
+            if fields.iter().all(|field| {
+                matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
+            }) {
+                write!(w, "/* private fields */");
+            } else {
+                for (i, field) in fields.iter().enumerate() {
+                    if i > 0 {
+                        w.write_str(", ");
+                    }
+                    match *field.kind {
+                        clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
+                        clean::StructFieldItem(ref ty) => {
+                            write!(
+                                w,
+                                "{}{}",
+                                visibility_print_with_space(
+                                    field.visibility(tcx),
+                                    field.item_id,
+                                    cx
+                                ),
+                                ty.print(cx),
+                            )
+                        }
+                        _ => unreachable!(),
                     }
-                    _ => unreachable!(),
                 }
             }
             w.write_str(")");
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index b96b1536156..76f63c6f63e 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -82,7 +82,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
         clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
         clean::UnionItem(ref u) => sidebar_union(cx, it, u),
         clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
-        clean::TypeAliasItem(_) => sidebar_type_alias(cx, it),
+        clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t),
         clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
         clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
         _ => vec![],
@@ -230,8 +230,33 @@ fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBl
     }
 }
 
-fn sidebar_type_alias<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+fn sidebar_type_alias<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    t: &'a clean::TypeAlias,
+) -> Vec<LinkBlock<'a>> {
     let mut items = vec![];
+    if let Some(inner_type) = &t.inner_type {
+        items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased 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"), 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"), fields));
+            }
+        }
+    }
     sidebar_assoc_items(cx, it, &mut items);
     items
 }
@@ -254,11 +279,12 @@ fn sidebar_assoc_items<'a>(
     links: &mut Vec<LinkBlock<'a>>,
 ) {
     let did = it.item_id.expect_def_id();
-    let cache = cx.cache();
+    let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
+    let v = v.as_slice();
 
     let mut assoc_consts = Vec::new();
     let mut methods = Vec::new();
-    if let Some(v) = cache.impls.get(&did) {
+    if !v.is_empty() {
         let mut used_links = FxHashSet::default();
         let mut id_map = IdMap::new();
 
@@ -294,7 +320,7 @@ fn sidebar_assoc_items<'a>(
                     cx,
                     &mut deref_methods,
                     impl_,
-                    v,
+                    v.iter().copied(),
                     &mut derefs,
                     &mut used_links,
                 );
@@ -324,7 +350,7 @@ fn sidebar_deref_methods<'a>(
     cx: &'a Context<'_>,
     out: &mut Vec<LinkBlock<'a>>,
     impl_: &Impl,
-    v: &[Impl],
+    v: impl Iterator<Item = &'a Impl>,
     derefs: &mut DefIdSet,
     used_links: &mut FxHashSet<String>,
 ) {
@@ -349,7 +375,7 @@ fn sidebar_deref_methods<'a>(
             // Avoid infinite cycles
             return;
         }
-        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+        let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
         let inner_impl = target
             .def_id(c)
             .or_else(|| {
@@ -400,7 +426,7 @@ fn sidebar_deref_methods<'a>(
                 cx,
                 out,
                 target_deref_impl,
-                target_impls,
+                target_impls.iter(),
                 derefs,
                 used_links,
             );