about summary refs log tree commit diff
path: root/src/librustdoc/html/render
diff options
context:
space:
mode:
authorTshepang Mbambo <tshepang@gmail.com>2025-04-19 16:06:07 +0200
committerGitHub <noreply@github.com>2025-04-19 16:06:07 +0200
commitb886a8e654ab660f2f2cd02c68bf170dd4918fd8 (patch)
tree86e6a9ab8518740ca6ce8deae8f409ee9c309f20 /src/librustdoc/html/render
parent52616fa902a9a551ba4e574bee41dbf6162746f4 (diff)
parent07b7f00a9e961fdf66ece2ecf70ae1dba505f10e (diff)
Merge pull request #2347 from rust-lang/rustc-pull
Rustc pull update
Diffstat (limited to 'src/librustdoc/html/render')
-rw-r--r--src/librustdoc/html/render/context.rs24
-rw-r--r--src/librustdoc/html/render/mod.rs429
-rw-r--r--src/librustdoc/html/render/print_item.rs26
-rw-r--r--src/librustdoc/html/render/search_index.rs29
-rw-r--r--src/librustdoc/html/render/sidebar.rs6
-rw-r--r--src/librustdoc/html/render/tests.rs3
-rw-r--r--src/librustdoc/html/render/type_layout.rs2
7 files changed, 259 insertions, 260 deletions
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 5f69e79f3ab..f22935df96c 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -5,7 +5,7 @@ use std::io;
 use std::path::{Path, PathBuf};
 use std::sync::mpsc::{Receiver, channel};
 
-use rinja::Template;
+use askama::Template;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
 use rustc_middle::ty::TyCtxt;
