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 { - '>' => ">", - '<' => "<", - '&' => "&", - '\'' => "'", - '"' => """, - _ => unreachable!(), - }; - fmt.write_str(s)?; - last = i + 1; - } - _ => {} - } + let s = match ch as char { + '>' => ">", + '<' => "<", + '&' => "&", + '\'' => "'", + '"' => """, + _ => 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\">−</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, ", ") + w.write_str(", ") } 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, ", "); + w.write_str(", "); } 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 {}<Target={}></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 { '>' => ">", '<' => "<", '&' => "&", @@ -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();