Skip to content

Commit b0ec3e0

Browse files
committed
Auto merge of #91948 - nnethercote:rustdoc-more-Symbols, r=GuillaumeGomez
rustdoc: avoid many `Symbol` to `String` conversions. Particularly when constructing file paths and fully qualified paths. This avoids a lot of allocations, speeding things up on almost all examples. r? `@GuillaumeGomez`
2 parents ad46af2 + c7147e4 commit b0ec3e0

File tree

14 files changed

+256
-169
lines changed

14 files changed

+256
-169
lines changed

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ symbols! {
567567
doc_spotlight,
568568
doctest,
569569
document_private_items,
570+
dotdot: "..",
570571
dotdot_in_tuple_patterns,
571572
dotdoteq_in_patterns,
572573
dreg,

src/librustdoc/clean/inline.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use rustc_data_structures::thin_vec::ThinVec;
99
use rustc_hir as hir;
1010
use rustc_hir::def::{DefKind, Res};
1111
use rustc_hir::def_id::DefId;
12-
use rustc_hir::definitions::DefPathData;
1312
use rustc_hir::Mutability;
1413
use rustc_metadata::creader::{CStore, LoadedMacro};
1514
use rustc_middle::ty::{self, TyCtxt};
@@ -164,12 +163,10 @@ crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
164163
/// These names are used later on by HTML rendering to generate things like
165164
/// source links back to the original item.
166165
crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
167-
let crate_name = cx.tcx.crate_name(did.krate).to_string();
166+
let crate_name = cx.tcx.crate_name(did.krate);
168167

169-
let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
170-
// Filter out extern blocks
171-
(elem.data != DefPathData::ForeignMod).then(|| elem.data.to_string())
172-
});
168+
let relative =
169+
cx.tcx.def_path(did).data.into_iter().filter_map(|elem| elem.data.get_opt_name());
173170
let fqn = if let ItemType::Macro = kind {
174171
// Check to see if it is a macro 2.0 or built-in macro
175172
if matches!(

src/librustdoc/formats/cache.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
44
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
55
use rustc_middle::middle::privacy::AccessLevels;
66
use rustc_middle::ty::TyCtxt;
7-
use rustc_span::symbol::sym;
7+
use rustc_span::{sym, Symbol};
88

99
use crate::clean::{self, types::ExternalLocation, ExternalCrate, ItemId, PrimitiveType};
1010
use crate::core::DocContext;
1111
use crate::fold::DocFolder;
1212
use crate::formats::item_type::ItemType;
1313
use crate::formats::Impl;
14+
use crate::html::format::join_with_double_colon;
1415
use crate::html::markdown::short_markdown_summary;
1516
use crate::html::render::search_index::get_function_type_for_search;
1617
use crate::html::render::IndexItem;
@@ -39,11 +40,11 @@ crate struct Cache {
3940
/// URLs when a type is being linked to. External paths are not located in
4041
/// this map because the `External` type itself has all the information
4142
/// necessary.
42-
crate paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
43+
crate paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
4344

4445
/// Similar to `paths`, but only holds external paths. This is only used for
4546
/// generating explicit hyperlinks to other crates.
46-
crate external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
47+
crate external_paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
4748

4849
/// Maps local `DefId`s of exported types to fully qualified paths.
4950
/// Unlike 'paths', this mapping ignores any renames that occur
@@ -55,7 +56,7 @@ crate struct Cache {
5556
/// to the path used if the corresponding type is inlined. By
5657
/// doing this, we can detect duplicate impls on a trait page, and only display
5758
/// the impl for the inlined type.
58-
crate exact_paths: FxHashMap<DefId, Vec<String>>,
59+
crate exact_paths: FxHashMap<DefId, Vec<Symbol>>,
5960

6061
/// This map contains information about all known traits of this crate.
6162
/// Implementations of a crate should inherit the documentation of the
@@ -92,7 +93,7 @@ crate struct Cache {
9293
crate masked_crates: FxHashSet<CrateNum>,
9394

9495
// Private fields only used when initially crawling a crate to build a cache
95-
stack: Vec<String>,
96+
stack: Vec<Symbol>,
9697
parent_stack: Vec<DefId>,
9798
parent_is_trait_impl: bool,
9899
stripped_mod: bool,
@@ -155,7 +156,7 @@ impl Cache {
155156
let dst = &render_options.output;
156157
let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx);
157158
cx.cache.extern_locations.insert(e.crate_num, location);
158-
cx.cache.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module));
159+
cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module));
159160
}
160161

161162
// FIXME: avoid this clone (requires implementing Default manually)
@@ -164,10 +165,9 @@ impl Cache {
164165
let crate_name = tcx.crate_name(def_id.krate);
165166
// Recall that we only allow primitive modules to be at the root-level of the crate.
166167
// If that restriction is ever lifted, this will have to include the relative paths instead.
167-
cx.cache.external_paths.insert(
168-
def_id,
169-
(vec![crate_name.to_string(), prim.as_sym().to_string()], ItemType::Primitive),
170-
);
168+
cx.cache
169+
.external_paths
170+
.insert(def_id, (vec![crate_name, prim.as_sym()], ItemType::Primitive));
171171
}
172172