@@ -521,23 +521,23 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         // Crawl the crate attributes looking for attributes which control how we're
         // going to emit HTML
         for attr in krate.module.attrs.lists(sym::doc) {
-            match (attr.name_or_empty(), attr.value_str()) {
-                (sym::html_favicon_url, Some(s)) => {
+            match (attr.name(), attr.value_str()) {
+                (Some(sym::html_favicon_url), Some(s)) => {
                     layout.favicon = s.to_string();
                 }
-                (sym::html_logo_url, Some(s)) => {
+                (Some(sym::html_logo_url), Some(s)) => {
                     layout.logo = s.to_string();
                 }
-                (sym::html_playground_url, Some(s)) => {
+                (Some(sym::html_playground_url), Some(s)) => {
                     playground = Some(markdown::Playground {
                         crate_name: Some(krate.name(tcx)),
                         url: s.to_string(),
                     });
                 }
-                (sym::issue_tracker_base_url, Some(s)) => {
+                (Some(sym::issue_tracker_base_url), Some(s)) => {
                     issue_tracker_base_url = Some(s.to_string());
                 }
-                (sym::html_no_source, None) if attr.is_word() => {
+                (Some(sym::html_no_source), None) if attr.is_word() => {
                     include_sources = false;
                 }
                 _ => {}
@@ -650,15 +650,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
 
         bar.render_into(&mut sidebar).unwrap();
 
-        let v = layout::render(
-            &shared.layout,
-            &page,
-            sidebar,
-            BufDisplay(|buf: &mut String| {
-                all.print(buf);
-            }),
-            &shared.style_files,
-        );
+        let v = layout::render(&shared.layout, &page, sidebar, all.print(), &shared.style_files);
         shared.fs.write(final_file, v)?;
 
         // if to avoid writing help, settings files to doc root unless we're on the final invocation
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 2237e0f987b..7e17f09aecd 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -13,6 +13,9 @@
 //! is cloned per-thread and contains information about what is currently being
 //! rendered.
 //!
+//! The main entry point to the rendering system is the implementation of
+//! `FormatRenderer` on `Context`.
+//!
 //! In order to speed up rendering (mostly because of markdown rendering), the
 //! rendering process has been parallelized. This parallelization is only
 //! exposed through the `crate` method on the context, and then also from the
@@ -37,13 +40,15 @@ mod span_map;
 mod type_layout;
 mod write_shared;
 
+use std::borrow::Cow;
 use std::collections::VecDeque;
 use std::fmt::{self, Display as _, Write};
 use std::iter::Peekable;
 use std::path::PathBuf;
 use std::{fs, str};
 
-use rinja::Template;
+use askama::Template;
+use itertools::Either;
 use rustc_attr_parsing::{
     ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince,
 };
@@ -90,15 +95,28 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
 /// Specifies whether rendering directly implemented trait items or ones from a certain Deref
 /// impl.
 #[derive(Copy, Clone, Debug)]
-pub(crate) enum AssocItemRender<'a> {
+enum AssocItemRender<'a> {
     All,
     DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
 }
 
+impl AssocItemRender<'_> {
+    fn render_mode(&self) -> RenderMode {
+        match self {
+            Self::All => RenderMode::Normal,
+            &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ },
+        }
+    }
+
+    fn class(&self) -> Option<&'static str> {
+        if let Self::DerefFor { .. } = self { Some("impl-items") } else { None }
+    }
+}
+
 /// For different handling of associated items from the Deref target of a type rather than the type
 /// itself.
 #[derive(Copy, Clone, PartialEq)]
-pub(crate) enum RenderMode {
+enum RenderMode {
     Normal,
     ForDeref { mut_: bool },
 }
@@ -126,7 +144,7 @@ pub(crate) struct IndexItem {
 
 /// A type used for the search index.
 #[derive(Debug, Eq, PartialEq)]
-pub(crate) struct RenderType {
+struct RenderType {
     id: Option<RenderTypeId>,
     generics: Option<Vec<RenderType>>,
     bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
@@ -137,7 +155,7 @@ impl RenderType {
     // The contents of the lists are always integers in self-terminating hex
     // form, handled by `RenderTypeId::write_to_string`, so no commas are
     // needed to separate the items.
-    pub fn write_to_string(&self, string: &mut String) {
+    fn write_to_string(&self, string: &mut String) {
         fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
             // 0 is a sentinel, everything else is one-indexed
             match id {
@@ -177,7 +195,7 @@ impl RenderType {
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub(crate) enum RenderTypeId {
+enum RenderTypeId {
     DefId(DefId),
     Primitive(clean::PrimitiveType),
     AssociatedType(Symbol),
@@ -186,7 +204,7 @@ pub(crate) enum RenderTypeId {
 }
 
 impl RenderTypeId {
-    pub fn write_to_string(&self, string: &mut String) {
+    fn write_to_string(&self, string: &mut String) {
         let id: i32 = match &self {
             // 0 is a sentinel, everything else is one-indexed
             // concrete type
@@ -205,11 +223,11 @@ pub(crate) struct IndexItemFunctionType {
     inputs: Vec<RenderType>,
     output: Vec<RenderType>,
     where_clause: Vec<Vec<RenderType>>,
-    param_names: Vec<Symbol>,
+    param_names: Vec<Option<Symbol>>,
 }
 
 impl IndexItemFunctionType {
-    pub fn write_to_string<'a>(
+    fn write_to_string<'a>(
         &'a self,
         string: &mut String,
         backref_queue: &mut VecDeque<&'a IndexItemFunctionType>,
@@ -309,7 +327,7 @@ impl ItemEntry {
 }
 
 impl ItemEntry {
-    pub(crate) fn print(&self) -> impl fmt::Display {
+    fn print(&self) -> impl fmt::Display {
         fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
     }
 }
@@ -436,44 +454,49 @@ impl AllTypes {
         sections
     }
 
-    fn print(&self, f: &mut String) {
-        fn print_entries(f: &mut String, e: &FxIndexSet<ItemEntry>, kind: ItemSection) {
-            if !e.is_empty() {
+    fn print(&self) -> impl fmt::Display {
+        fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display {
+            fmt::from_fn(move |f| {
+                if e.is_empty() {
+                    return Ok(());
+                }
+
                 let mut e: Vec<&ItemEntry> = e.iter().collect();
                 e.sort();
-                write_str(
+                write!(
                     f,
-                    format_args!(
-                        "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
-                        id = kind.id(),
-                        title = kind.name(),
-                    ),
-                );
+                    "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
+                    id = kind.id(),
+                    title = kind.name(),
+                )?;
 
                 for s in e.iter() {
-                    write_str(f, format_args!("<li>{}</li>", s.print()));
+                    write!(f, "<li>{}</li>", s.print())?;
                 }
 
-                f.push_str("</ul>");
-            }
+                f.write_str("</ul>")
+            })
         }
 
-        f.push_str("<h1>List of all items</h1>");
-        // Note: print_entries does not escape the title, because we know the current set of titles
-        // doesn't require escaping.
-        print_entries(f, &self.structs, ItemSection::Structs);
-        print_entries(f, &self.enums, ItemSection::Enums);
-        print_entries(f, &self.unions, ItemSection::Unions);
-        print_entries(f, &self.primitives, ItemSection::PrimitiveTypes);
-        print_entries(f, &self.traits, ItemSection::Traits);
-        print_entries(f, &self.macros, ItemSection::Macros);
-        print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros);
-        print_entries(f, &self.derive_macros, ItemSection::DeriveMacros);
-        print_entries(f, &self.functions, ItemSection::Functions);
-        print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
-        print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
-        print_entries(f, &self.statics, ItemSection::Statics);
-        print_entries(f, &self.constants, ItemSection::Constants);
+        fmt::from_fn(|f| {
+            f.write_str("<h1>List of all items</h1>")?;
+            // Note: print_entries does not escape the title, because we know the current set of titles
+            // doesn't require escaping.
+            print_entries(&self.structs, ItemSection::Structs).fmt(f)?;
+            print_entries(&self.enums, ItemSection::Enums).fmt(f)?;
+            print_entries(&self.unions, ItemSection::Unions).fmt(f)?;
+            print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?;
+            print_entries(&self.traits, ItemSection::Traits).fmt(f)?;
+            print_entries(&self.macros, ItemSection::Macros).fmt(f)?;
+            print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?;
+            print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?;
+            print_entries(&self.functions, ItemSection::Functions).fmt(f)?;
+            print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?;
+            print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?;
+            print_entries(&self.statics, ItemSection::Statics).fmt(f)?;
+            print_entries(&self.constants, ItemSection::Constants).fmt(f)?;
+            Ok(())
+        })
     }
 }
 
@@ -760,7 +783,7 @@ fn short_item_info(
 
 // Render the list of items inside one of the sections "Trait Implementations",
 // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
-pub(crate) fn render_impls(
+fn render_impls(
     cx: &Context<'_>,
     mut w: impl Write,
     impls: &[&Impl],
@@ -1201,8 +1224,8 @@ impl<'a> AssocItemLink<'a> {
     }
 }
 
-pub fn write_section_heading(
-    title: &str,
+fn write_section_heading(
+    title: impl fmt::Display,
     id: &str,
     extra_class: Option<&str>,
     extra: impl fmt::Display,
@@ -1222,11 +1245,11 @@ pub fn write_section_heading(
     })
 }
 
-fn write_impl_section_heading(title: &str, id: &str) -> impl fmt::Display {
+fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
     write_section_heading(title, id, None, "")
 }
 
-pub(crate) fn render_all_impls(
+fn render_all_impls(
     mut w: impl Write,
     cx: &Context<'_>,
     containing_item: &clean::Item,
@@ -1299,20 +1322,17 @@ fn render_assoc_items_inner(
     let (mut non_trait, traits): (Vec<_>, _) =
         v.iter().partition(|i| i.inner_impl().trait_.is_none());
     if !non_trait.is_empty() {
-        let mut close_tags = <Vec<&str>>::with_capacity(1);
-        let mut tmp_buf = String::new();
-        let (render_mode, id, class_html) = match what {
-            AssocItemRender::All => {
-                write_str(
-                    &mut tmp_buf,
-                    format_args!(
-                        "{}",
-                        write_impl_section_heading("Implementations", "implementations")
-                    ),
-                );
-                (RenderMode::Normal, "implementations-list".to_owned(), "")
-            }
-            AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
+        let render_mode = what.render_mode();
+        let class_html = what
+            .class()
+            .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
+            .maybe_display();
+        let (section_heading, id) = match what {
+            AssocItemRender::All => (
+                Either::Left(write_impl_section_heading("Implementations", "implementations")),
+                Cow::Borrowed("implementations-list"),
+            ),
+            AssocItemRender::DerefFor { trait_, type_, .. } => {
                 let id =
                     cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
                 // the `impls.get` above only looks at the outermost type,
@@ -1326,25 +1346,27 @@ fn render_assoc_items_inner(
                     type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
                 });
                 let derived_id = cx.derive_id(&id);
-                close_tags.push("</details>");
-                write_str(
-                    &mut tmp_buf,
-                    format_args!(
-                        "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
-                        write_impl_section_heading(
-                            &format!(
-                                "<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>",
-                                trait_ = trait_.print(cx),
-                                type_ = type_.print(cx),
-                            ),
-                            &id,
-                        )
-                    ),
-                );
                 if let Some(def_id) = type_.def_id(cx.cache()) {
-                    cx.deref_id_map.borrow_mut().insert(def_id, id);
+                    cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
                 }
-                (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#)
+                (
+                    Either::Right(fmt::from_fn(move |f| {
+                        write!(
+                            f,
+                            "<details class=\"toggle big-toggle\" open><summary>{}</summary>",
+                            write_impl_section_heading(
+                                fmt::from_fn(|f| write!(
+                                    f,
+                                    "<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>",
+                                    trait_ = trait_.print(cx),
+                                    type_ = type_.print(cx),
+                                )),
+                                &id,
+                            )
+                        )
+                    })),
+                    Cow::Owned(derived_id),
+                )
             }
         };
         let mut impls_buf = String::new();
@@ -1372,10 +1394,14 @@ fn render_assoc_items_inner(
             );
         }
         if !impls_buf.is_empty() {
-            write!(w, "{tmp_buf}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap();
-            for tag in close_tags.into_iter().rev() {
-                w.write_str(tag).unwrap();
-            }
+            write!(
+                w,
+                "{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
+                matches!(what, AssocItemRender::DerefFor { .. })
+                    .then_some("</details>")
+                    .maybe_display(),
+            )
+            .unwrap();
         }
     }
 
@@ -1473,10 +1499,7 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
     }
 }
 
-pub(crate) fn notable_traits_button(
-    ty: &clean::Type,
-    cx: &Context<'_>,
-) -> Option<impl fmt::Display> {
+fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
     if ty.is_unit() {
         // Very common fast path.
         return None;
@@ -1588,10 +1611,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
     (format!("{:#}", ty.print(cx)), out)
 }
 
-pub(crate) fn notable_traits_json<'a>(
-    tys: impl Iterator<Item = &'a clean::Type>,
-    cx: &Context<'_>,
-) -> String {
+fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
     let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
     mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
     struct NotableTraitsMap(Vec<(String, String)>);
@@ -1642,8 +1662,8 @@ fn render_impl(
         // `containing_item` is used for rendering stability info. If the parent is a trait impl,
         // `containing_item` will the grandparent, since trait impls can't have stability attached.
         fn doc_impl_item(
-            boring: &mut String,
-            interesting: &mut String,
+            boring: impl fmt::Write,
+            interesting: impl fmt::Write,
             cx: &Context<'_>,
             item: &clean::Item,
             parent: &clean::Item,
@@ -1652,7 +1672,7 @@ fn render_impl(
             is_default_item: bool,
             trait_: Option<&clean::Trait>,
             rendering_params: ImplRenderingParameters,
-        ) {
+        ) -> fmt::Result {
             let item_type = item.type_();
             let name = item.name.as_ref().unwrap();
 
@@ -1727,15 +1747,16 @@ fn render_impl(
                     );
                 }
             }
-            let w = if short_documented && trait_.is_some() { interesting } else { boring };
+            let mut w = if short_documented && trait_.is_some() {
+                Either::Left(interesting)
+            } else {
+                Either::Right(boring)
+            };
 
             let toggled = !doc_buffer.is_empty();
             if toggled {
                 let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
-                write_str(
-                    w,
-                    format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
-                );
+                write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
             }
             match &item.kind {
                 clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
@@ -1750,172 +1771,151 @@ fn render_impl(
                                     .find(|item| item.name.map(|n| n == *name).unwrap_or(false))
                             })
                             .map(|item| format!("{}.{name}", item.type_()));
-                        write_str(
+                        write!(
                             w,
-                            format_args!(
-                                "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
+                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
                                 {}",
-                                render_rightside(cx, item, render_mode)
-                            ),
-                        );
+                            render_rightside(cx, item, render_mode)
+                        )?;
                         if trait_.is_some() {
                             // Anchors are only used on trait impls.
-                            write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
+                            write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
                         }
-                        write_str(
+                        write!(
                             w,
-                            format_args!(
-                                "<h4 class=\"code-header\">{}</h4></section>",
-                                render_assoc_item(
-                                    item,
-                                    link.anchor(source_id.as_ref().unwrap_or(&id)),
-                                    ItemType::Impl,
-                                    cx,
-                                    render_mode,
-                                ),
+                            "<h4 class=\"code-header\">{}</h4></section>",
+                            render_assoc_item(
+                                item,
+                                link.anchor(source_id.as_ref().unwrap_or(&id)),
+                                ItemType::Impl,
+                                cx,
+                                render_mode,
                             ),
-                        );
+                        )?;
                     }
                 }
                 clean::RequiredAssocConstItem(generics, ty) => {
                     let source_id = format!("{item_type}.{name}");
                     let id = cx.derive_id(&source_id);
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
+                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
                             {}",
-                            render_rightside(cx, item, render_mode)
-                        ),
-                    );
+                        render_rightside(cx, item, render_mode)
+                    )?;
                     if trait_.is_some() {
                         // Anchors are only used on trait impls.
-                        write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
+                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
                     }
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<h4 class=\"code-header\">{}</h4></section>",
-                            assoc_const(
-                                item,
-                                generics,
-                                ty,
-                                AssocConstValue::None,
-                                link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                                0,
-                                cx,
-                            )
+                        "<h4 class=\"code-header\">{}</h4></section>",
+                        assoc_const(
+                            item,
+                            generics,
+                            ty,
+                            AssocConstValue::None,
+                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
+                            0,
+                            cx,
                         ),
-                    );
+                    )?;
                 }
                 clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
                     let source_id = format!("{item_type}.{name}");
                     let id = cx.derive_id(&source_id);
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
+                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
                             {}",
-                            render_rightside(cx, item, render_mode)
-                        ),
-                    );
+                        render_rightside(cx, item, render_mode),
+                    )?;
                     if trait_.is_some() {
                         // Anchors are only used on trait impls.
-                        write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
+                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
                     }
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<h4 class=\"code-header\">{}</h4></section>",
-                            assoc_const(
-                                item,
-                                &ci.generics,
-                                &ci.type_,
-                                match item.kind {
-                                    clean::ProvidedAssocConstItem(_) =>
-                                        AssocConstValue::TraitDefault(&ci.kind),
-                                    clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
-                                    _ => unreachable!(),
-                                },
-                                link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                                0,
-                                cx,
-                            )
+                        "<h4 class=\"code-header\">{}</h4></section>",
+                        assoc_const(
+                            item,
+                            &ci.generics,
+                            &ci.type_,
+                            match item.kind {
+                                clean::ProvidedAssocConstItem(_) =>
+                                    AssocConstValue::TraitDefault(&ci.kind),
+                                clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
+                                _ => unreachable!(),
+                            },
+                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
+                            0,
+                            cx,
                         ),
-                    );
+                    )?;
                 }
                 clean::RequiredAssocTypeItem(generics, bounds) => {
                     let source_id = format!("{item_type}.{name}");
                     let id = cx.derive_id(&source_id);
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
+                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
                             {}",
-                            render_rightside(cx, item, render_mode)
-                        ),
-                    );
+                        render_rightside(cx, item, render_mode),
+                    )?;
                     if trait_.is_some() {
                         // Anchors are only used on trait impls.
-                        write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
+                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
                     }
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<h4 class=\"code-header\">{}</h4></section>",
-                            assoc_type(
-                                item,
-                                generics,
-                                bounds,
-                                None,
-                                link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                                0,
-                                cx,
-                            )
+                        "<h4 class=\"code-header\">{}</h4></section>",
+                        assoc_type(
+                            item,
+                            generics,
+                            bounds,
+                            None,
+                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
+                            0,
+                            cx,
                         ),
-                    );
+                    )?;
                 }
                 clean::AssocTypeItem(tydef, _bounds) => {
                     let source_id = format!("{item_type}.{name}");
                     let id = cx.derive_id(&source_id);
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
+                        "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
                             {}",
-                            render_rightside(cx, item, render_mode)
-                        ),
-                    );
+                        render_rightside(cx, item, render_mode),
+                    )?;
                     if trait_.is_some() {
                         // Anchors are only used on trait impls.
-                        write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
+                        write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
                     }
-                    write_str(
+                    write!(
                         w,
-                        format_args!(
-                            "<h4 class=\"code-header\">{}</h4></section>",
-                            assoc_type(
-                                item,
-                                &tydef.generics,
-                                &[], // intentionally leaving out bounds
-                                Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
-                                link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                                0,
-                                cx,
-                            )
+                        "<h4 class=\"code-header\">{}</h4></section>",
+                        assoc_type(
+                            item,
+                            &tydef.generics,
+                            &[], // intentionally leaving out bounds
+                            Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
+                            link.anchor(if trait_.is_some() { &source_id } else { &id }),
+                            0,
+                            cx,
                         ),
-                    );
+                    )?;
                 }
-                clean::StrippedItem(..) => return,
+                clean::StrippedItem(..) => return Ok(()),
                 _ => panic!("can't make docs for trait item with name {:?}", item.name),
             }
 
-            w.push_str(&info_buffer);
+            w.write_str(&info_buffer)?;
             if toggled {
-                w.push_str("</summary>");
-                w.push_str(&doc_buffer);
-                w.push_str("</details>");
+                write!(w, "</summary>{doc_buffer}</details>")?;
             }
+            Ok(())
         }
 
         let mut impl_items = String::new();
@@ -1958,7 +1958,7 @@ fn render_impl(
                             false,
                             trait_,
                             rendering_params,
-                        );
+                        )?;
                     }
                     _ => {}
                 }
@@ -1976,7 +1976,7 @@ fn render_impl(
                     false,
                     trait_,
                     rendering_params,
-                );
+                )?;
             }
             for method in methods {
                 doc_impl_item(
@@ -1990,20 +1990,20 @@ fn render_impl(
                     false,
                     trait_,
                     rendering_params,
-                );
+                )?;
             }
         }
 
         fn render_default_items(
-            boring: &mut String,
-            interesting: &mut String,
+            mut boring: impl fmt::Write,
+            mut interesting: impl fmt::Write,
             cx: &Context<'_>,
             t: &clean::Trait,
             i: &clean::Impl,
             parent: &clean::Item,
             render_mode: RenderMode,
             rendering_params: ImplRenderingParameters,
-        ) {
+        ) -> fmt::Result {
             for trait_item in &t.items {
                 // Skip over any default trait items that are impossible to reference
                 // (e.g. if it has a `Self: Sized` bound on an unsized type).
@@ -2023,8 +2023,8 @@ fn render_impl(
                 let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
 
                 doc_impl_item(
-                    boring,
-                    interesting,
+                    &mut boring,
+                    &mut interesting,
                     cx,
                     trait_item,
                     parent,
@@ -2033,8 +2033,9 @@ fn render_impl(
                     true,
                     Some(t),
                     rendering_params,
-                );
+                )?;
             }
+            Ok(())
         }
 
         // If we've implemented a trait, then also emit documentation for all
@@ -2054,7 +2055,7 @@ fn render_impl(
                     &i.impl_item,
                     render_mode,
                     rendering_params,
-                );
+                )?;
             }
         }
         if render_mode == RenderMode::Normal {
@@ -2171,7 +2172,7 @@ fn render_rightside(
     })
 }
 
-pub(crate) fn render_impl_summary(
+fn render_impl_summary(
     cx: &Context<'_>,
     i: &Impl,
     parent: &clean::Item,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 3c5c2ce1976..39a631b637b 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -2,7 +2,7 @@ use std::cmp::Ordering;
 use std::fmt::{self, Display, Write as _};
 use std::iter;
 
-use rinja::Template;
+use askama::Template;
 use rustc_abi::VariantIdx;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_hir as hir;
@@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId;
 use rustc_index::IndexVec;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::hygiene::MacroKind;
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::symbol::{Symbol, sym};
 use tracing::{debug, info};
 
 use super::type_layout::document_type_layout;
@@ -37,7 +37,7 @@ use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
 use crate::html::render::{document_full, document_item_info};
 use crate::html::url_parts_builder::UrlPartsBuilder;
 
-/// Generates a Rinja template struct for rendering items with common methods.
+/// Generates an Askama template struct for rendering items with common methods.
 ///
 /// Usage:
 /// ```ignore (illustrative)
@@ -301,7 +301,7 @@ fn toggle_close(mut w: impl fmt::Write) {
     w.write_str("</details>").unwrap();
 }
 
-trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + Display {
+trait ItemTemplate<'a, 'cx: 'a>: askama::Template + Display {
     fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>);
 }
 
@@ -347,9 +347,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
                 // but we actually want stable items to come first
                 return is_stable2.cmp(&is_stable1);
             }
