From 73a0c79fe61f7797ed01638437d6fe3801d22058 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Mon, 25 Jan 2021 22:53:00 +0100
Subject: [PATCH 01/25] build_local_trait_impl: reuse memory

Co-authored-by: Joshua Nelson <joshua@yottadb.com>
---
 src/librustdoc/passes/collect_trait_impls.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 6ec6620681bf5..ecfe7b4870c00 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -56,11 +56,11 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
 
     // `tcx.crates()` doesn't include the local crate, and `tcx.all_trait_implementations`
     // doesn't work with it anyway, so pull them from the HIR map instead
+    let mut extra_attrs = Vec::new();
     for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() {
         for &impl_node in cx.tcx.hir().trait_impls(trait_did) {
             let impl_did = cx.tcx.hir().local_def_id(impl_node);
             cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| {
-                let mut extra_attrs = Vec::new();
                 let mut parent = cx.tcx.parent(impl_did.to_def_id());
                 while let Some(did) = parent {
                     extra_attrs.extend(
@@ -86,6 +86,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
                     Some(&extra_attrs),
                     &mut new_items,
                 );
+                extra_attrs.clear();
             });
         }
     }

From 30c264631d7ea21c59a853187b3b48020e0b762a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 19:40:52 +0100
Subject: [PATCH 02/25] Cleanup: only call to_def_id once

---
 src/librustdoc/passes/collect_trait_impls.rs | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index ecfe7b4870c00..a06b55fb3937b 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -59,9 +59,9 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
     let mut extra_attrs = Vec::new();
     for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() {
         for &impl_node in cx.tcx.hir().trait_impls(trait_did) {
-            let impl_did = cx.tcx.hir().local_def_id(impl_node);
+            let impl_did = cx.tcx.hir().local_def_id(impl_node).to_def_id();
             cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| {
-                let mut parent = cx.tcx.parent(impl_did.to_def_id());
+                let mut parent = cx.tcx.parent(impl_did);
                 while let Some(did) = parent {
                     extra_attrs.extend(
                         cx.tcx
@@ -79,13 +79,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
                     );
                     parent = cx.tcx.parent(did);
                 }
-                inline::build_impl(
-                    cx,
-                    None,
-                    impl_did.to_def_id(),
-                    Some(&extra_attrs),
-                    &mut new_items,
-                );
+                inline::build_impl(cx, None, impl_did, Some(&extra_attrs), &mut new_items);
                 extra_attrs.clear();
             });
         }

From d6f80ad489e1adabbeb01cebf2f8295e264dfb9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 19:48:54 +0100
Subject: [PATCH 03/25] Use ArrayVec instead of SmallVec

---
 Cargo.lock                    |  1 +
 src/librustdoc/Cargo.toml     |  1 +
 src/librustdoc/clean/types.rs | 20 ++++++++++----------
 3 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index a4ba7704426e8..9149ffeded952 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4386,6 +4386,7 @@ dependencies = [
 name = "rustdoc"
 version = "0.0.0"
 dependencies = [
+ "arrayvec",
  "expect-test",
  "itertools 0.9.0",
  "minifier",
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index db64b31f31cfc..b6965898b4e44 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2018"
 path = "lib.rs"
 
 [dependencies]
+arrayvec = { version = "0.5.1", default-features = false }
 pulldown-cmark = { version = "0.8", default-features = false }
 minifier = "0.0.33"
 rayon = { version = "0.3.0", package = "rustc-rayon" }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index bebb746c92d2f..c8fa6c54c425a 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -8,6 +8,7 @@ use std::rc::Rc;
 use std::sync::Arc;
 use std::{slice, vec};
 
+use arrayvec::ArrayVec;
 use rustc_ast::attr;
 use rustc_ast::util::comments::beautify_doc_string;
 use rustc_ast::{self as ast, AttrStyle};
@@ -28,7 +29,6 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol, SymbolStr};
 use rustc_span::{self, FileName, Loc};
 use rustc_target::abi::VariantIdx;
 use rustc_target::spec::abi::Abi;
-use smallvec::{smallvec, SmallVec};
 
 use crate::clean::cfg::Cfg;
 use crate::clean::external_path;
@@ -1539,12 +1539,12 @@ impl PrimitiveType {
         }
     }
 
-    crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static SmallVec<[DefId; 4]> {
+    crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec<[DefId; 4]> {
         Self::all_impls(tcx).get(self).expect("missing impl for primitive type")
     }
 
-    crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap<PrimitiveType, SmallVec<[DefId; 4]>> {
-        static CELL: OnceCell<FxHashMap<PrimitiveType, SmallVec<[DefId; 4]>>> = OnceCell::new();
+    crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap<PrimitiveType, ArrayVec<[DefId; 4]>> {
+        static CELL: OnceCell<FxHashMap<PrimitiveType, ArrayVec<[DefId; 4]>>> = OnceCell::new();
 
         CELL.get_or_init(move || {
             use self::PrimitiveType::*;
@@ -1568,7 +1568,7 @@ impl PrimitiveType {
             }
 
             let single = |a: Option<DefId>| a.into_iter().collect();
-            let both = |a: Option<DefId>, b: Option<DefId>| -> SmallVec<_> {
+            let both = |a: Option<DefId>, b: Option<DefId>| -> ArrayVec<_> {
                 a.into_iter().chain(b).collect()
             };
 
@@ -1601,8 +1601,8 @@ impl PrimitiveType {
                         .collect()
                 },
                 Array => single(lang_items.array_impl()),
-                Tuple => smallvec![],
-                Unit => smallvec![],
+                Tuple => ArrayVec::new(),
+                Unit => ArrayVec::new(),
                 RawPointer => {
                     lang_items
                         .const_ptr_impl()
@@ -1612,9 +1612,9 @@ impl PrimitiveType {
                         .chain(lang_items.mut_slice_ptr_impl())
                         .collect()
                 },
-                Reference => smallvec![],
-                Fn => smallvec![],
-                Never => smallvec![],
+                Reference => ArrayVec::new(),
+                Fn => ArrayVec::new(),
+                Never => ArrayVec::new(),
             }
         })
     }

From fcdaffcb8740a787fc50113c3dab7e0bfaf8d860 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 20:35:06 +0100
Subject: [PATCH 04/25] Clean up build_deref_target_impls

---
 src/librustdoc/clean/utils.rs | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 50ec6e69fbd40..f9accd6b3253d 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -322,20 +322,14 @@ crate fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut
             ItemKind::TypedefItem(ref t, true) => &t.type_,
             _ => continue,
         };
-        let primitive = match *target {
-            ResolvedPath { did, .. } if did.is_local() => continue,
-            ResolvedPath { did, .. } => {
-                ret.extend(inline::build_impls(cx, None, did, None));
-                continue;
+
+        if let Some(prim) = target.primitive_type() {
+            for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) {
+                inline::build_impl(cx, None, did, None, ret);
             }
-            _ => match target.primitive_type() {
-                Some(prim) => prim,
-                None => continue,
-            },
-        };
-        for &did in primitive.impls(tcx) {
+        } else if let ResolvedPath { did, .. } = *target {
             if !did.is_local() {
-                inline::build_impl(cx, None, did, None, ret);
+                ret.extend(inline::build_impls(cx, None, did, None));
             }
         }
     }

From 0d9d9edc74bac97d43cfa70d9f50d77e1f8e55a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 20:38:36 +0100
Subject: [PATCH 05/25] Align try_inline and try_inline_glob

---
 src/librustdoc/clean/inline.rs | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 1f9e7f8ae5cd4..590c2d08dea16 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -133,10 +133,7 @@ crate fn try_inline_glob(
     res: Res,
     visited: &mut FxHashSet<DefId>,
 ) -> Option<Vec<clean::Item>> {
-    if res == Res::Err {
-        return None;
-    }
-    let did = res.def_id();
+    let did = res.opt_def_id()?;
     if did.is_local() {
         return None;
     }

From b59efa02cfacee4799131dbba14fe19190db980a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 22:44:52 +0100
Subject: [PATCH 06/25] Avoid unnecessary re-hashing

---
 src/librustdoc/core.rs | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 16f11e460e6f0..1062a3e8708f9 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -150,11 +150,9 @@ impl<'tcx> DocContext<'tcx> {
 
         let mut fake_ids = self.fake_def_ids.borrow_mut();
 
-        let def_id = *fake_ids.entry(crate_num).or_insert(start_def_id);
-        fake_ids.insert(
-            crate_num,
-            DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) },
-        );
+        let def_id = fake_ids.entry(crate_num).or_insert(start_def_id);
+        *def_id = DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) };
+        let def_id = *def_id;
 
         MAX_DEF_ID.with(|m| {
             m.borrow_mut().entry(def_id.krate).or_insert(start_def_id);

From a9c20944da37abfcaa985ff9a8121fe9b477a79f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 22:49:57 +0100
Subject: [PATCH 07/25] Avoid writing MAX_DEF_ID

---
 src/librustdoc/core.rs | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 1062a3e8708f9..73c0e551deb27 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -24,9 +24,12 @@ use rustc_span::source_map;
 use rustc_span::symbol::sym;
 use rustc_span::DUMMY_SP;
 
-use std::cell::{Cell, RefCell};
 use std::mem;
 use std::rc::Rc;
+use std::{
+    cell::{Cell, RefCell},
+    collections::hash_map::Entry,
+};
 
 use crate::clean;
 use crate::clean::{AttributesExt, MAX_DEF_ID};
@@ -150,14 +153,18 @@ impl<'tcx> DocContext<'tcx> {
 
         let mut fake_ids = self.fake_def_ids.borrow_mut();
 
-        let def_id = fake_ids.entry(crate_num).or_insert(start_def_id);
+        let def_id = match fake_ids.entry(crate_num) {
+            Entry::Vacant(e) => {
+                MAX_DEF_ID.with(|m| {
+                    m.borrow_mut().insert(crate_num, start_def_id);
+                });
+                e.insert(start_def_id)
+            }
+            Entry::Occupied(e) => e.into_mut(),
+        };
         *def_id = DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) };
         let def_id = *def_id;
 
-        MAX_DEF_ID.with(|m| {
-            m.borrow_mut().entry(def_id.krate).or_insert(start_def_id);
-        });
-
         self.all_fake_def_ids.borrow_mut().insert(def_id);
 
         def_id

From f0b662e728d7386ffa2f37e6c805617abb12488e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 22:54:21 +0100
Subject: [PATCH 08/25] Remove all_fake_def_ids

---
 src/librustdoc/core.rs | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 73c0e551deb27..57a765608fc45 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -67,7 +67,6 @@ crate struct DocContext<'tcx> {
     /// Table synthetic type parameter for `impl Trait` in argument position -> bounds
     crate impl_trait_bounds: RefCell<FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>>,
     crate fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
-    crate all_fake_def_ids: RefCell<FxHashSet<DefId>>,
     /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
     // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
     crate generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
@@ -163,17 +162,16 @@ impl<'tcx> DocContext<'tcx> {
             Entry::Occupied(e) => e.into_mut(),
         };
         *def_id = DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) };
-        let def_id = *def_id;
 
-        self.all_fake_def_ids.borrow_mut().insert(def_id);
-
-        def_id
+        *def_id
     }
 
     /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds.
     /// (This avoids a slice-index-out-of-bounds panic.)
     crate fn as_local_hir_id(&self, def_id: DefId) -> Option<HirId> {
-        if self.all_fake_def_ids.borrow().contains(&def_id) {
+        if MAX_DEF_ID.with(|m| {
+            m.borrow().get(&def_id.krate).map(|id| id.index <= def_id.index).unwrap_or(false)
+        }) {
             None
         } else {
             def_id.as_local().map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id))
@@ -522,7 +520,6 @@ crate fn run_global_ctxt(
         ct_substs: Default::default(),
         impl_trait_bounds: Default::default(),
         fake_def_ids: Default::default(),
-        all_fake_def_ids: Default::default(),
         generated_synthetics: Default::default(),
         auto_traits: tcx
             .all_traits(LOCAL_CRATE)

From 90ef94e73957cc602a28ecf92eec4c201acd1309 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 22:59:26 +0100
Subject: [PATCH 09/25] Only calculate the start_def_id if necessary

---
 src/librustdoc/core.rs | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 57a765608fc45..c741f9082c307 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -140,20 +140,20 @@ impl<'tcx> DocContext<'tcx> {
     /// [`Debug`]: std::fmt::Debug
     /// [`clean::Item`]: crate::clean::types::Item
     crate fn next_def_id(&self, crate_num: CrateNum) -> DefId {
-        let start_def_id = {
-            let num_def_ids = if crate_num == LOCAL_CRATE {
-                self.tcx.hir().definitions().def_path_table().num_def_ids()
-            } else {
-                self.enter_resolver(|r| r.cstore().num_def_ids(crate_num))
-            };
-
-            DefId { krate: crate_num, index: DefIndex::from_usize(num_def_ids) }
-        };
-
         let mut fake_ids = self.fake_def_ids.borrow_mut();
 
         let def_id = match fake_ids.entry(crate_num) {
             Entry::Vacant(e) => {
+                let start_def_id = {
+                    let num_def_ids = if crate_num == LOCAL_CRATE {
+                        self.tcx.hir().definitions().def_path_table().num_def_ids()
+                    } else {
+                        self.enter_resolver(|r| r.cstore().num_def_ids(crate_num))
+                    };
+
+                    DefId { krate: crate_num, index: DefIndex::from_usize(num_def_ids) }
+                };
+
                 MAX_DEF_ID.with(|m| {
                     m.borrow_mut().insert(crate_num, start_def_id);
                 });

From 76cd3ba151d8d3edaf0a1461946136ed14d840d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 23:36:07 +0100
Subject: [PATCH 10/25] No need to store crate num twice

---
 src/librustdoc/clean/types.rs |  8 ++++----
 src/librustdoc/core.rs        | 26 +++++++++++++-------------
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index c8fa6c54c425a..18683f6809f44 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -17,7 +17,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_feature::UnstableFeatures;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId};
+use rustc_hir::def_id::{CrateNum, DefId, DefIndex};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::Mutability;
 use rustc_index::vec::IndexVec;
@@ -45,7 +45,7 @@ use self::ItemKind::*;
 use self::SelfTy::*;
 use self::Type::*;
 
-thread_local!(crate static MAX_DEF_ID: RefCell<FxHashMap<CrateNum, DefId>> = Default::default());
+thread_local!(crate static MAX_DEF_IDX: RefCell<FxHashMap<CrateNum, DefIndex>> = Default::default());
 
 #[derive(Clone, Debug)]
 crate struct Crate {
@@ -293,8 +293,8 @@ impl Item {
     ///
     /// [`next_def_id()`]: DocContext::next_def_id()
     crate fn is_fake(&self) -> bool {
-        MAX_DEF_ID.with(|m| {
-            m.borrow().get(&self.def_id.krate).map(|id| self.def_id >= *id).unwrap_or(false)
+        MAX_DEF_IDX.with(|m| {
+            m.borrow().get(&self.def_id.krate).map(|&idx| idx <= self.def_id.index).unwrap_or(false)
         })
     }
 }
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index c741f9082c307..8eea102fa2fdb 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -32,7 +32,7 @@ use std::{
 };
 
 use crate::clean;
-use crate::clean::{AttributesExt, MAX_DEF_ID};
+use crate::clean::{AttributesExt, MAX_DEF_IDX};
 use crate::config::{Options as RustdocOptions, RenderOptions};
 use crate::config::{OutputFormat, RenderInfo};
 use crate::formats::cache::Cache;
@@ -66,7 +66,7 @@ crate struct DocContext<'tcx> {
     crate ct_substs: RefCell<FxHashMap<DefId, clean::Constant>>,
     /// Table synthetic type parameter for `impl Trait` in argument position -> bounds
     crate impl_trait_bounds: RefCell<FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>>,
-    crate fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
+    crate fake_def_ids: RefCell<FxHashMap<CrateNum, DefIndex>>,
     /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
     // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
     crate generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
@@ -142,35 +142,35 @@ impl<'tcx> DocContext<'tcx> {
     crate fn next_def_id(&self, crate_num: CrateNum) -> DefId {
         let mut fake_ids = self.fake_def_ids.borrow_mut();
 
-        let def_id = match fake_ids.entry(crate_num) {
+        let def_index = match fake_ids.entry(crate_num) {
             Entry::Vacant(e) => {
-                let start_def_id = {
-                    let num_def_ids = if crate_num == LOCAL_CRATE {
+                let num_def_idx = {
+                    let num_def_idx = if crate_num == LOCAL_CRATE {
                         self.tcx.hir().definitions().def_path_table().num_def_ids()
                     } else {
                         self.enter_resolver(|r| r.cstore().num_def_ids(crate_num))
                     };
 
-                    DefId { krate: crate_num, index: DefIndex::from_usize(num_def_ids) }
+                    DefIndex::from_usize(num_def_idx)
                 };
 
-                MAX_DEF_ID.with(|m| {
-                    m.borrow_mut().insert(crate_num, start_def_id);
+                MAX_DEF_IDX.with(|m| {
+                    m.borrow_mut().insert(crate_num, num_def_idx);
                 });
-                e.insert(start_def_id)
+                e.insert(num_def_idx)
             }
             Entry::Occupied(e) => e.into_mut(),
         };
-        *def_id = DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) };
+        *def_index = DefIndex::from(*def_index + 1);
 
-        *def_id
+        DefId { krate: crate_num, index: *def_index }
     }
 
     /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds.
     /// (This avoids a slice-index-out-of-bounds panic.)
     crate fn as_local_hir_id(&self, def_id: DefId) -> Option<HirId> {
-        if MAX_DEF_ID.with(|m| {
-            m.borrow().get(&def_id.krate).map(|id| id.index <= def_id.index).unwrap_or(false)
+        if MAX_DEF_IDX.with(|m| {
+            m.borrow().get(&def_id.krate).map(|&idx| idx <= def_id.index).unwrap_or(false)
         }) {
             None
         } else {

From b56c383b14635d7b856ed3ba8dfa316b8486cd65 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Tue, 26 Jan 2021 19:29:25 +0100
Subject: [PATCH 11/25] Use drain to avoid moving elements of the vector twice

---
 src/librustdoc/passes/collect_trait_impls.rs | 29 +++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index a06b55fb3937b..6f992b3a916ee 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -132,25 +132,28 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
         }
     }
 
-    new_items.retain(|it| {
-        if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind {
-            cleaner.keep_impl(for_)
-                || trait_.as_ref().map_or(false, |t| cleaner.keep_impl(t))
-                || blanket_impl.is_some()
-        } else {
-            true
-        }
-    });
-
-    if let Some(ref mut it) = krate.module {
+    let items = if let Some(ref mut it) = krate.module {
         if let ModuleItem(Module { ref mut items, .. }) = *it.kind {
-            items.extend(synth.impls);
-            items.extend(new_items);
+            items
         } else {
             panic!("collect-trait-impls can't run");
         }
     } else {
         panic!("collect-trait-impls can't run");
+    };
+
+    items.extend(synth.impls);
+    for it in new_items.drain(..) {
+        if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind {
+            if !(cleaner.keep_impl(for_)
+                || trait_.as_ref().map_or(false, |t| cleaner.keep_impl(t))
+                || blanket_impl.is_some())
+            {
+                continue;
+            }
+        }
+
+        items.push(it);
     }
 
     krate

From 3b71d3aea96ada9f180d3bc95b0108a2c3a46d57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Wed, 27 Jan 2021 08:39:48 +0100
Subject: [PATCH 12/25] Pass result vec into build_impls

---
 src/librustdoc/clean/inline.rs | 20 +++++++++-----------
 src/librustdoc/clean/utils.rs  |  2 +-
 2 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 590c2d08dea16..2588c00f2cffd 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -56,7 +56,7 @@ crate fn try_inline(
     let kind = match res {
         Res::Def(DefKind::Trait, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Trait);
-            ret.extend(build_impls(cx, Some(parent_module), did, attrs));
+            build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::TraitItem(build_external_trait(cx, did))
         }
         Res::Def(DefKind::Fn, did) => {
@@ -65,27 +65,27 @@ crate fn try_inline(
         }
         Res::Def(DefKind::Struct, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Struct);
-            ret.extend(build_impls(cx, Some(parent_module), did, attrs));
+            build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::StructItem(build_struct(cx, did))
         }
         Res::Def(DefKind::Union, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Union);
-            ret.extend(build_impls(cx, Some(parent_module), did, attrs));
+            build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::UnionItem(build_union(cx, did))
         }
         Res::Def(DefKind::TyAlias, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Typedef);
-            ret.extend(build_impls(cx, Some(parent_module), did, attrs));
+            build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::TypedefItem(build_type_alias(cx, did), false)
         }
         Res::Def(DefKind::Enum, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Enum);
-            ret.extend(build_impls(cx, Some(parent_module), did, attrs));
+            build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::EnumItem(build_enum(cx, did))
         }
         Res::Def(DefKind::ForeignTy, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Foreign);
-            ret.extend(build_impls(cx, Some(parent_module), did, attrs));
+            build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::ForeignTypeItem
         }
         // Never inline enum variants but leave them shown as re-exports.
@@ -277,16 +277,14 @@ crate fn build_impls(
     parent_module: Option<DefId>,
     did: DefId,
     attrs: Option<Attrs<'_>>,
-) -> Vec<clean::Item> {
+    ret: &mut Vec<clean::Item>,
+) {
     let tcx = cx.tcx;
-    let mut impls = Vec::new();
 
     // for each implementation of an item represented by `did`, build the clean::Item for that impl
     for &did in tcx.inherent_impls(did).iter() {
-        build_impl(cx, parent_module, did, attrs, &mut impls);
+        build_impl(cx, parent_module, did, attrs, ret);
     }
-
-    impls
 }
 
 /// `parent_module` refers to the parent of the re-export, not the original item
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index f9accd6b3253d..ce8e0cf981c38 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -329,7 +329,7 @@ crate fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut
             }
         } else if let ResolvedPath { did, .. } = *target {
             if !did.is_local() {
-                ret.extend(inline::build_impls(cx, None, did, None));
+                inline::build_impls(cx, None, did, None, ret);
             }
         }
     }

From a137c23e7aa23a92b6167adf74459aa32b1b2b6d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Wed, 27 Jan 2021 15:58:49 +0100
Subject: [PATCH 13/25] Don't copy file contents just to remove the BOM

---
 src/librustdoc/html/highlight.rs  | 2 +-
 src/librustdoc/html/markdown.rs   | 2 +-
 src/librustdoc/html/render/mod.rs | 2 +-
 src/librustdoc/html/sources.rs    | 9 ++++-----
 4 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index d21998bb8cfe5..c320fd58b4e82 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -17,7 +17,7 @@ use rustc_span::with_default_session_globals;
 
 /// Highlights `src`, returning the HTML output.
 crate fn render_with_highlighting(
-    src: String,
+    src: &str,
     class: Option<&str>,
     playground_button: Option<&str>,
     tooltip: Option<(Option<Edition>, &str)>,
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 7c8b76be374a8..d10e21aaeaaa4 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -299,7 +299,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
         };
 
         s.push_str(&highlight::render_with_highlighting(
-            text,
+            &text,
             Some(&format!(
                 "rust-example-rendered{}",
                 if let Some((_, class)) = tooltip { format!(" {}", class) } else { String::new() }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index df3701487d797..a4fb89bab0523 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -4785,7 +4785,7 @@ fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
 fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
     wrap_into_docblock(w, |w| {
         w.write_str(&highlight::render_with_highlighting(
-            t.source.clone(),
+            &t.source,
             Some("macro"),
             None,
             None,
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index ac07aeb8bc8b7..da66ae13377c0 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -86,7 +86,7 @@ impl SourceCollector<'_, '_> {
             return Ok(());
         }
 
-        let mut contents = match fs::read_to_string(&p) {
+        let contents = match fs::read_to_string(&p) {
             Ok(contents) => contents,
             Err(e) => {
                 return Err(Error::new(e, &p));
@@ -94,9 +94,8 @@ impl SourceCollector<'_, '_> {
         };
 
         // Remove the utf-8 BOM if any
-        if contents.starts_with('\u{feff}') {
-            contents.drain(..3);
-        }
+        let contents =
+            if contents.starts_with('\u{feff}') { &contents[3..] } else { &contents[..] };
 
         // Create the intermediate directories
         let mut cur = self.dst.clone();
@@ -171,7 +170,7 @@ where
 
 /// Wrapper struct to render the source code of a file. This will do things like
 /// adding line numbers to the left-hand side.
-fn print_src(buf: &mut Buffer, s: String, edition: Edition) {
+fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
     let lines = s.lines().count();
     let mut cols = 0;
     let mut tmp = lines;

From 4398750fed95dc58bbc34386e7531914ba85177d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Wed, 27 Jan 2021 16:09:34 +0100
Subject: [PATCH 14/25] Don't pre-concatenate path

---
 src/librustdoc/html/layout.rs | 26 +++++++++++---------------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index c6ff4b57a6e59..e5a686bd07d07 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -114,7 +114,6 @@ crate fn render<T: Print, S: Print>(
     {after_content}\
     <div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\"></div>
     <script src=\"{static_root_path}main{suffix}.js\"></script>\
-    {static_extra_scripts}\
     {extra_scripts}\
     <script defer src=\"{root_path}search-index{suffix}.js\"></script>\
 </body>\
@@ -135,22 +134,23 @@ crate fn render<T: Print, S: Print>(
         root_path = page.root_path,
         css_class = page.css_class,
         logo = {
-            let p = format!("{}{}", page.root_path, layout.krate);
-            let p = ensure_trailing_slash(&p);
             if layout.logo.is_empty() {
                 format!(
-                    "<a href='{path}index.html'>\
+                    "<a href='{root}{path}index.html'>\
                      <div class='logo-container rust-logo'>\
                      <img src='{static_root_path}rust-logo{suffix}.png' alt='logo'></div></a>",
-                    path = p,
+                    root = page.root_path,
+                    path = ensure_trailing_slash(&layout.krate),
                     static_root_path = static_root_path,
                     suffix = page.resource_suffix
                 )
             } else {
                 format!(
-                    "<a href='{}index.html'>\
-                     <div class='logo-container'><img src='{}' alt='logo'></div></a>",
-                    p, layout.logo
+                    "<a href='{root}{path}index.html'>\
+                     <div class='logo-container'><img src='{logo}' alt='logo'></div></a>",
+                    root = page.root_path,
+                    path = ensure_trailing_slash(&layout.krate),
+                    logo = layout.logo
                 )
             }
         },
@@ -194,7 +194,7 @@ crate fn render<T: Print, S: Print>(
             ))
             .collect::<String>(),
         suffix = page.resource_suffix,
-        static_extra_scripts = page
+        extra_scripts = page
             .static_extra_scripts
             .iter()
             .map(|e| {
@@ -204,17 +204,13 @@ crate fn render<T: Print, S: Print>(
                     extra_script = e
                 )
             })
-            .collect::<String>(),
-        extra_scripts = page
-            .extra_scripts
-            .iter()
-            .map(|e| {
+            .chain(page.extra_scripts.iter().map(|e| {
                 format!(
                     "<script src=\"{root_path}{extra_script}.js\"></script>",
                     root_path = page.root_path,
                     extra_script = e
                 )
-            })
+            }))
             .collect::<String>(),
         filter_crates = if layout.generate_search_filter {
             "<select id=\"crate-search\">\

From e8f90b1cb95d65a45501e67add0aa8360f37fa4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 08:31:18 +0100
Subject: [PATCH 15/25] Try to avoid string copies

---
 src/librustdoc/config.rs          |  2 +-
 src/librustdoc/html/markdown.rs   | 10 ++++----
 src/librustdoc/html/render/mod.rs | 42 ++++++++++++++-----------------
 3 files changed, 25 insertions(+), 29 deletions(-)

diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 94773ac77ccb0..95c6989dab4ee 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -474,7 +474,7 @@ impl Options {
         };
 
         let mut id_map = html::markdown::IdMap::new();
-        id_map.populate(html::render::initial_ids());
+        id_map.populate(&html::render::INITIAL_IDS);
         let external_html = match ExternalHtml::load(
             &matches.opt_strs("html-in-header"),
             &matches.opt_strs("html-before-content"),
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index d10e21aaeaaa4..1cf30ecae90ec 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1332,7 +1332,7 @@ impl IdMap {
         IdMap { map: init_id_map() }
     }
 
-    crate fn populate<I: IntoIterator<Item = String>>(&mut self, ids: I) {
+    crate fn populate<I: IntoIterator<Item = S>, S: AsRef<str> + ToString>(&mut self, ids: I) {
         for id in ids {
             let _ = self.derive(id);
         }
@@ -1342,11 +1342,11 @@ impl IdMap {
         self.map = init_id_map();
     }
 
-    crate fn derive(&mut self, candidate: String) -> String {
-        let id = match self.map.get_mut(&candidate) {
-            None => candidate,
+    crate fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
+        let id = match self.map.get_mut(candidate.as_ref()) {
+            None => candidate.to_string(),
             Some(a) => {
-                let id = format!("{}-{}", candidate, *a);
+                let id = format!("{}-{}", candidate.as_ref(), *a);
                 *a += 1;
                 id
             }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index a4fb89bab0523..f4cc4332d955f 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -359,28 +359,24 @@ crate struct StylePath {
 
 thread_local!(crate static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
 
-crate fn initial_ids() -> Vec<String> {
-    [
-        "main",
-        "search",
-        "help",
-        "TOC",
-        "render-detail",
-        "associated-types",
-        "associated-const",
-        "required-methods",
-        "provided-methods",
-        "implementors",
-        "synthetic-implementors",
-        "implementors-list",
-        "synthetic-implementors-list",
-        "methods",
-        "implementations",
-    ]
-    .iter()
-    .map(|id| (String::from(*id)))
-    .collect()
-}
+// FIXME: make this work
+crate const INITIAL_IDS: [&'static str; 15] = [
+    "main",
+    "search",
+    "help",
+    "TOC",
+    "render-detail",
+    "associated-types",
+    "associated-const",
+    "required-methods",
+    "provided-methods",
+    "implementors",
+    "synthetic-implementors",
+    "implementors-list",
+    "synthetic-implementors-list",
+    "methods",
+    "implementations",
+];
 
 /// Generates the documentation for `crate` into the directory `dst`
 impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
@@ -1581,7 +1577,7 @@ impl Context<'_> {
 
         {
             self.id_map.borrow_mut().reset();
-            self.id_map.borrow_mut().populate(initial_ids());
+            self.id_map.borrow_mut().populate(&INITIAL_IDS);
         }
 
         if !self.render_redirect_pages {

From 14824c56eeaac50813d1ae800aad38e3f69ebbd4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 19:27:40 +0100
Subject: [PATCH 16/25] Only allocate maximum once in small_url_encode

---
 src/librustdoc/html/render/mod.rs | 52 +++++++++++++++++++++----------
 1 file changed, 35 insertions(+), 17 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index f4cc4332d955f..08e100d6fe69d 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -3510,7 +3510,7 @@ fn render_assoc_items(
                 RenderMode::Normal
             }
             AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
-                let id = cx.derive_id(small_url_encode(&format!(
+                let id = cx.derive_id(small_url_encode(format!(
                     "deref-methods-{:#}",
                     type_.print(cx.cache())
                 )));
@@ -3761,7 +3761,7 @@ fn render_impl(
                 if is_on_foreign_type {
                     get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx.cache())
                 } else {
-                    format!("impl-{}", small_url_encode(&format!("{:#}", t.print(cx.cache()))))
+                    format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx.cache()))))
                 }
             }
             None => "impl".to_string(),
@@ -4264,19 +4264,37 @@ fn get_methods(
 }
 
 // The point is to url encode any potential character from a type with genericity.
-fn small_url_encode(s: &str) -> String {
-    s.replace("<", "%3C")
-        .replace(">", "%3E")
-        .replace(" ", "%20")
-        .replace("?", "%3F")
-        .replace("'", "%27")
-        .replace("&", "%26")
-        .replace(",", "%2C")
-        .replace(":", "%3A")
-        .replace(";", "%3B")
-        .replace("[", "%5B")
-        .replace("]", "%5D")
-        .replace("\"", "%22")
+fn small_url_encode(s: String) -> String {
+    let mut st = String::new();
+    let mut last_match = 0;
+    for (idx, c) in s.char_indices() {
+        let escaped = match c {
+            '<' => "%3C",
+            '>' => "%3E",
+            ' ' => "%20",
+            '?' => "%3F",
+            '\'' => "%27",
+            '&' => "%26",
+            ',' => "%2C",
+            ':' => "%3A",
+            ';' => "%3B",
+            '[' => "%5B",
+            ']' => "%5D",
+            '"' => "%22",
+            _ => continue,
+        };
+
+        st += &s[last_match..idx];
+        st += escaped;
+        last_match = idx + c.len_utf8();
+    }
+
+    if last_match != 0 {
+        st += &s[last_match..];
+        st
+    } else {
+        s
+    }
 }
 
 fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
@@ -4321,7 +4339,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
                         if let Some(ref i) = it.inner_impl().trait_ {
                             let i_display = format!("{:#}", i.print(cx.cache()));
                             let out = Escape(&i_display);
-                            let encoded = small_url_encode(&format!("{:#}", i.print(cx.cache())));
+                            let encoded = small_url_encode(format!("{:#}", i.print(cx.cache())));
                             let generated = format!(
                                 "<a href=\"#impl-{}\">{}{}</a>",
                                 encoded,
@@ -4477,7 +4495,7 @@ fn get_id_for_impl_on_foreign_type(
     trait_: &clean::Type,
     cache: &Cache,
 ) -> String {
-    small_url_encode(&format!("impl-{:#}-for-{:#}", trait_.print(cache), for_.print(cache)))
+    small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cache), for_.print(cache)))
 }
 
 fn extract_for_impl_name(item: &clean::Item, cache: &Cache) -> Option<(String, String)> {

From 7885ce5757b7aa998e35a061a9043a18213aa783 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 21:01:30 +0100
Subject: [PATCH 17/25] Align small_url_encode and Escape

---
 src/librustdoc/html/escape.rs     | 27 +++++++++++----------------
 src/librustdoc/html/render/mod.rs |  6 +++---
 2 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs
index 60c19551983cd..5200eb3b9071d 100644
--- a/src/librustdoc/html/escape.rs
+++ b/src/librustdoc/html/escape.rs
@@ -17,22 +17,17 @@ impl<'a> fmt::Display for Escape<'a> {
         let pile_o_bits = s;
         let mut last = 0;
         for (i, ch) in s.bytes().enumerate() {
-            match ch as char {
-                '<' | '>' | '&' | '\'' | '"' => {
-                    fmt.write_str(&pile_o_bits[last..i])?;
-                    let s = match ch as char {
-                        '>' => "&gt;",
-                        '<' => "&lt;",
-                        '&' => "&amp;",
-                        '\'' => "&#39;",
-                        '"' => "&quot;",
-                        _ => unreachable!(),
-                    };
-                    fmt.write_str(s)?;
-                    last = i + 1;
-                }
-                _ => {}
-            }
+            let s = match ch as char {
+                '>' => "&gt;",
+                '<' => "&lt;",
+                '&' => "&amp;",
+                '\'' => "&#39;",
+                '"' => "&quot;",
+                _ => continue,
+            };
+            fmt.write_str(&pile_o_bits[last..i])?;
+            fmt.write_str(s)?;
+            last = i + 1;
         }
 
         if last < s.len() {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 08e100d6fe69d..38d926c1925ad 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -4267,8 +4267,8 @@ fn get_methods(
 fn small_url_encode(s: String) -> String {
     let mut st = String::new();
     let mut last_match = 0;
-    for (idx, c) in s.char_indices() {
-        let escaped = match c {
+    for (idx, c) in s.bytes().enumerate() {
+        let escaped = match c as char {
             '<' => "%3C",
             '>' => "%3E",
             ' ' => "%20",
@@ -4286,7 +4286,7 @@ fn small_url_encode(s: String) -> String {
 
         st += &s[last_match..idx];
         st += escaped;
-        last_match = idx + c.len_utf8();
+        last_match = idx + 1;
     }
 
     if last_match != 0 {

From 1b8741cf673f8f251ae2cf8820b4c681f22f6168 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 21:31:18 +0100
Subject: [PATCH 18/25] Avoid allocating when rendering some sidebar sections

---
 src/librustdoc/html/render/mod.rs | 164 ++++++++++++++----------------
 1 file changed, 76 insertions(+), 88 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 38d926c1925ad..a4983d71c146d 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -4515,82 +4515,74 @@ fn extract_for_impl_name(item: &clean::Item, cache: &Cache) -> Option<(String, S
 }
 
 fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
-    let mut sidebar = String::new();
+    write!(buf, "<div class=\"block items\">");
+
+    fn print_sidebar_section(
+        out: &mut Buffer,
+        items: &[clean::Item],
+        before: &str,
+        filter: impl Fn(&clean::Item) -> bool,
+        write: impl Fn(&mut Buffer, &Symbol),
+        after: &str,
+    ) {
+        let mut items = items
+            .iter()
+            .filter_map(|m| match m.name {
+                Some(ref name) if filter(m) => Some(name),
+                _ => None,
+            })
+            .collect::<Vec<_>>();
 
-    let mut types = t
-        .items
-        .iter()
-        .filter_map(|m| match m.name {
-            Some(ref name) if m.is_associated_type() => {
-                Some(format!("<a href=\"#associatedtype.{name}\">{name}</a>", name = name))
-            }
-            _ => None,
-        })
-        .collect::<Vec<_>>();
-    let mut consts = t
-        .items
-        .iter()
-        .filter_map(|m| match m.name {
-            Some(ref name) if m.is_associated_const() => {
-                Some(format!("<a href=\"#associatedconstant.{name}\">{name}</a>", name = name))
-            }
-            _ => None,
-        })
-        .collect::<Vec<_>>();
-    let mut required = t
-        .items
-        .iter()
-        .filter_map(|m| match m.name {
-            Some(ref name) if m.is_ty_method() => {
-                Some(format!("<a href=\"#tymethod.{name}\">{name}</a>", name = name))
-            }
-            _ => None,
-        })
-        .collect::<Vec<String>>();
-    let mut provided = t
-        .items
-        .iter()
-        .filter_map(|m| match m.name {
-            Some(ref name) if m.is_method() => {
-                Some(format!("<a href=\"#method.{0}\">{0}</a>", name))
+        if !items.is_empty() {
+            items.sort();
+            out.push_str(before);
+            for item in items.into_iter() {
+                write(out, item);
             }
-            _ => None,
-        })
-        .collect::<Vec<String>>();
-
-    if !types.is_empty() {
-        types.sort();
-        sidebar.push_str(&format!(
-            "<a class=\"sidebar-title\" href=\"#associated-types\">\
-                Associated Types</a><div class=\"sidebar-links\">{}</div>",
-            types.join("")
-        ));
-    }
-    if !consts.is_empty() {
-        consts.sort();
-        sidebar.push_str(&format!(
-            "<a class=\"sidebar-title\" href=\"#associated-const\">\
-                Associated Constants</a><div class=\"sidebar-links\">{}</div>",
-            consts.join("")
-        ));
-    }
-    if !required.is_empty() {
-        required.sort();
-        sidebar.push_str(&format!(
-            "<a class=\"sidebar-title\" href=\"#required-methods\">\
-                Required Methods</a><div class=\"sidebar-links\">{}</div>",
-            required.join("")
-        ));
-    }
-    if !provided.is_empty() {
-        provided.sort();
-        sidebar.push_str(&format!(
-            "<a class=\"sidebar-title\" href=\"#provided-methods\">\
-                Provided Methods</a><div class=\"sidebar-links\">{}</div>",
-            provided.join("")
-        ));
+            out.push_str(after);
+        }
     }
 
+    print_sidebar_section(
+        buf,
+        &t.items,
+        "<a class=\"sidebar-title\" href=\"#associated-types\">\
+            Associated Types</a><div class=\"sidebar-links\">",
+        |m| m.is_associated_type(),
+        |out, sym| write!(out, "<a href=\"#associatedtype.{0}\">{0}</a>", sym),
+        "</div>",
+    );
+
+    print_sidebar_section(
+        buf,
+        &t.items,
+        "<a class=\"sidebar-title\" href=\"#associated-const\">\
+            Associated Constants</a><div class=\"sidebar-links\">",
+        |m| m.is_associated_const(),
+        |out, sym| write!(out, "<a href=\"#associatedconstant.{0}\">{0}</a>", sym),
+        "</div>",
+    );
+
+    print_sidebar_section(
+        buf,
+        &t.items,
+        "<a class=\"sidebar-title\" href=\"#required-methods\">\
+            Required Methods</a><div class=\"sidebar-links\">",
+        |m| m.is_ty_method(),
+        |out, sym| write!(out, "<a href=\"#tymethod.{0}\">{0}</a>", sym),
+        "</div>",
+    );
+
+    print_sidebar_section(
+        buf,
+        &t.items,
+        "<a class=\"sidebar-title\" href=\"#provided-methods\">\
+            Provided Methods</a><div class=\"sidebar-links\">",
+        |m| m.is_method(),
+        |out, sym| write!(out, "<a href=\"#method.{0}\">{0}</a>", sym),
+        "</div>",
+    );
+
     if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
         let mut res = implementors
             .iter()
@@ -4605,29 +4597,29 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean
 
         if !res.is_empty() {
             res.sort();
-            sidebar.push_str(&format!(
+            buf.push_str(
                 "<a class=\"sidebar-title\" href=\"#foreign-impls\">\
                     Implementations on Foreign Types</a>\
-                 <div class=\"sidebar-links\">{}</div>",
-                res.into_iter()
-                    .map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(&name)))
-                    .collect::<Vec<_>>()
-                    .join("")
-            ));
+                 <div class=\"sidebar-links\">",
+            );
+            for (name, id) in res.into_iter() {
+                buf.push_str(&format!("<a href=\"#{}\">{}</a>", id, Escape(&name)));
+            }
+            buf.push_str("</div>");
         }
     }
 
-    sidebar.push_str(&sidebar_assoc_items(cx, it));
+    buf.push_str(&sidebar_assoc_items(cx, it));
 
-    sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>");
+    buf.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>");
     if t.is_auto {
-        sidebar.push_str(
+        buf.push_str(
             "<a class=\"sidebar-title\" \
                 href=\"#synthetic-implementors\">Auto Implementors</a>",
         );
     }
 
-    write!(buf, "<div class=\"block items\">{}</div>", sidebar)
+    write!(buf, "</div>")
 }
 
 fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
@@ -4743,11 +4735,7 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
     if items.iter().any(|it| {
         it.type_() == ItemType::ExternCrate || (it.type_() == ItemType::Import && !it.is_stripped())
     }) {
-        sidebar.push_str(&format!(
-            "<li><a href=\"#{id}\">{name}</a></li>",
-            id = "reexports",
-            name = "Re-exports"
-        ));
+        sidebar.push_str("<li><a href=\"#reexports\">Re-exports</a></li>");
     }
 
     // ordering taken from item_module, reorder, where it prioritized elements in a certain order

From 7aaf95dac91ea7e3a12ff460c3a7f6a14fb1d229 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 22:06:21 +0100
Subject: [PATCH 19/25] Section titles don't need escaping

---
 src/librustdoc/html/render/mod.rs | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index a4983d71c146d..2dba145ad6a37 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1321,23 +1321,22 @@ impl AllTypes {
     }
 }
 
-fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &str) {
-    if !e.is_empty() {
-        let mut e: Vec<&ItemEntry> = e.iter().collect();
-        e.sort();
-        write!(
-            f,
-            "<h3 id=\"{}\">{}</h3><ul class=\"{} docblock\">{}</ul>",
-            title,
-            Escape(title),
-            class,
-            e.iter().map(|s| format!("<li>{}</li>", s.print())).collect::<String>()
-        );
-    }
-}
-
 impl AllTypes {
     fn print(self, f: &mut Buffer) {
+        fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &str) {
+            if !e.is_empty() {
+                let mut e: Vec<&ItemEntry> = e.iter().collect();
+                e.sort();
+                write!(f, "<h3 id=\"{}\">{}</h3><ul class=\"{} docblock\">", title, title, class);
+
+                for s in e.iter() {
+                    write!(f, "<li>{}</li>", s.print());
+                }
+
+                write!(f, "</ul>");
+            }
+        }
+
         write!(
             f,
             "<h1 class=\"fqn\">\
@@ -1352,6 +1351,8 @@ impl AllTypes {
                  </span>
              </h1>"
         );
+        // Note: print_entries does not escape the title, because we know the current set of titles
+        // don't require escaping.
         print_entries(f, &self.structs, "Structs", "structs");
         print_entries(f, &self.enums, "Enums", "enums");
         print_entries(f, &self.unions, "Unions", "unions");

From f93f422f773365551018099dead4250672c1bd74 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 22:29:20 +0100
Subject: [PATCH 20/25] Only format prefix once

---
 src/librustdoc/html/render/mod.rs | 57 ++++++++++++++++---------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 2dba145ad6a37..6495e44f19134 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -89,7 +89,7 @@ crate type NameDoc = (String, Option<String>);
 
 crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
     crate::html::format::display_fn(move |f| {
-        if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { write!(f, "{}", v) }
+        if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) }
     })
 }
 
@@ -904,12 +904,13 @@ themePicker.onblur = handleThemeButtonsBlur;
         let mut krates = Vec::new();
 
         if path.exists() {
+            let prefix = format!(r#"{}["{}"]"#, key, krate);
             for line in BufReader::new(File::open(path)?).lines() {
                 let line = line?;
                 if !line.starts_with(key) {
                     continue;
                 }
-                if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) {
+                if line.starts_with(&prefix) {
                     continue;
                 }
                 ret.push(line.to_string());
@@ -930,12 +931,13 @@ themePicker.onblur = handleThemeButtonsBlur;
         let mut krates = Vec::new();
 
         if path.exists() {
+            let prefix = format!("\"{}\"", krate);
             for line in BufReader::new(File::open(path)?).lines() {
                 let line = line?;
                 if !line.starts_with('"') {
                     continue;
                 }
-                if line.starts_with(&format!("\"{}\"", krate)) {
+                if line.starts_with(&prefix) {
                     continue;
                 }
                 if line.ends_with(",\\") {
@@ -1920,12 +1922,13 @@ fn document_full(
         debug!("Doc block: =====\n{}\n=====", s);
         render_markdown(w, cx, &*s, item.links(&cx.cache), prefix, is_hidden);
     } else if !prefix.is_empty() {
-        write!(
-            w,
-            "<div class=\"docblock{}\">{}</div>",
-            if is_hidden { " hidden" } else { "" },
-            prefix
-        );
+        if is_hidden {
+            w.write_str("<div class=\"docblock hidden\">");
+        } else {
+            w.write_str("<div class=\"docblock\">");
+        }
+        w.write_str(prefix);
+        w.write_str("</div");
     }
 }
 
@@ -1943,11 +1946,15 @@ fn document_item_info(
 ) {
     let item_infos = short_item_info(item, cx, parent);
     if !item_infos.is_empty() {
-        write!(w, "<div class=\"item-info{}\">", if is_hidden { " hidden" } else { "" });
+        if is_hidden {
+            w.write_str("<div class=\"item-info hidden\">");
+        } else {
+            w.write_str("<div class=\"item-info\">");
+        }
         for info in item_infos {
-            write!(w, "{}", info);
+            w.write_str(&info);
         }
-        write!(w, "</div>");
+        w.write_str("</div>");
     }
 }
 
@@ -1970,36 +1977,32 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
         });
 
         if item.is_struct() {
-            write!(
-                w,
+            w.write_str(
                 "Non-exhaustive structs could have additional fields added in future. \
                  Therefore, non-exhaustive structs cannot be constructed in external crates \
                  using the traditional <code>Struct {{ .. }}</code> syntax; cannot be \
                  matched against without a wildcard <code>..</code>; and \
-                 struct update syntax will not work."
+                 struct update syntax will not work.",
             );
         } else if item.is_enum() {
-            write!(
-                w,
+            w.write_str(
                 "Non-exhaustive enums could have additional variants added in future. \
                  Therefore, when matching against variants of non-exhaustive enums, an \
-                 extra wildcard arm must be added to account for any future variants."
+                 extra wildcard arm must be added to account for any future variants.",
             );
         } else if item.is_variant() {
-            write!(
-                w,
+            w.write_str(
                 "Non-exhaustive enum variants could have additional fields added in future. \
                  Therefore, non-exhaustive enum variants cannot be constructed in external \
-                 crates and cannot be matched against."
+                 crates and cannot be matched against.",
             );
         } else {
-            write!(
-                w,
-                "This type will require a wildcard arm in any match statements or constructors."
+            w.write_str(
+                "This type will require a wildcard arm in any match statements or constructors.",
             );
         }
 
-        write!(w, "</div>");
+        w.write_str("</div>");
     }
 }
 
@@ -2136,7 +2139,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
             curty = myty;
         } else if myty != curty {
             if curty.is_some() {
-                write!(w, "</table>");
+                w.write_str("</table>");
             }
             curty = myty;
             let (short, name) = item_ty_to_strs(&myty.unwrap());
@@ -2168,7 +2171,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                         anchor(myitem.def_id, &*name.as_str(), cx.cache())
                     ),
                 }
-                write!(w, "</code></td></tr>");
+                w.write_str("</code></td></tr>");
             }
 
             clean::ImportItem(ref import) => {

From e367d0cdcbf65d94f5aeed5dcfcee5215c36fbab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 22:33:05 +0100
Subject: [PATCH 21/25] Remove unnecessary get_mut

---
 src/librustdoc/html/render/mod.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 6495e44f19134..29b09f5246081 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1018,8 +1018,7 @@ themePicker.onblur = handleThemeButtonsBlur;
                     break;
                 } else {
                     let e = cur_elem.clone();
-                    h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e));
-                    h = h.children.get_mut(&cur_elem).expect("not found child");
+                    h = h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e));
                 }
             }
         }

From a43ef3ba2e15bb9d47e12e9885e1d5417a496c89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 22:34:44 +0100
Subject: [PATCH 22/25] Inline constant format arg

---
 src/librustdoc/html/render/mod.rs | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 29b09f5246081..3c503ce98c4ae 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1211,11 +1211,7 @@ fn write_minify(
 
 fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
     if let Some(l) = cx.src_href(item) {
-        write!(
-            buf,
-            "<a class=\"srclink\" href=\"{}\" title=\"{}\">[src]</a>",
-            l, "goto source code"
-        )
+        write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">[src]</a>", l)
     }
 }
 

From e2fa4e023db0208e86f34bcbab6141b10f79f682 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 23:00:18 +0100
Subject: [PATCH 23/25] Render: don't call format for string literals

---
 src/librustdoc/html/render/mod.rs | 208 +++++++++++++++---------------
 1 file changed, 102 insertions(+), 106 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 3c503ce98c4ae..cc7a42de32069 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1330,12 +1330,11 @@ impl AllTypes {
                     write!(f, "<li>{}</li>", s.print());
                 }
 
-                write!(f, "</ul>");
+                f.write_str("</ul>");
             }
         }
 
-        write!(
-            f,
+        f.write_str(
             "<h1 class=\"fqn\">\
                  <span class=\"in-band\">List of all items</span>\
                  <span class=\"out-of-band\">\
@@ -1346,7 +1345,8 @@ impl AllTypes {
                          </a>\
                      </span>
                  </span>
-             </h1>"
+                 <span class=\"in-band\">List of all items</span>\
+             </h1>",
         );
         // Note: print_entries does not escape the title, because we know the current set of titles
         // don't require escaping.
@@ -1697,15 +1697,15 @@ fn wrap_into_docblock<F>(w: &mut Buffer, f: F)
 where
     F: FnOnce(&mut Buffer),
 {
-    write!(w, "<div class=\"docblock type-decl hidden-by-usual-hider\">");
+    w.write_str("<div class=\"docblock type-decl hidden-by-usual-hider\">");
     f(w);
-    write!(w, "</div>")
+    w.write_str("</div>")
 }
 
 fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
     debug_assert!(!item.is_stripped());
     // Write the breadcrumb trail header for the top
-    write!(buf, "<h1 class=\"fqn\"><span class=\"in-band\">");
+    buf.write_str("<h1 class=\"fqn\"><span class=\"in-band\">");
     let name = match *item.kind {
         clean::ModuleItem(ref m) => {
             if m.is_crate {
@@ -1753,8 +1753,8 @@ fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
     }
     write!(buf, "<a class=\"{}\" href=\"\">{}</a>", item.type_(), item.name.as_ref().unwrap());
 
-    write!(buf, "</span>"); // in-band
-    write!(buf, "<span class=\"out-of-band\">");
+    buf.write_str("</span>"); // in-band
+    buf.write_str("<span class=\"out-of-band\">");
     render_stability_since_raw(
         buf,
         item.stable_since(cx.tcx()).as_deref(),
@@ -1762,14 +1762,13 @@ fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
         None,
         None,
     );
-    write!(
-        buf,
+    buf.write_str(
         "<span id=\"render-detail\">\
                 <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \
                     title=\"collapse all docs\">\
                     [<span class=\"inner\">&#x2212;</span>]\
                 </a>\
-            </span>"
+            </span>",
     );
 
     // Write `src` tag
@@ -1782,7 +1781,7 @@ fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
         write_srclink(cx, item, buf);
     }
 
-    write!(buf, "</span></h1>"); // out-of-band
+    buf.write_str("</span></h1>"); // out-of-band
 
     match *item.kind {
         clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
@@ -1923,7 +1922,7 @@ fn document_full(
             w.write_str("<div class=\"docblock\">");
         }
         w.write_str(prefix);
-        w.write_str("</div");
+        w.write_str("</div>");
     }
 }
 
@@ -2222,7 +2221,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
     }
 
     if curty.is_some() {
-        write!(w, "</table>");
+        w.write_str("</table>");
     }
 }
 
@@ -2384,7 +2383,7 @@ fn short_item_info(
 }
 
 fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
-    write!(w, "<pre class=\"rust const\">");
+    w.write_str("<pre class=\"rust const\">");
     render_attributes(w, it, false);
 
     write!(
@@ -2398,7 +2397,7 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::
     if c.value.is_some() || c.is_literal {
         write!(w, " = {expr};", expr = Escape(&c.expr));
     } else {
-        write!(w, ";");
+        w.write_str(";");
     }
 
     if let Some(value) = &c.value {
@@ -2414,12 +2413,12 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::
         }
     }
 
-    write!(w, "</pre>");
+    w.write_str("</pre>");
     document(w, cx, it, None)
 }
 
 fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
-    write!(w, "<pre class=\"rust static\">");
+    w.write_str("<pre class=\"rust static\">");
     render_attributes(w, it, false);
     write!(
         w,
@@ -2444,7 +2443,7 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::
         f.generics.print(cx.cache())
     )
     .len();
-    write!(w, "<pre class=\"rust fn\">");
+    w.write_str("<pre class=\"rust fn\">");
     render_attributes(w, it, false);
     write!(
         w,
@@ -2568,7 +2567,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
 
     // Output the trait definition
     wrap_into_docblock(w, |w| {
-        write!(w, "<pre class=\"rust trait\">");
+        w.write_str("<pre class=\"rust trait\">");
         render_attributes(w, it, true);
         write!(
             w,
@@ -2585,34 +2584,34 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
             let where_ = WhereClause { gens: &t.generics, indent: 0, end_newline: true };
             write!(w, "{}", where_.print(cx.cache()));
         } else {
-            write!(w, " ");
+            w.write_str(" ");
         }
 
         if t.items.is_empty() {
-            write!(w, "{{ }}");
+            w.write_str("{ }");
         } else {
             // FIXME: we should be using a derived_id for the Anchors here
-            write!(w, "{{\n");
+            w.write_str("{\n");
             for t in &types {
                 render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
-                write!(w, ";\n");
+                w.write_str(";\n");
             }
             if !types.is_empty() && !consts.is_empty() {
                 w.write_str("\n");
             }
             for t in &consts {
                 render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
-                write!(w, ";\n");
+                w.write_str(";\n");
             }
             if !consts.is_empty() && !required.is_empty() {
                 w.write_str("\n");
             }
             for (pos, m) in required.iter().enumerate() {
                 render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
-                write!(w, ";\n");
+                w.write_str(";\n");
 
                 if pos < required.len() - 1 {
-                    write!(w, "<div class=\"item-spacer\"></div>");
+                    w.write_str("<div class=\"item-spacer\"></div>");
                 }
             }
             if !required.is_empty() && !provided.is_empty() {
@@ -2624,19 +2623,19 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                     clean::MethodItem(ref inner, _)
                         if !inner.generics.where_predicates.is_empty() =>
                     {
-                        write!(w, ",\n    {{ ... }}\n");
+                        w.write_str(",\n    { ... }\n");
                     }
                     _ => {
-                        write!(w, " {{ ... }}\n");
+                        w.write_str(" { ... }\n");
                     }
                 }
                 if pos < provided.len() - 1 {
-                    write!(w, "<div class=\"item-spacer\"></div>");
+                    w.write_str("<div class=\"item-spacer\"></div>");
                 }
             }
-            write!(w, "}}");
+            w.write_str("}");
         }
-        write!(w, "</pre>")
+        w.write_str("</pre>")
     });
 
     // Trait documentation
@@ -2663,10 +2662,10 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         let id = cx.derive_id(format!("{}.{}", item_type, name));
         write!(w, "<h3 id=\"{id}\" class=\"method\"><code>", id = id,);
         render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx);
-        write!(w, "</code>");
+        w.write_str("</code>");
         render_stability_since(w, m, t, cx.tcx());
         write_srclink(cx, m, w);
-        write!(w, "</h3>");
+        w.write_str("</h3>");
         document(w, cx, m, Some(t));
     }
 
@@ -3067,10 +3066,10 @@ fn render_assoc_item(
 
 fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
     wrap_into_docblock(w, |w| {
-        write!(w, "<pre class=\"rust struct\">");
+        w.write_str("<pre class=\"rust struct\">");
         render_attributes(w, it, true);
         render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
-        write!(w, "</pre>")
+        w.write_str("</pre>")
     });
 
     document(w, cx, it, None);
@@ -3117,10 +3116,10 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
 
 fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
     wrap_into_docblock(w, |w| {
-        write!(w, "<pre class=\"rust union\">");
+        w.write_str("<pre class=\"rust union\">");
         render_attributes(w, it, true);
         render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
-        write!(w, "</pre>")
+        w.write_str("</pre>")
     });
 
     document(w, cx, it, None);
@@ -3163,7 +3162,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
 
 fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
     wrap_into_docblock(w, |w| {
-        write!(w, "<pre class=\"rust enum\">");
+        w.write_str("<pre class=\"rust enum\">");
         render_attributes(w, it, true);
         write!(
             w,
@@ -3174,11 +3173,11 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
             WhereClause { gens: &e.generics, indent: 0, end_newline: true }.print(cx.cache())
         );
         if e.variants.is_empty() && !e.variants_stripped {
-            write!(w, " {{}}");
+            w.write_str(" {}");
         } else {
-            write!(w, " {{\n");
+            w.write_str(" {\n");
             for v in &e.variants {
-                write!(w, "    ");
+                w.write_str("    ");
                 let name = v.name.as_ref().unwrap();
                 match *v.kind {
                     clean::VariantItem(ref var) => match var {
@@ -3187,11 +3186,11 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                             write!(w, "{}(", name);
                             for (i, ty) in tys.iter().enumerate() {
                                 if i > 0 {
-                                    write!(w, ",&nbsp;")
+                                    w.write_str(",&nbsp;")
                                 }
                                 write!(w, "{}", ty.print(cx.cache()));
                             }
-                            write!(w, ")");
+                            w.write_str(")");
                         }
                         clean::Variant::Struct(ref s) => {
                             render_struct(w, v, None, s.struct_type, &s.fields, "    ", false, cx);
@@ -3199,15 +3198,15 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                     },
                     _ => unreachable!(),
                 }
-                write!(w, ",\n");
+                w.write_str(",\n");
             }
 
             if e.variants_stripped {
-                write!(w, "    // some variants omitted\n");
+                w.write_str("    // some variants omitted\n");
             }
-            write!(w, "}}");
+            w.write_str("}");
         }
-        write!(w, "</pre>")
+        w.write_str("</pre>")
     });
 
     document(w, cx, it, None);
@@ -3231,16 +3230,16 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                 name = variant.name.as_ref().unwrap()
             );
             if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind {
-                write!(w, "(");
+                w.write_str("(");
                 for (i, ty) in tys.iter().enumerate() {
                     if i > 0 {
-                        write!(w, ",&nbsp;");
+                        w.write_str(",&nbsp;");
                     }
                     write!(w, "{}", ty.print(cx.cache()));
                 }
-                write!(w, ")");
+                w.write_str(")");
             }
-            write!(w, "</code></div>");
+            w.write_str("</code></div>");
             document(w, cx, variant, Some(it));
             document_non_exhaustive(w, variant);
 
@@ -3278,7 +3277,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                         document(w, cx, field, Some(variant));
                     }
                 }
-                write!(w, "</div></div>");
+                w.write_str("</div></div>");
             }
             render_stability_since(w, variant, it, cx.tcx());
         }
@@ -3358,7 +3357,7 @@ fn render_struct(
                 )
             }
             let mut has_visible_fields = false;
-            write!(w, " {{");
+            w.write_str(" {");
             for field in fields {
                 if let clean::StructFieldItem(ref ty) = *field.kind {
                     write!(
@@ -3383,13 +3382,13 @@ fn render_struct(
                 // `{ /* fields omitted */ }` to save space.
                 write!(w, " /* fields omitted */ ");
             }
-            write!(w, "}}");
+            w.write_str("}");
         }
         CtorKind::Fn => {
-            write!(w, "(");
+            w.write_str("(");
             for (i, field) in fields.iter().enumerate() {
                 if i > 0 {
-                    write!(w, ", ");
+                    w.write_str(", ");
                 }
                 match *field.kind {
                     clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
@@ -3404,7 +3403,7 @@ fn render_struct(
                     _ => unreachable!(),
                 }
             }
-            write!(w, ")");
+            w.write_str(")");
             if let Some(g) = g {
                 write!(
                     w,
@@ -3412,7 +3411,7 @@ fn render_struct(
                     WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache())
                 )
             }
-            write!(w, ";");
+            w.write_str(";");
         }
         CtorKind::Const => {
             // Needed for PhantomData.
@@ -3423,7 +3422,7 @@ fn render_struct(
                     WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache())
                 )
             }
-            write!(w, ";");
+            w.write_str(";");
         }
     }
 }
@@ -3466,7 +3465,7 @@ fn render_union(
     if it.has_stripped_fields().unwrap() {
         write!(w, "    // some fields omitted\n{}", tab);
     }
-    write!(w, "}}");
+    w.write_str("}");
 }
 
 #[derive(Copy, Clone)]
@@ -3500,11 +3499,10 @@ fn render_assoc_items(
     if !non_trait.is_empty() {
         let render_mode = match what {
             AssocItemRender::All => {
-                write!(
-                    w,
+                w.write_str(
                     "<h2 id=\"implementations\" class=\"small-section-header\">\
                          Implementations<a href=\"#implementations\" class=\"anchor\"></a>\
-                    </h2>"
+                    </h2>",
                 );
                 RenderMode::Normal
             }
@@ -3584,29 +3582,27 @@ fn render_assoc_items(
         }
 
         if !synthetic.is_empty() {
-            write!(
-                w,
+            w.write_str(
                 "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\
                      Auto Trait Implementations\
                      <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\
                  </h2>\
-                 <div id=\"synthetic-implementations-list\">"
+                 <div id=\"synthetic-implementations-list\">",
             );
             render_impls(cx, w, &synthetic, containing_item);
-            write!(w, "</div>");
+            w.write_str("</div>");
         }
 
         if !blanket_impl.is_empty() {
-            write!(
-                w,
+            w.write_str(
                 "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\
                      Blanket Implementations\
                      <a href=\"#blanket-implementations\" class=\"anchor\"></a>\
                  </h2>\
-                 <div id=\"blanket-implementations-list\">"
+                 <div id=\"blanket-implementations-list\">",
             );
             render_impls(cx, w, &blanket_impl, containing_item);
-            write!(w, "</div>");
+            w.write_str("</div>");
         }
     }
 }
@@ -3776,7 +3772,7 @@ fn render_impl(
             if show_def_docs {
                 for it in &i.inner_impl().items {
                     if let clean::TypedefItem(ref tydef, _) = *it.kind {
-                        write!(w, "<span class=\"where fmt-newline\">  ");
+                        w.write_str("<span class=\"where fmt-newline\">  ");
                         assoc_type(
                             w,
                             it,
@@ -3786,11 +3782,11 @@ fn render_impl(
                             "",
                             cx.cache(),
                         );
-                        write!(w, ";</span>");
+                        w.write_str(";</span>");
                     }
                 }
             }
-            write!(w, "</code>");
+            w.write_str("</code>");
         } else {
             write!(
                 w,
@@ -3809,7 +3805,7 @@ fn render_impl(
             outer_const_version,
         );
         write_srclink(cx, &i.impl_item, w);
-        write!(w, "</h3>");
+        w.write_str("</h3>");
 
         if trait_.is_some() {
             if let Some(portability) = portability(&i.impl_item, Some(parent)) {
@@ -3872,9 +3868,9 @@ fn render_impl(
                 if render_method_item {
                     let id = cx.derive_id(format!("{}.{}", item_type, name));
                     write!(w, "<h4 id=\"{}\" class=\"{}{}\">", id, item_type, extra_class);
-                    write!(w, "<code>");
+                    w.write_str("<code>");
                     render_assoc_item(w, item, link.anchor(&id), ItemType::Impl, cx);
-                    write!(w, "</code>");
+                    w.write_str("</code>");
                     render_stability_since_raw(
                         w,
                         item.stable_since(cx.tcx()).as_deref(),
@@ -3883,7 +3879,7 @@ fn render_impl(
                         outer_const_version,
                     );
                     write_srclink(cx, item, w);
-                    write!(w, "</h4>");
+                    w.write_str("</h4>");
                 }
             }
             clean::TypedefItem(ref tydef, _) => {
@@ -3898,13 +3894,13 @@ fn render_impl(
                     "",
                     cx.cache(),
                 );
-                write!(w, "</code></h4>");
+                w.write_str("</code></h4>");
             }
             clean::AssocConstItem(ref ty, ref default) => {
                 let id = cx.derive_id(format!("{}.{}", item_type, name));
                 write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class);
                 assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), "", cx);
-                write!(w, "</code>");
+                w.write_str("</code>");
                 render_stability_since_raw(
                     w,
                     item.stable_since(cx.tcx()).as_deref(),
@@ -3913,13 +3909,13 @@ fn render_impl(
                     outer_const_version,
                 );
                 write_srclink(cx, item, w);
-                write!(w, "</h4>");
+                w.write_str("</h4>");
             }
             clean::AssocTypeItem(ref bounds, ref default) => {
                 let id = cx.derive_id(format!("{}.{}", item_type, name));
                 write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class);
                 assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id), "", cx.cache());
-                write!(w, "</code></h4>");
+                w.write_str("</code></h4>");
             }
             clean::StrippedItem(..) => return,
             _ => panic!("can't make docs for trait item with name {:?}", item.name),
@@ -3963,7 +3959,7 @@ fn render_impl(
         }
     }
 
-    write!(w, "<div class=\"impl-items\">");
+    w.write_str("<div class=\"impl-items\">");
     for trait_item in &i.inner_impl().items {
         doc_impl_item(
             w,
@@ -4034,11 +4030,11 @@ fn render_impl(
             );
         }
     }
-    write!(w, "</div>");
+    w.write_str("</div>");
 }
 
 fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
-    write!(w, "<pre class=\"rust opaque\">");
+    w.write_str("<pre class=\"rust opaque\">");
     render_attributes(w, it, false);
     write!(
         w,
@@ -4060,7 +4056,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean:
 }
 
 fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
-    write!(w, "<pre class=\"rust trait-alias\">");
+    w.write_str("<pre class=\"rust trait-alias\">");
     render_attributes(w, it, false);
     write!(
         w,
@@ -4081,7 +4077,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea
 }
 
 fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
-    write!(w, "<pre class=\"rust typedef\">");
+    w.write_str("<pre class=\"rust typedef\">");
     render_attributes(w, it, false);
     write!(
         w,
@@ -4103,7 +4099,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T
 }
 
 fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
-    writeln!(w, "<pre class=\"rust foreigntype\">extern {{");
+    w.write_str("<pre class=\"rust foreigntype\">extern {");
     render_attributes(w, it, false);
     write!(
         w,
@@ -4163,7 +4159,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
         }
     }
 
-    write!(buffer, "<div class=\"sidebar-elems\">");
+    buffer.write_str("<div class=\"sidebar-elems\">");
     if it.is_crate() {
         write!(
             buffer,
@@ -4191,10 +4187,10 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
     // as much HTML as possible in order to allow non-JS-enabled browsers
     // to navigate the documentation (though slightly inefficiently).
 
-    write!(buffer, "<p class=\"location\">");
+    buffer.write_str("<p class=\"location\">");
     for (i, name) in cx.current.iter().take(parentlen).enumerate() {
         if i > 0 {
-            write!(buffer, "::<wbr>");
+            buffer.write_str("::<wbr>");
         }
         write!(
             buffer,
@@ -4203,7 +4199,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
             *name
         );
     }
-    write!(buffer, "</p>");
+    buffer.write_str("</p>");
 
     // Sidebar refers to the enclosing module, not this module.
     let relpath = if it.is_mod() { "../" } else { "" };
@@ -4222,7 +4218,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
         write!(buffer, "<script defer src=\"{path}sidebar-items.js\"></script>", path = relpath);
     }
     // Closes sidebar-elems div.
-    write!(buffer, "</div>");
+    buffer.write_str("</div>");
 }
 
 fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
@@ -4514,7 +4510,7 @@ fn extract_for_impl_name(item: &clean::Item, cache: &Cache) -> Option<(String, S
 }
 
 fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
-    write!(buf, "<div class=\"block items\">");
+    buf.write_str("<div class=\"block items\">");
 
     fn print_sidebar_section(
         out: &mut Buffer,
@@ -4618,7 +4614,7 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean
         );
     }
 
-    write!(buf, "</div>")
+    buf.push_str("</div>")
 }
 
 fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
@@ -4800,27 +4796,27 @@ fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean
     let name = it.name.as_ref().expect("proc-macros always have names");
     match m.kind {
         MacroKind::Bang => {
-            write!(w, "<pre class=\"rust macro\">");
+            w.push_str("<pre class=\"rust macro\">");
             write!(w, "{}!() {{ /* proc-macro */ }}", name);
-            write!(w, "</pre>");
+            w.push_str("</pre>");
         }
         MacroKind::Attr => {
-            write!(w, "<pre class=\"rust attr\">");
+            w.push_str("<pre class=\"rust attr\">");
             write!(w, "#[{}]", name);
-            write!(w, "</pre>");
+            w.push_str("</pre>");
         }
         MacroKind::Derive => {
-            write!(w, "<pre class=\"rust derive\">");
+            w.push_str("<pre class=\"rust derive\">");
             write!(w, "#[derive({})]", name);
             if !m.helpers.is_empty() {
-                writeln!(w, "\n{{");
-                writeln!(w, "    // Attributes available to this derive:");
+                w.push_str("\n{");
+                w.push_str("    // Attributes available to this derive:");
                 for attr in &m.helpers {
                     writeln!(w, "    #[{}]", attr);
                 }
-                write!(w, "}}");
+                w.push_str("}");
             }
-            write!(w, "</pre>");
+            w.push_str("</pre>");
         }
     }
     document(w, cx, it, None)

From d5cac19d9145930bb44309a2cf82527a332fd941 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Thu, 28 Jan 2021 23:18:01 +0100
Subject: [PATCH 24/25] Fewer temporary strings

---
 src/librustdoc/formats/item_type.rs    |   2 +-
 src/librustdoc/html/highlight.rs       |  44 ++++----
 src/librustdoc/html/highlight/tests.rs |   9 +-
 src/librustdoc/html/markdown.rs        |  17 ++-
 src/librustdoc/html/render/mod.rs      | 145 +++++++++++++++----------
 src/librustdoc/html/sources.rs         |   6 +-
 6 files changed, 126 insertions(+), 97 deletions(-)

diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index ad51adf2fe3f5..f904de22f6ba4 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -158,6 +158,6 @@ impl ItemType {
 
 impl fmt::Display for ItemType {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.as_str())
+        f.write_str(self.as_str())
     }
 }
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index c320fd58b4e82..7e50d72e60f7d 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -7,7 +7,7 @@
 
 use crate::html::escape::Escape;
 
-use std::fmt::{Display, Write};
+use std::fmt::Display;
 use std::iter::Peekable;
 
 use rustc_lexer::{LiteralKind, TokenKind};
@@ -15,16 +15,18 @@ use rustc_span::edition::Edition;
 use rustc_span::symbol::Symbol;
 use rustc_span::with_default_session_globals;
 
+use super::format::Buffer;
+
 /// Highlights `src`, returning the HTML output.
 crate fn render_with_highlighting(
     src: &str,
+    out: &mut Buffer,
     class: Option<&str>,
     playground_button: Option<&str>,
     tooltip: Option<(Option<Edition>, &str)>,
     edition: Edition,
-) -> String {
+) {
     debug!("highlighting: ================\n{}\n==============", src);
-    let mut out = String::with_capacity(src.len());
     if let Some((edition_info, class)) = tooltip {
         write!(
             out,
@@ -35,23 +37,19 @@ crate fn render_with_highlighting(
             } else {
                 String::new()
             },
-        )
-        .unwrap();
+        );
     }
 
-    write_header(&mut out, class);
-    write_code(&mut out, &src, edition);
-    write_footer(&mut out, playground_button);
-
-    out
+    write_header(out, class);
+    write_code(out, &src, edition);
+    write_footer(out, playground_button);
 }
 
-fn write_header(out: &mut String, class: Option<&str>) {
-    write!(out, "<div class=\"example-wrap\"><pre class=\"rust {}\">\n", class.unwrap_or_default())
-        .unwrap()
+fn write_header(out: &mut Buffer, class: Option<&str>) {
+    write!(out, "<div class=\"example-wrap\"><pre class=\"rust {}\">\n", class.unwrap_or_default());
 }
 
-fn write_code(out: &mut String, src: &str, edition: Edition) {
+fn write_code(out: &mut Buffer, src: &str, edition: Edition) {
     // This replace allows to fix how the code source with DOS backline characters is displayed.
     let src = src.replace("\r\n", "\n");
     Classifier::new(&src, edition).highlight(&mut |highlight| {
@@ -63,8 +61,8 @@ fn write_code(out: &mut String, src: &str, edition: Edition) {
     });
 }
 
-fn write_footer(out: &mut String, playground_button: Option<&str>) {
-    write!(out, "</pre>{}</div>\n", playground_button.unwrap_or_default()).unwrap()
+fn write_footer(out: &mut Buffer, playground_button: Option<&str>) {
+    write!(out, "</pre>{}</div>\n", playground_button.unwrap_or_default());
 }
 
 /// How a span of text is classified. Mostly corresponds to token kinds.
@@ -331,13 +329,13 @@ impl<'a> Classifier<'a> {
 
 /// Called when we start processing a span of text that should be highlighted.
 /// The `Class` argument specifies how it should be highlighted.
-fn enter_span(out: &mut String, klass: Class) {
-    write!(out, "<span class=\"{}\">", klass.as_html()).unwrap()
+fn enter_span(out: &mut Buffer, klass: Class) {
+    write!(out, "<span class=\"{}\">", klass.as_html());
 }
 
 /// Called at the end of a span of highlighted text.
-fn exit_span(out: &mut String) {
-    write!(out, "</span>").unwrap()
+fn exit_span(out: &mut Buffer) {
+    out.write_str("</span>");
 }
 
 /// Called for a span of text. If the text should be highlighted differently
@@ -351,10 +349,10 @@ fn exit_span(out: &mut String) {
 /// ```
 /// The latter can be thought of as a shorthand for the former, which is more
 /// flexible.
-fn string<T: Display>(out: &mut String, text: T, klass: Option<Class>) {
+fn string<T: Display>(out: &mut Buffer, text: T, klass: Option<Class>) {
     match klass {
-        None => write!(out, "{}", text).unwrap(),
-        Some(klass) => write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text).unwrap(),
+        None => write!(out, "{}", text),
+        Some(klass) => write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text),
     }
 }
 
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index f97c8a7ab7148..305cf61091dc6 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -1,4 +1,5 @@
 use super::write_code;
+use crate::html::format::Buffer;
 use expect_test::expect_file;
 use rustc_span::edition::Edition;
 
@@ -18,9 +19,9 @@ const STYLE: &str = r#"
 fn test_html_highlighting() {
     let src = include_str!("fixtures/sample.rs");
     let html = {
-        let mut out = String::new();
+        let mut out = Buffer::new();
         write_code(&mut out, src, Edition::Edition2018);
-        format!("{}<pre><code>{}</code></pre>\n", STYLE, out)
+        format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
     };
     expect_file!["fixtures/sample.html"].assert_eq(&html);
 }
@@ -30,7 +31,7 @@ fn test_dos_backline() {
     let src = "pub fn foo() {\r\n\
     println!(\"foo\");\r\n\
 }\r\n";
-    let mut html = String::new();
+    let mut html = Buffer::new();
     write_code(&mut html, src, Edition::Edition2018);
-    expect_file!["fixtures/dos_line.html"].assert_eq(&html);
+    expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
 }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 1cf30ecae90ec..bdb92844f07ad 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -41,6 +41,8 @@ use pulldown_cmark::{
     html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag,
 };
 
+use super::format::Buffer;
+
 #[cfg(test)]
 mod tests;
 
@@ -235,9 +237,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
         }
         let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
         let text = lines.collect::<Vec<Cow<'_, str>>>().join("\n");
-        // insert newline to clearly separate it from the
-        // previous block so we can shorten the html output
-        let mut s = String::from("\n");
+
         let playground_button = self.playground.as_ref().and_then(|playground| {
             let krate = &playground.crate_name;
             let url = &playground.url;
@@ -298,8 +298,13 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             None
         };
 
-        s.push_str(&highlight::render_with_highlighting(
+        // insert newline to clearly separate it from the
+        // previous block so we can shorten the html output
+        let mut s = Buffer::new();
+        s.push_str("\n");
+        highlight::render_with_highlighting(
             &text,
+            &mut s,
             Some(&format!(
                 "rust-example-rendered{}",
                 if let Some((_, class)) = tooltip { format!(" {}", class) } else { String::new() }
@@ -307,8 +312,8 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             playground_button.as_deref(),
             tooltip,
             edition,
-        ));
-        Some(Event::Html(s.into()))
+        );
+        Some(Event::Html(s.into_inner().into()))
     }
 }
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index cc7a42de32069..c869faa62d380 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -3684,19 +3684,21 @@ fn spotlight_decl(decl: &clean::FnDecl, cache: &Cache) -> String {
                 if impl_.trait_.def_id_full(cache).map_or(false, |d| cache.traits[&d].is_spotlight)
                 {
                     if out.is_empty() {
-                        out.push_str(&format!(
+                        write!(
+                            &mut out,
                             "<h3 class=\"notable\">Notable traits for {}</h3>\
                              <code class=\"content\">",
                             impl_.for_.print(cache)
-                        ));
+                        );
                         trait_.push_str(&impl_.for_.print(cache).to_string());
                     }
 
                     //use the "where" class here to make it small
-                    out.push_str(&format!(
+                    write!(
+                        &mut out,
                         "<span class=\"where fmt-newline\">{}</span>",
                         impl_.print(cache)
-                    ));
+                    );
                     let t_did = impl_.trait_.def_id_full(cache).unwrap();
                     for it in &impl_.items {
                         if let clean::TypedefItem(ref tydef, _) = *it.kind {
@@ -4292,8 +4294,7 @@ fn small_url_encode(s: String) -> String {
     }
 }
 
-fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
-    let mut out = String::new();
+fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
     if let Some(v) = cx.cache.impls.get(&it.def_id) {
         let mut used_links = FxHashSet::default();
 
@@ -4309,11 +4310,15 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
             if !ret.is_empty() {
                 // We want links' order to be reproducible so we don't use unstable sort.
                 ret.sort();
-                out.push_str(&format!(
+
+                out.push_str(
                     "<a class=\"sidebar-title\" href=\"#implementations\">Methods</a>\
-                     <div class=\"sidebar-links\">{}</div>",
-                    ret.join("")
-                ));
+                     <div class=\"sidebar-links\">",
+                );
+                for line in ret {
+                    out.push_str(&line);
+                }
+                out.push_str("</div>");
             }
         }
 
@@ -4323,7 +4328,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
                 .filter(|i| i.inner_impl().trait_.is_some())
                 .find(|i| i.inner_impl().trait_.def_id_full(cx.cache()) == cx.cache.deref_trait_did)
             {
-                out.push_str(&sidebar_deref_methods(cx, impl_, v));
+                sidebar_deref_methods(cx, out, impl_, v);
             }
             let format_impls = |impls: Vec<&Impl>| {
                 let mut links = FxHashSet::default();
@@ -4348,7 +4353,15 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
                     })
                     .collect::<Vec<String>>();
                 ret.sort();
-                ret.join("")
+                ret
+            };
+
+            let write_sidebar_links = |out: &mut Buffer, links: Vec<String>| {
+                out.push_str("<div class=\"sidebar-links\">");
+                for link in links {
+                    out.push_str(&link);
+                }
+                out.push_str("</div>");
             };
 
             let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
@@ -4366,7 +4379,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
                     "<a class=\"sidebar-title\" href=\"#trait-implementations\">\
                         Trait Implementations</a>",
                 );
-                out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", concrete_format));
+                write_sidebar_links(out, concrete_format);
             }
 
             if !synthetic_format.is_empty() {
@@ -4374,7 +4387,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
                     "<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\
                         Auto Trait Implementations</a>",
                 );
-                out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", synthetic_format));
+                write_sidebar_links(out, synthetic_format);
             }
 
             if !blanket_format.is_empty() {
@@ -4382,16 +4395,13 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
                     "<a class=\"sidebar-title\" href=\"#blanket-implementations\">\
                         Blanket Implementations</a>",
                 );
-                out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", blanket_format));
+                write_sidebar_links(out, blanket_format);
             }
         }
     }
-
-    out
 }
 
-fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec<Impl>) -> String {
-    let mut out = String::new();
+fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &Vec<Impl>) {
     let c = cx.cache();
 
     debug!("found Deref: {:?}", impl_);
@@ -4428,15 +4438,20 @@ fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec<Impl>) -> Strin
                 let id = deref_id_map
                     .get(&real_target.def_id_full(cx.cache()).unwrap())
                     .expect("Deref section without derived id");
-                out.push_str(&format!(
+                write!(
+                    out,
                     "<a class=\"sidebar-title\" href=\"#{}\">Methods from {}&lt;Target={}&gt;</a>",
                     id,
                     Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(c))),
                     Escape(&format!("{:#}", real_target.print(c))),
-                ));
+                );
                 // We want links' order to be reproducible so we don't use unstable sort.
                 ret.sort();
-                out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret.join("")));
+                out.push_str("<div class=\"sidebar-links\">");
+                for link in ret {
+                    out.push_str(&link);
+                }
+                out.push_str("</div>");
             }
         }
 
@@ -4452,36 +4467,39 @@ fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec<Impl>) -> Strin
                         // `impl Deref<Target = S> for S`
                         if target_did == type_did {
                             // Avoid infinite cycles
-                            return out;
+                            return;
                         }
                     }
-                    out.push_str(&sidebar_deref_methods(cx, target_deref_impl, target_impls));
+                    sidebar_deref_methods(cx, out, target_deref_impl, target_impls);
                 }
             }
         }
     }
-
-    out
 }
 
 fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
-    let mut sidebar = String::new();
+    let mut sidebar = Buffer::new();
     let fields = get_struct_fields_name(&s.fields);
 
     if !fields.is_empty() {
         if let CtorKind::Fictive = s.struct_type {
-            sidebar.push_str(&format!(
+            sidebar.push_str(
                 "<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\
-                 <div class=\"sidebar-links\">{}</div>",
-                fields
-            ));
+                <div class=\"sidebar-links\">",
+            );
+
+            for field in fields {
+                sidebar.push_str(&field);
+            }
+
+            sidebar.push_str("</div>");
         }
     }
 
-    sidebar.push_str(&sidebar_assoc_items(cx, it));
+    sidebar_assoc_items(cx, &mut sidebar, it);
 
     if !sidebar.is_empty() {
-        write!(buf, "<div class=\"block items\">{}</div>", sidebar);
+        write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
     }
 }
 
@@ -4598,13 +4616,13 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean
                  <div class=\"sidebar-links\">",
             );
             for (name, id) in res.into_iter() {
-                buf.push_str(&format!("<a href=\"#{}\">{}</a>", id, Escape(&name)));
+                write!(buf, "<a href=\"#{}\">{}</a>", id, Escape(&name));
             }
             buf.push_str("</div>");
         }
     }
 
-    buf.push_str(&sidebar_assoc_items(cx, it));
+    sidebar_assoc_items(cx, buf, it);
 
     buf.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>");
     if t.is_auto {
@@ -4618,57 +4636,61 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean
 }
 
 fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let sidebar = sidebar_assoc_items(cx, it);
+    let mut sidebar = Buffer::new();
+    sidebar_assoc_items(cx, &mut sidebar, it);
 
     if !sidebar.is_empty() {
-        write!(buf, "<div class=\"block items\">{}</div>", sidebar);
+        write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
     }
 }
 
 fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let sidebar = sidebar_assoc_items(cx, it);
+    let mut sidebar = Buffer::new();
+    sidebar_assoc_items(cx, &mut sidebar, it);
 
     if !sidebar.is_empty() {
-        write!(buf, "<div class=\"block items\">{}</div>", sidebar);
+        write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
     }
 }
 
-fn get_struct_fields_name(fields: &[clean::Item]) -> String {
+fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> {
     let mut fields = fields
         .iter()
         .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
-        .filter_map(|f| match f.name {
-            Some(ref name) => {
-                Some(format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
-            }
-            _ => None,
+        .filter_map(|f| {
+            f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
         })
         .collect::<Vec<_>>();
     fields.sort();
-    fields.join("")
+    fields
 }
 
 fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
-    let mut sidebar = String::new();
+    let mut sidebar = Buffer::new();
     let fields = get_struct_fields_name(&u.fields);
 
     if !fields.is_empty() {
-        sidebar.push_str(&format!(
+        sidebar.push_str(
             "<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\
-             <div class=\"sidebar-links\">{}</div>",
-            fields
-        ));
+            <div class=\"sidebar-links\">",
+        );
+
+        for field in fields {
+            sidebar.push_str(&field);
+        }
+
+        sidebar.push_str("</div>");
     }
 
-    sidebar.push_str(&sidebar_assoc_items(cx, it));
+    sidebar_assoc_items(cx, &mut sidebar, it);
 
     if !sidebar.is_empty() {
-        write!(buf, "<div class=\"block items\">{}</div>", sidebar);
+        write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
     }
 }
 
 fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
-    let mut sidebar = String::new();
+    let mut sidebar = Buffer::new();
 
     let mut variants = e
         .variants
@@ -4687,10 +4709,10 @@ fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean:
         ));
     }
 
-    sidebar.push_str(&sidebar_assoc_items(cx, it));
+    sidebar_assoc_items(cx, &mut sidebar, it);
 
     if !sidebar.is_empty() {
-        write!(buf, "<div class=\"block items\">{}</div>", sidebar);
+        write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
     }
 }
 
@@ -4773,21 +4795,24 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
 }
 
 fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let sidebar = sidebar_assoc_items(cx, it);
+    let mut sidebar = Buffer::new();
+    sidebar_assoc_items(cx, &mut sidebar, it);
+
     if !sidebar.is_empty() {
-        write!(buf, "<div class=\"block items\">{}</div>", sidebar);
+        write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
     }
 }
 
 fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
     wrap_into_docblock(w, |w| {
-        w.write_str(&highlight::render_with_highlighting(
+        highlight::render_with_highlighting(
             &t.source,
+            w,
             Some("macro"),
             None,
             None,
             it.source.span().edition(),
-        ))
+        );
     });
     document(w, cx, it, None)
 }
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index da66ae13377c0..d4006c878cad6 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -178,10 +178,10 @@ fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
         cols += 1;
         tmp /= 10;
     }
-    write!(buf, "<pre class=\"line-numbers\">");
+    buf.write_str("<pre class=\"line-numbers\">");
     for i in 1..=lines {
         write!(buf, "<span id=\"{0}\">{0:1$}</span>\n", i, cols);
     }