173173
krate = CacheBuilder { tcx, cache: &mut cx.cache }.fold_crate(krate);
@@ -299,7 +299,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
299299
self.cache.search_index.push(IndexItem {
300300
ty: item.type_(),
301301
name: s.to_string(),
302-
path: path.join("::"),
302+
path: join_with_double_colon(path),
303303
desc,
304304
parent,
305305
parent_idx: None,
@@ -320,7 +320,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
320320
// Keep track of the fully qualified path for this item.
321321
let pushed = match item.name {
322322
Some(n) if !n.is_empty() => {
323-
self.cache.stack.push(n.to_string());
323+
self.cache.stack.push(n);
324324
true
325325
}
326326
_ => false,

src/librustdoc/html/format.rs

+60-39
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_middle::ty;
1919
use rustc_middle::ty::DefIdTree;
2020
use rustc_middle::ty::TyCtxt;
2121
use rustc_span::def_id::CRATE_DEF_INDEX;
22+
use rustc_span::{sym, Symbol};
2223
use rustc_target::spec::abi::Abi;
2324

2425
use crate::clean::{
@@ -29,6 +30,7 @@ use crate::formats::item_type::ItemType;
2930
use crate::html::escape::Escape;
3031
use crate::html::render::Context;
3132

33+
use super::url_parts_builder::estimate_item_path_byte_length;
3234
use super::url_parts_builder::UrlPartsBuilder;
3335

3436
crate trait Print {
@@ -502,11 +504,22 @@ crate enum HrefError {
502504
NotInExternalCache,
503505
}
504506

507+
// Panics if `syms` is empty.
508+
crate fn join_with_double_colon(syms: &[Symbol]) -> String {
509+
let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len()));
510+
s.push_str(&syms[0].as_str());
511+
for sym in &syms[1..] {
512+
s.push_str("::");
513+
s.push_str(&sym.as_str());
514+
}
515+
s
516+
}
517+
505518
crate fn href_with_root_path(
506519
did: DefId,
507520
cx: &Context<'_>,
508521
root_path: Option<&str>,
509-
) -> Result<(String, ItemType, Vec<String>), HrefError> {
522+
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
510523
let tcx = cx.tcx();
511524
let def_kind = tcx.def_kind(did);
512525
let did = match def_kind {
@@ -518,7 +531,7 @@ crate fn href_with_root_path(
518531
};
519532
let cache = cx.cache();
520533
let relative_to = &cx.current;
521-
fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
534+
fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
522535
if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
523536
}
524537

@@ -533,9 +546,9 @@ crate fn href_with_root_path(
533546
let mut is_remote = false;
534547
let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
535548
Some(&(ref fqp, shortty)) => (fqp, shortty, {
536-
let module_fqp = to_module_fqp(shortty, fqp);
549+
let module_fqp = to_module_fqp(shortty, fqp.as_slice());
537550
debug!(?fqp, ?shortty, ?module_fqp);
538-
href_relative_parts(module_fqp, relative_to)
551+
href_relative_parts(module_fqp, relative_to).collect()
539552
}),
540553
None => {
541554
if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
@@ -548,10 +561,12 @@ crate fn href_with_root_path(
548561
is_remote = true;
549562
let s = s.trim_end_matches('/');
550563
let mut builder = UrlPartsBuilder::singleton(s);
551-
builder.extend(module_fqp.iter().map(String::as_str));
564+
builder.extend(module_fqp.iter().copied());
552565
builder
553566
}
554-
ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
567+
ExternalLocation::Local => {
568+
href_relative_parts(module_fqp, relative_to).collect()
569+
}
555570
ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
556571
},
557572
)
@@ -567,45 +582,50 @@ crate fn href_with_root_path(
567582
}
568583
}
569584
debug!(?url_parts);
570-
let last = &fqp.last().unwrap()[..];
571585
match shortty {
572586
ItemType::Module => {
573587
url_parts.push("index.html");
574588
}
575589
_ => {
576-
let filename = format!("{}.{}.html", shortty.as_str(), last);
577-
url_parts.push(&filename);
590+
let prefix = shortty.as_str();
591+
let last = fqp.last().unwrap();
592+
url_parts.push_fmt(format_args!("{}.{}.html", prefix, last));
578593
}
579594
}
580595
Ok((url_parts.finish(), shortty, fqp.to_vec()))
581596
}
582597

583-
crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
598+
crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
584599
href_with_root_path(did, cx, None)
585600
}
586601

587602
/// Both paths should only be modules.
588603
/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
589604
/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
590-
crate fn href_relative_parts(fqp: &[String], relative_to_fqp: &[String]) -> UrlPartsBuilder {
605+
crate fn href_relative_parts<'fqp>(
606+
fqp: &'fqp [Symbol],
607+
relative_to_fqp: &[Symbol],
608+
) -> Box<dyn Iterator<Item = Symbol> + 'fqp> {
591609
for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
592610
// e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
593611
if f != r {
594612
let dissimilar_part_count = relative_to_fqp.len() - i;
595-
let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
596-
return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
613+
let fqp_module = &fqp[i..fqp.len()];
614+
return box iter::repeat(sym::dotdot)
615+
.take(dissimilar_part_count)
616+
.chain(fqp_module.iter().copied());
597617
}
598618
}
599619
// e.g. linking to std::sync::atomic from std::sync
600620
if relative_to_fqp.len() < fqp.len() {
601-
fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
621+
box fqp[relative_to_fqp.len()..fqp.len()].iter().copied()
602622
// e.g. linking to std::sync from std::sync::atomic
603623
} else if fqp.len() < relative_to_fqp.len() {
604624
let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
605-
iter::repeat("..").take(dissimilar_part_count).collect()
625+
box iter::repeat(sym::dotdot).take(dissimilar_part_count)
606626
// linking to the same module
607627
} else {
608-
UrlPartsBuilder::new()
628+
box iter::empty()
609629
}
610630
}
611631

@@ -632,14 +652,14 @@ fn resolved_path<'cx>(
632652
if let Ok((_, _, fqp)) = href(did, cx) {
633653
format!(
634654
"{}::{}",
635-
fqp[..fqp.len() - 1].join("::"),
636-
anchor(did, fqp.last().unwrap(), cx)
655+
join_with_double_colon(&fqp[..fqp.len() - 1]),
656+
anchor(did, *fqp.last().unwrap(), cx)
637657
)
638658
} else {
639659
last.name.to_string()
640660
}
641661
} else {
642-
anchor(did, last.name.as_str(), cx).to_string()
662+
anchor(did, last.name, cx).to_string()
643663
};
644664
write!(w, "{}{}", path, last.args.print(cx))?;
645665
}
@@ -668,30 +688,31 @@ fn primitive_link(
668688
needs_termination = true;
669689
}
670690
Some(&def_id) => {
671-
let cname_sym;
672691
let loc = match m.extern_locations[&def_id.krate] {
673692
ExternalLocation::Remote(ref s) => {
674-
cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
675-
Some(vec![s.trim_end_matches('/'), cname_sym.as_str()])
693+
let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
694+
let builder: UrlPartsBuilder =
695+
[s.as_str().trim_end_matches('/'), cname_sym.as_str()]
696+
.into_iter()
697+
.collect();
698+
Some(builder)
676699
}
677700
ExternalLocation::Local => {
678-
cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
679-
Some(if cx.current.first().map(|x| &x[..]) == Some(cname_sym.as_str()) {
680-
iter::repeat("..").take(cx.current.len() - 1).collect()
701+
let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
702+
Some(if cx.current.first() == Some(&cname_sym) {
703+
iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect()
681704
} else {
682-
let cname = iter::once(cname_sym.as_str());
683-
iter::repeat("..").take(cx.current.len()).chain(cname).collect()
705+
iter::repeat(sym::dotdot)
706+
.take(cx.current.len())
707+
.chain(iter::once(cname_sym))
708+
.collect()
684709
})
685710
}
686711
ExternalLocation::Unknown => None,
687712
};
688-
if let Some(loc) = loc {
689-
write!(
690-
f,
691-
"<a class=\"primitive\" href=\"{}/primitive.{}.html\">",
692-
loc.join("/"),
693-
prim.as_sym()
694-
)?;
713+
if let Some(mut loc) = loc {
714+
loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
715+
write!(f, "<a class=\"primitive\" href=\"{}\">", loc.finish())?;
695716
needs_termination = true;
696717
}
697718
}
@@ -730,7 +751,7 @@ fn tybounds<'a, 'tcx: 'a>(
730751

731752
crate fn anchor<'a, 'cx: 'a>(
732753
did: DefId,
733-
text: &'a str,
754+
text: Symbol,
734755
cx: &'cx Context<'_>,
735756
) -> impl fmt::Display + 'a {
736757
let parts = href(did, cx);
@@ -742,8 +763,8 @@ crate fn anchor<'a, 'cx: 'a>(
742763
short_ty,
743764
url,
744765
short_ty,
745-
fqp.join("::"),
746-
text
766+
join_with_double_colon(&fqp),
767+
text.as_str()
747768
)
748769
} else {
749770
write!(f, "{}", text)
@@ -960,7 +981,7 @@ fn fmt_type<'cx>(
960981
url = url,
961982
shortty = ItemType::AssocType,
962983
name = name,
963-
path = path.join("::")
984+
path = join_with_double_colon(path),
964985
)?;
965986
}
966987
_ => write!(f, "{}", name)?,
@@ -1270,7 +1291,7 @@ impl clean::Visibility {
12701291
debug!("path={:?}", path);
12711292
// modified from `resolved_path()` to work with `DefPathData`
12721293
let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
1273-
let anchor = anchor(vis_did, last_name.as_str(), cx).to_string();
1294+
let anchor = anchor(vis_did, last_name, cx).to_string();
12741295

12751296
let mut s = "pub(in ".to_owned();
12761297
for seg in &path.data[..path.data.len() - 1] {

0 commit comments

Comments
 (0)