-            let lhs = i1.name.unwrap_or(kw::Empty);
-            let rhs = i2.name.unwrap_or(kw::Empty);
-            compare_names(lhs.as_str(), rhs.as_str())
+            match (i1.name, i2.name) {
+                (Some(name1), Some(name2)) => compare_names(name1.as_str(), name2.as_str()),
+                (Some(_), None) => Ordering::Greater,
+                (None, Some(_)) => Ordering::Less,
+                (None, None) => Ordering::Equal,
+            }
         }
 
         let tcx = cx.tcx();
@@ -1229,12 +1232,13 @@ fn item_trait_alias(
         wrap_item(w, |w| {
             write!(
                 w,
-                "{attrs}trait {name}{generics}{where_b} = {bounds};",
+                "{attrs}trait {name}{generics} = {bounds}{where_clause};",
                 attrs = render_attributes_in_pre(it, "", cx),
                 name = it.name.unwrap(),
                 generics = t.generics.print(cx),
-                where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(),
                 bounds = bounds(&t.bounds, true, cx),
+                where_clause =
+                    print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(),
             )
         })?;
 
@@ -1867,7 +1871,7 @@ fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) ->
                     }
                 }
             }
-            Ok(())
+            fmt::Result::Ok(())
         })?;
         write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
     })