-    write!(buf, "</pre>");
-    write!(buf, "{}", highlight::render_with_highlighting(s, None, None, None, edition));
+    buf.write_str("</pre>");
+    highlight::render_with_highlighting(s, buf, None, None, None, edition);
 }

From c9c120024467de5f8b933b9dea97c5d0d776ef71 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Buga?= <bugadani@gmail.com>
Date: Fri, 29 Jan 2021 11:45:27 +0100
Subject: [PATCH 25/25] Address review comments

---
 src/librustdoc/html/escape.rs     |  6 ++++--
 src/librustdoc/html/render/mod.rs | 15 ++++++++-------
 src/librustdoc/html/sources.rs    |  3 +--
 3 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs
index 5200eb3b9071d..ce44722a532b0 100644
--- a/src/librustdoc/html/escape.rs
+++ b/src/librustdoc/html/escape.rs
@@ -16,8 +16,8 @@ impl<'a> fmt::Display for Escape<'a> {
         let Escape(s) = *self;
         let pile_o_bits = s;
         let mut last = 0;
-        for (i, ch) in s.bytes().enumerate() {
-            let s = match ch as char {
+        for (i, ch) in s.char_indices() {
+            let s = match ch {
                 '>' => "&gt;",
                 '<' => "&lt;",
                 '&' => "&amp;",
@@ -27,6 +27,8 @@ impl<'a> fmt::Display for Escape<'a> {
             };
             fmt.write_str(&pile_o_bits[last..i])?;
             fmt.write_str(s)?;
+            // NOTE: we only expect single byte characters here - which is fine as long as we
+            // only match single byte characters
             last = i + 1;
         }
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index c869faa62d380..daaa2c719b0e6 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -359,7 +359,6 @@ crate struct StylePath {
 
 thread_local!(crate static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
 
-// FIXME: make this work
 crate const INITIAL_IDS: [&'static str; 15] = [
     "main",
     "search",
@@ -4101,7 +4100,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T
 }
 
 fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
-    w.write_str("<pre class=\"rust foreigntype\">extern {");
+    w.write_str("<pre class=\"rust foreigntype\">extern {\n");
     render_attributes(w, it, false);
     write!(
         w,
@@ -4264,8 +4263,8 @@ fn get_methods(
 fn small_url_encode(s: String) -> String {
     let mut st = String::new();
     let mut last_match = 0;
-    for (idx, c) in s.bytes().enumerate() {
-        let escaped = match c as char {
+    for (idx, c) in s.char_indices() {
+        let escaped = match c {
             '<' => "%3C",
             '>' => "%3E",
             ' ' => "%20",
@@ -4283,6 +4282,8 @@ fn small_url_encode(s: String) -> String {
 
         st += &s[last_match..idx];
         st += escaped;
+        // NOTE: we only expect single byte characters here - which is fine as long as we
+        // only match single byte characters
         last_match = idx + 1;
     }
 
@@ -4834,12 +4835,12 @@ fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean
             w.push_str("<pre class=\"rust derive\">");
             write!(w, "#[derive({})]", name);
             if !m.helpers.is_empty() {
-                w.push_str("\n{");
-                w.push_str("    // Attributes available to this derive:");
+                w.push_str("\n{\n");
+                w.push_str("    // Attributes available to this derive:\n");
                 for attr in &m.helpers {
                     writeln!(w, "    #[{}]", attr);
                 }
-                w.push_str("}");
+                w.push_str("}\n");
             }
             w.push_str("</pre>");
         }
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index d4006c878cad6..bbb833e54aeb9 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -94,8 +94,7 @@ impl SourceCollector<'_, '_> {
         };
 
         // Remove the utf-8 BOM if any
-        let contents =
-            if contents.starts_with('\u{feff}') { &contents[3..] } else { &contents[..] };
+        let contents = if contents.starts_with('\u{feff}') { &contents[3..] } else { &contents };
 
         // Create the intermediate directories
         let mut cur = self.dst.clone();