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.
This commit is contained in:
Nicholas Nethercote 2021-12-15 06:18:18 +11:00 committed by Noah Lev
parent 02c9e73e6c
commit 10b1c9aa8b
13 changed files with 198 additions and 120 deletions

View File

@ -567,6 +567,7 @@ symbols! {
doc_spotlight,
doctest,
document_private_items,
dotdot: "..",
dotdot_in_tuple_patterns,
dotdoteq_in_patterns,
dreg,

View File

@ -9,7 +9,6 @@ use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::definitions::DefPathData;
use rustc_hir::Mutability;
use rustc_metadata::creader::{CStore, LoadedMacro};
use rustc_middle::ty::{self, TyCtxt};
@ -164,12 +163,10 @@ crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
let crate_name = cx.tcx.crate_name(did.krate).to_string();
let crate_name = cx.tcx.crate_name(did.krate);
let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
// Filter out extern blocks
(elem.data != DefPathData::ForeignMod).then(|| elem.data.to_string())
});
let relative =
cx.tcx.def_path(did).data.into_iter().filter_map(|elem| elem.data.get_opt_name());
let fqn = if let ItemType::Macro = kind {
// Check to see if it is a macro 2.0 or built-in macro
if matches!(

View File

@ -4,13 +4,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
use rustc_middle::middle::privacy::AccessLevels;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::sym;
use rustc_span::{sym, Symbol};
use crate::clean::{self, types::ExternalLocation, ExternalCrate, ItemId, PrimitiveType};
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::formats::item_type::ItemType;
use crate::formats::Impl;
use crate::html::format::join_with_double_colon;
use crate::html::markdown::short_markdown_summary;
use crate::html::render::search_index::get_function_type_for_search;
use crate::html::render::IndexItem;
@ -39,11 +40,11 @@ crate struct Cache {
/// URLs when a type is being linked to. External paths are not located in
/// this map because the `External` type itself has all the information
/// necessary.
crate paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
crate paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
/// Similar to `paths`, but only holds external paths. This is only used for
/// generating explicit hyperlinks to other crates.
crate external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
crate external_paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
/// Maps local `DefId`s of exported types to fully qualified paths.
/// Unlike 'paths', this mapping ignores any renames that occur
@ -55,7 +56,7 @@ crate struct Cache {
/// to the path used if the corresponding type is inlined. By
/// doing this, we can detect duplicate impls on a trait page, and only display
/// the impl for the inlined type.
crate exact_paths: FxHashMap<DefId, Vec<String>>,
crate exact_paths: FxHashMap<DefId, Vec<Symbol>>,
/// This map contains information about all known traits of this crate.
/// Implementations of a crate should inherit the documentation of the
@ -92,7 +93,7 @@ crate struct Cache {
crate masked_crates: FxHashSet<CrateNum>,
// Private fields only used when initially crawling a crate to build a cache
stack: Vec<String>,
stack: Vec<Symbol>,
parent_stack: Vec<DefId>,
parent_is_trait_impl: bool,
stripped_mod: bool,
@ -155,7 +156,7 @@ impl Cache {
let dst = &render_options.output;
let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx);
cx.cache.extern_locations.insert(e.crate_num, location);
cx.cache.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module));
cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module));
}
// FIXME: avoid this clone (requires implementing Default manually)
@ -164,10 +165,9 @@ impl Cache {
let crate_name = tcx.crate_name(def_id.krate);
// Recall that we only allow primitive modules to be at the root-level of the crate.
// If that restriction is ever lifted, this will have to include the relative paths instead.
cx.cache.external_paths.insert(
def_id,
(vec![crate_name.to_string(), prim.as_sym().to_string()], ItemType::Primitive),
);
cx.cache
.external_paths
.insert(def_id, (vec![crate_name, prim.as_sym()], ItemType::Primitive));
}
krate = CacheBuilder { tcx, cache: &mut cx.cache }.fold_crate(krate);
@ -299,7 +299,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
self.cache.search_index.push(IndexItem {
ty: item.type_(),
name: s.to_string(),
path: path.join("::"),
path: join_with_double_colon(path),
desc,
parent,
parent_idx: None,
@ -320,7 +320,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
// Keep track of the fully qualified path for this item.
let pushed = match item.name {
Some(n) if !n.is_empty() => {
self.cache.stack.push(n.to_string());
self.cache.stack.push(n);
true
}
_ => false,

View File

@ -19,6 +19,7 @@ use rustc_middle::ty;
use rustc_middle::ty::DefIdTree;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::CRATE_DEF_INDEX;
use rustc_span::{sym, Symbol};
use rustc_target::spec::abi::Abi;
use crate::clean::{
@ -502,11 +503,45 @@ crate enum HrefError {
NotInExternalCache,
}
// This mostly works with sequences of symbols, but sometimes the first item
// comes from a string, and in that case we want to trim any trailing `/`.
// `syms` can be empty.
crate fn join_with_slash(first: Option<&str>, syms: &[Symbol]) -> String {
// 64 bytes covers 99.9%+ of cases.
let mut s = String::with_capacity(64);
if let Some(first) = first {
s.push_str(first.trim_end_matches('/'));
if !syms.is_empty() {
s.push('/');
}
}
if !syms.is_empty() {
s.push_str(&syms[0].as_str());
for sym in &syms[1..] {
s.push('/');
s.push_str(&sym.as_str());
}
}
s
}
// Panics if `syms` is empty.
crate fn join_with_double_colon(syms: &[Symbol]) -> String {
// 64 bytes covers 99.9%+ of cases.
let mut s = String::with_capacity(64);
s.push_str(&syms[0].as_str());
for sym in &syms[1..] {
s.push_str("::");
s.push_str(&sym.as_str());
}
s
}
crate fn href_with_root_path(
did: DefId,
cx: &Context<'_>,
root_path: Option<&str>,
) -> Result<(String, ItemType, Vec<String>), HrefError> {
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
let tcx = cx.tcx();
let def_kind = tcx.def_kind(did);
let did = match def_kind {
@ -518,7 +553,7 @@ crate fn href_with_root_path(
};
let cache = cx.cache();
let relative_to = &cx.current;
fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
}
@ -533,9 +568,9 @@ crate fn href_with_root_path(
let mut is_remote = false;
let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
Some(&(ref fqp, shortty)) => (fqp, shortty, {
let module_fqp = to_module_fqp(shortty, fqp);
let module_fqp = to_module_fqp(shortty, fqp.as_slice());
debug!(?fqp, ?shortty, ?module_fqp);
href_relative_parts(module_fqp, relative_to)
href_relative_parts(module_fqp, relative_to).collect()
}),
None => {
if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
@ -548,10 +583,12 @@ crate fn href_with_root_path(
is_remote = true;
let s = s.trim_end_matches('/');
let mut builder = UrlPartsBuilder::singleton(s);
builder.extend(module_fqp.iter().map(String::as_str));
builder.extend(module_fqp.iter().copied());
builder
}
ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
ExternalLocation::Local => {
href_relative_parts(module_fqp, relative_to).collect()
}
ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
},
)
@ -567,45 +604,50 @@ crate fn href_with_root_path(
}
}
debug!(?url_parts);
let last = &fqp.last().unwrap()[..];
match shortty {
ItemType::Module => {
url_parts.push("index.html");
}
_ => {
let filename = format!("{}.{}.html", shortty.as_str(), last);
url_parts.push(&filename);
let prefix = shortty.as_str();
let last = fqp.last().unwrap();
url_parts.push_fmt(format_args!("{}.{}.html", prefix, last));
}
}
Ok((url_parts.finish(), shortty, fqp.to_vec()))
}
crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
href_with_root_path(did, cx, None)
}
/// Both paths should only be modules.
/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
crate fn href_relative_parts(fqp: &[String], relative_to_fqp: &[String]) -> UrlPartsBuilder {
crate fn href_relative_parts<'fqp>(
fqp: &'fqp [Symbol],
relative_to_fqp: &[Symbol],
) -> Box<dyn Iterator<Item = Symbol> + 'fqp> {
for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
// e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
if f != r {
let dissimilar_part_count = relative_to_fqp.len() - i;
let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
let fqp_module = &fqp[i..fqp.len()];
return box iter::repeat(sym::dotdot)
.take(dissimilar_part_count)
.chain(fqp_module.iter().copied());
}
}
// e.g. linking to std::sync::atomic from std::sync
if relative_to_fqp.len() < fqp.len() {
fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
box fqp[relative_to_fqp.len()..fqp.len()].iter().copied()
// e.g. linking to std::sync from std::sync::atomic
} else if fqp.len() < relative_to_fqp.len() {
let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
iter::repeat("..").take(dissimilar_part_count).collect()
box iter::repeat(sym::dotdot).take(dissimilar_part_count)
// linking to the same module
} else {
UrlPartsBuilder::new()
box iter::empty()
}
}
@ -632,14 +674,14 @@ fn resolved_path<'cx>(
if let Ok((_, _, fqp)) = href(did, cx) {
format!(
"{}::{}",
fqp[..fqp.len() - 1].join("::"),
anchor(did, fqp.last().unwrap(), cx)
join_with_double_colon(&fqp[..fqp.len() - 1]),
anchor(did, *fqp.last().unwrap(), cx)
)
} else {
last.name.to_string()
}
} else {
anchor(did, last.name.as_str(), cx).to_string()
anchor(did, last.name, cx).to_string()
};
write!(w, "{}{}", path, last.args.print(cx))?;
}
@ -668,30 +710,31 @@ fn primitive_link(
needs_termination = true;
}
Some(&def_id) => {
let cname_sym;
let loc = match m.extern_locations[&def_id.krate] {
ExternalLocation::Remote(ref s) => {
cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
Some(vec![s.trim_end_matches('/'), cname_sym.as_str()])
let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
let builder: UrlPartsBuilder =
[s.as_str().trim_end_matches('/'), cname_sym.as_str()]
.into_iter()
.collect();
Some(builder)
}
ExternalLocation::Local => {
cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
Some(if cx.current.first().map(|x| &x[..]) == Some(cname_sym.as_str()) {
iter::repeat("..").take(cx.current.len() - 1).collect()
let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
Some(if cx.current.first() == Some(&cname_sym) {
iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect()
} else {
let cname = iter::once(cname_sym.as_str());
iter::repeat("..").take(cx.current.len()).chain(cname).collect()
iter::repeat(sym::dotdot)
.take(cx.current.len())
.chain(iter::once(cname_sym))
.collect()
})
}
ExternalLocation::Unknown => None,
};
if let Some(loc) = loc {
write!(
f,
"<a class=\"primitive\" href=\"{}/primitive.{}.html\">",
loc.join("/"),
prim.as_sym()
)?;
if let Some(mut loc) = loc {
loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
write!(f, "<a class=\"primitive\" href=\"{}\">", loc.finish())?;
needs_termination = true;
}
}
@ -730,7 +773,7 @@ fn tybounds<'a, 'tcx: 'a>(
crate fn anchor<'a, 'cx: 'a>(
did: DefId,
text: &'a str,
text: Symbol,
cx: &'cx Context<'_>,
) -> impl fmt::Display + 'a {
let parts = href(did, cx);
@ -742,8 +785,8 @@ crate fn anchor<'a, 'cx: 'a>(
short_ty,
url,
short_ty,
fqp.join("::"),
text
join_with_double_colon(&fqp),
&*text.as_str()
)
} else {
write!(f, "{}", text)
@ -960,7 +1003,7 @@ fn fmt_type<'cx>(
url = url,
shortty = ItemType::AssocType,
name = name,
path = path.join("::")
path = join_with_double_colon(path),
)?;
}
_ => write!(f, "{}", name)?,
@ -1270,7 +1313,7 @@ impl clean::Visibility {
debug!("path={:?}", path);
// modified from `resolved_path()` to work with `DefPathData`
let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
let anchor = anchor(vis_did, last_name.as_str(), cx).to_string();
let anchor = anchor(vis_did, last_name, cx).to_string();
let mut s = "pub(in ".to_owned();
for seg in &path.data[..path.data.len() - 1] {

View File

@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use rustc_span::symbol::sym;
use rustc_span::{sym, Symbol};
use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
@ -29,7 +29,7 @@ use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::Buffer;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
use crate::html::{layout, sources};
use crate::scrape_examples::AllCallLocations;
@ -45,7 +45,7 @@ use crate::try_err;
crate struct Context<'tcx> {
/// Current hierarchy of components leading down to what's currently being
/// rendered
pub(crate) current: Vec<String>,
pub(crate) current: Vec<Symbol>,
/// The current destination folder of where HTML artifacts should be placed.
/// This changes as the context descends into the module hierarchy.
crate dst: PathBuf,
@ -176,7 +176,7 @@ impl<'tcx> Context<'tcx> {
title.push_str(" in ");
}
// No need to include the namespace for primitive types and keywords
title.push_str(&self.current.join("::"));
title.push_str(&join_with_double_colon(&self.current));
};
title.push_str(" - Rust");
let tyname = it.type_();
@ -225,18 +225,18 @@ impl<'tcx> Context<'tcx> {
if let Some(&(ref names, ty)) = self.cache().paths.get(&it.def_id.expect_def_id()) {
let mut path = String::new();
for name in &names[..names.len() - 1] {
path.push_str(name);
path.push_str(&name.as_str());
path.push('/');
}
path.push_str(&item_path(ty, names.last().unwrap()));
path.push_str(&item_path(ty, &names.last().unwrap().as_str()));
match self.shared.redirections {
Some(ref redirections) => {
let mut current_path = String::new();
for name in &self.current {
current_path.push_str(name);
current_path.push_str(&name.as_str());
current_path.push('/');
}
current_path.push_str(&item_path(ty, names.last().unwrap()));
current_path.push_str(&item_path(ty, &names.last().unwrap().as_str()));
redirections.borrow_mut().insert(current_path, path);
}
None => return layout::redirect(&format!("{}{}", self.root_path(), path)),
@ -634,8 +634,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
self.render_redirect_pages = item.is_stripped();
}
let scx = &self.shared;
let item_name = item.name.as_ref().unwrap().to_string();
self.dst.push(&item_name);
let item_name = item.name.unwrap();
self.dst.push(&*item_name.as_str());
self.current.push(item_name);
info!("Recursing into {}", self.dst.display());

View File

@ -69,8 +69,9 @@ use crate::formats::item_type::ItemType;
use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
href, print_abi_with_space, print_constness_with_space, print_default_space,
print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace,
href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
print_default_space, print_generic_bounds, print_where_clause, Buffer, HrefError,
PrintWithSpace,
};
use crate::html::highlight;
use crate::html::markdown::{HeadingOffset, Markdown, MarkdownHtml, MarkdownSummaryLine};
@ -2515,7 +2516,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
if let Some(path) = fqp {
out.push(path.join("::"));
out.push(join_with_double_colon(&path));
}
};

View File

@ -26,7 +26,8 @@ use crate::formats::item_type::ItemType;
use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
print_abi_with_space, print_constness_with_space, print_where_clause, Buffer, PrintWithSpace,
join_with_double_colon, join_with_slash, print_abi_with_space, print_constness_with_space,
print_where_clause, Buffer, PrintWithSpace,
};
use crate::html::highlight;
use crate::html::layout::Page;
@ -40,9 +41,9 @@ const ITEM_TABLE_ROW_OPEN: &str = "<div class=\"item-row\">";
const ITEM_TABLE_ROW_CLOSE: &str = "</div>";
// A component in a `use` path, like `string` in std::string::ToString
struct PathComponent<'a> {
struct PathComponent {
path: String,
name: &'a str,
name: Symbol,
}
#[derive(Template)]
@ -53,7 +54,7 @@ struct ItemVars<'a> {
typ: &'a str,
name: &'a str,
item_type: &'a str,
path_components: Vec<PathComponent<'a>>,
path_components: Vec<PathComponent>,
stability_since_raw: &'a str,
src_href: Option<&'a str>,
}
@ -121,7 +122,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer,
.take(amt)
.map(|(i, component)| PathComponent {
path: "../".repeat(cur.len() - i - 1),
name: component,
name: *component,
})
.collect()
};
@ -304,22 +305,18 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
w.write_str(ITEM_TABLE_ROW_OPEN);
match *src {
Some(ref src) => write!(
Some(src) => write!(
w,
"<div class=\"item-left\"><code>{}extern crate {} as {};",
myitem.visibility.print_with_space(myitem.def_id, cx),
anchor(myitem.def_id.expect_def_id(), src.as_str(), cx),
anchor(myitem.def_id.expect_def_id(), src, cx),
myitem.name.as_ref().unwrap(),
),
None => write!(
w,
"<div class=\"item-left\"><code>{}extern crate {};",
myitem.visibility.print_with_space(myitem.def_id, cx),
anchor(
myitem.def_id.expect_def_id(),
myitem.name.as_ref().unwrap().as_str(),
cx
),
anchor(myitem.def_id.expect_def_id(), *myitem.name.as_ref().unwrap(), cx),
),
}
w.write_str("</code></div>");
@ -864,10 +861,10 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
</script>",
root_path = vec![".."; cx.current.len()].join("/"),
path = if it.def_id.is_local() {
cx.current.join("/")
join_with_slash(None, &cx.current)
} else {
let (ref path, _) = cache.external_paths[&it.def_id.expect_def_id()];
path[..path.len() - 1].join("/")
join_with_slash(None, &path[..path.len() - 1])
},
ty = it.type_(),
name = *it.name.as_ref().unwrap()
@ -1410,7 +1407,7 @@ crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering {
}
pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
let mut s = cx.current.join("::");
let mut s = join_with_double_colon(&cx.current);
s.push_str("::");
s.push_str(item.name.unwrap().as_str());
s

View File

@ -10,6 +10,7 @@ use crate::clean;
use crate::clean::types::{FnRetTy, Function, GenericBound, Generics, Type, WherePredicate};
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::format::join_with_double_colon;
use crate::html::markdown::short_markdown_summary;
use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
@ -28,7 +29,7 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
cache.search_index.push(IndexItem {
ty: item.type_(),
name: item.name.unwrap().to_string(),
path: fqp[..fqp.len() - 1].join("::"),
path: join_with_double_colon(&fqp[..fqp.len() - 1]),
desc,
parent: Some(did),
parent_idx: None,
@ -102,7 +103,7 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
paths: Vec<(ItemType, String)>,
paths: Vec<(ItemType, Symbol)>,
// The String is alias name and the vec is the list of the elements with this alias.
//
// To be noted: the `usize` elements are indexes to `items`.
@ -154,7 +155,10 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
"f",
&self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(),
)?;
crate_data.serialize_field("p", &self.paths)?;
crate_data.serialize_field(
"p",
&self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(),
)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}

View File

@ -562,7 +562,7 @@ pub(super) fn write_shared(
let mut mydst = dst.clone();
for part in &remote_path[..remote_path.len() - 1] {
mydst.push(part);
mydst.push(part.to_string());
}
cx.shared.ensure_dir(&mydst)?;
mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1]));