@@ -1944,7 +1948,7 @@ fn item_constant(
                     }
                 }
             }
-            Ok(())
+            Ok::<(), fmt::Error>(())
         })?;
 
         write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index b39701fae1d..aff8684ee3a 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -709,8 +709,11 @@ pub(crate) fn build_index(
                 let mut result = Vec::new();
                 for (index, item) in self.items.iter().enumerate() {
                     if let Some(ty) = &item.search_type
-                        && let my =
-                            ty.param_names.iter().map(|sym| sym.as_str()).collect::<Vec<_>>()
+                        && let my = ty
+                            .param_names
+                            .iter()
+                            .filter_map(|sym| sym.map(|sym| sym.to_string()))
+                            .collect::<Vec<_>>()
                         && my != prev
                     {
                         result.push((index, my.join(",")));
@@ -1109,7 +1112,7 @@ fn simplify_fn_type<'a, 'tcx>(
         }
         Type::BareFunction(ref bf) => {
             let mut ty_generics = Vec::new();
-            for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) {
+            for ty in bf.decl.inputs.iter().map(|arg| &arg.type_) {
                 simplify_fn_type(
                     self_,
                     generics,
@@ -1372,7 +1375,7 @@ fn simplify_fn_constraint<'a>(
 /// Used to allow type-based search on constants and statics.
 fn make_nullary_fn(
     clean_type: &clean::Type,
-) -> (Vec<RenderType>, Vec<RenderType>, Vec<Symbol>, Vec<Vec<RenderType>>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Option<Symbol>>, Vec<Vec<RenderType>>) {
     let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
     let output = get_index_type(clean_type, vec![], &mut rgen);
     (vec![], vec![output], vec![], vec![])
@@ -1387,7 +1390,7 @@ fn get_fn_inputs_and_outputs(
     tcx: TyCtxt<'_>,
     impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>,
     cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>, Vec<Symbol>, Vec<Vec<RenderType>>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Option<Symbol>>, Vec<Vec<RenderType>>) {
     let decl = &func.decl;
 
     let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
@@ -1415,15 +1418,15 @@ fn get_fn_inputs_and_outputs(
         (None, &func.generics)
     };
 
-    let mut arg_types = Vec::new();
-    for arg in decl.inputs.values.iter() {
+    let mut param_types = Vec::new();
+    for param in decl.inputs.iter() {
         simplify_fn_type(
             self_,
             generics,
-            &arg.type_,
+            &param.type_,
             tcx,
             0,
-            &mut arg_types,
+            &mut param_types,
             &mut rgen,
             false,
             cache,
@@ -1436,15 +1439,15 @@ fn get_fn_inputs_and_outputs(
     let mut simplified_params = rgen.into_iter().collect::<Vec<_>>();
     simplified_params.sort_by_key(|(_, (idx, _))| -idx);
     (
-        arg_types,
+        param_types,
         ret_types,
         simplified_params
             .iter()
             .map(|(name, (_idx, _traits))| match name {
-                SimplifiedParam::Symbol(name) => *name,
-                SimplifiedParam::Anonymous(_) => kw::Empty,
+                SimplifiedParam::Symbol(name) => Some(*name),
+                SimplifiedParam::Anonymous(_) => None,
                 SimplifiedParam::AssociatedType(def_id, name) => {
-                    Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name))
+                    Some(Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name)))
                 }
             })
             .collect(),
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 89ff61ecb03..cd0c9775f5c 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -1,7 +1,7 @@
 use std::borrow::Cow;
 use std::cmp::Ordering;
 
-use rinja::Template;
+use askama::Template;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::{DefIdMap, DefIdSet};
@@ -123,10 +123,10 @@ impl<'a> Link<'a> {
 pub(crate) mod filters {
     use std::fmt::{self, Display};
 
-    use rinja::filters::Safe;
+    use askama::filters::Safe;
 
     use crate::html::escape::EscapeBodyTextWithWbr;
-    pub(crate) fn wrapped<T>(v: T) -> rinja::Result<Safe<impl Display>>
+    pub(crate) fn wrapped<T>(v: T) -> askama::Result<Safe<impl Display>>
     where
         T: Display,
     {
diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs
index 657cd3c82aa..327a30887b1 100644
--- a/src/librustdoc/html/render/tests.rs
+++ b/src/librustdoc/html/render/tests.rs
@@ -47,8 +47,7 @@ fn test_all_types_prints_header_once() {
     // Regression test for #82477
     let all_types = AllTypes::new();
 
-    let mut buffer = String::new();
-    all_types.print(&mut buffer);
+    let buffer = all_types.print().to_string();
 
     assert_eq!(1, buffer.matches("List of all items").count());
 }
diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs
index a1ee5c8c548..fb1f0271c2a 100644
--- a/src/librustdoc/html/render/type_layout.rs
+++ b/src/librustdoc/html/render/type_layout.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use rinja::Template;
+use askama::Template;
 use rustc_abi::{Primitive, TagEncoding, Variants};
 use rustc_hir::def_id::DefId;
 use rustc_middle::span_bug;