View File

@ -1,44 +1,50 @@
use crate::html::format::href_relative_parts;
use rustc_span::{sym, Symbol};
fn assert_relative_path(expected: &str, relative_to_fqp: &[&str], fqp: &[&str]) {
let relative_to_fqp: Vec<String> = relative_to_fqp.iter().copied().map(String::from).collect();
let fqp: Vec<String> = fqp.iter().copied().map(String::from).collect();
assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).finish());
fn assert_relative_path(expected: &[Symbol], relative_to_fqp: &[Symbol], fqp: &[Symbol]) {
// No `create_default_session_globals_then` call is needed here because all
// the symbols used are static, and no `Symbol::intern` calls occur.
assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).collect::<Vec<_>>());
}
#[test]
fn href_relative_parts_basic() {
let relative_to_fqp = &["std", "vec"];
let fqp = &["std", "iter"];
assert_relative_path("../iter", relative_to_fqp, fqp);
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std, sym::iter];
assert_relative_path(&[sym::dotdot, sym::iter], relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_parent_module() {
let relative_to_fqp = &["std", "vec"];
let fqp = &["std"];
assert_relative_path("..", relative_to_fqp, fqp);
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std];
assert_relative_path(&[sym::dotdot], relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_different_crate() {
let relative_to_fqp = &["std", "vec"];
let fqp = &["core", "iter"];
assert_relative_path("../../core/iter", relative_to_fqp, fqp);
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::core, sym::iter];
assert_relative_path(&[sym::dotdot, sym::dotdot, sym::core, sym::iter], relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_same_module() {
let relative_to_fqp = &["std", "vec"];
let fqp = &["std", "vec"];
assert_relative_path("", relative_to_fqp, fqp);
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std, sym::vec];
assert_relative_path(&[], relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_child_module() {
let relative_to_fqp = &["std"];
let fqp = &["std", "vec"];
assert_relative_path("vec", relative_to_fqp, fqp);
let relative_to_fqp = &[sym::std];
let fqp = &[sym::std, sym::vec];
assert_relative_path(&[sym::vec], relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_root() {
let relative_to_fqp = &[];
let fqp = &["std"];
assert_relative_path("std", relative_to_fqp, fqp);
let fqp = &[sym::std];
assert_relative_path(&[sym::std], relative_to_fqp, fqp);
}

View File

@ -1,3 +1,7 @@
use std::fmt::{self, Write};
use rustc_span::Symbol;
/// A builder that allows efficiently and easily constructing the part of a URL
/// after the domain: `nightly/core/str/struct.Bytes.html`.
///
@ -10,6 +14,7 @@ crate struct UrlPartsBuilder {
impl UrlPartsBuilder {
/// Create an empty buffer.
#[allow(dead_code)]
crate fn new() -> Self {
Self { buf: String::new() }
}
@ -62,6 +67,13 @@ impl UrlPartsBuilder {
self.buf.push_str(part);
}
crate fn push_fmt(&mut self, args: fmt::Arguments<'_>) {
if !self.buf.is_empty() {
self.buf.push('/');
}
self.buf.write_fmt(args).unwrap()
}
/// Push a component onto the front of the buffer.
///
/// # Examples
@ -115,5 +127,26 @@ impl<'a> Extend<&'a str> for UrlPartsBuilder {
}
}
impl FromIterator<Symbol> for UrlPartsBuilder {
fn from_iter<T: IntoIterator<Item = Symbol>>(iter: T) -> Self {
// This code has to be duplicated from the `&str` impl because of
// `Symbol::as_str`'s lifetimes.
let iter = iter.into_iter();
let mut builder = Self::with_capacity_bytes(AVG_PART_LENGTH * iter.size_hint().0);
iter.for_each(|part| builder.push(part.as_str()));
builder
}
}
impl Extend<Symbol> for UrlPartsBuilder {
fn extend<T: IntoIterator<Item = Symbol>>(&mut self, iter: T) {
// This code has to be duplicated from the `&str` impl because of
// `Symbol::as_str`'s lifetimes.
let iter = iter.into_iter();
self.buf.reserve(AVG_PART_LENGTH * iter.size_hint().0);
iter.for_each(|part| self.push(part.as_str()));
}
}
#[cfg(test)]
mod tests;

View File

@ -120,7 +120,7 @@ impl<'tcx> JsonRenderer<'tcx> {
})
.0
.last()
.map(Clone::clone),
.map(|s| s.to_string()),
visibility: types::Visibility::Public,
inner: types::ItemEnum::Trait(trait_item.clone().into_tcx(self.tcx)),
span: None,
@ -230,7 +230,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
from_item_id(k.into()),
types::ItemSummary {
crate_id: k.krate.as_u32(),
path,
path: path.iter().map(|s| s.to_string()).collect(),
kind: kind.into_tcx(self.tcx),
},
)

View File

@ -5,7 +5,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::definitions::DefPathData;
use rustc_hir::Node;
use rustc_hir::CRATE_HIR_ID;
use rustc_middle::middle::privacy::AccessLevel;
@ -43,12 +42,9 @@ impl Module<'_> {
}
// FIXME: Should this be replaced with tcx.def_path_str?
fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<String> {
let crate_name = tcx.crate_name(did.krate).to_string();
let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| {
// Filter out extern blocks
(elem.data != DefPathData::ForeignMod).then(|| elem.data.to_string())
});
fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<Symbol> {
let crate_name = tcx.crate_name(did.krate);
let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| elem.data.get_opt_name());
std::iter::once(crate_name).chain(relative).collect()
}
@ -71,7 +67,7 @@ crate struct RustdocVisitor<'a, 'tcx> {
inlining: bool,
/// Are the current module and all of its parents public?
inside_public_path: bool,
exact_paths: FxHashMap<DefId, Vec<String>>,
exact_paths: FxHashMap<DefId, Vec<Symbol>>,
}
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {