Auto merge of #141573 - nnethercote:rustdoc-alloc-cleanups, r=camelid

rustdoc: cleanups relating to allocations

These commits generally clean up the code a bit and also reduce allocation rates a bit.

r? `@camelid`
This commit is contained in:
bors 2025-05-30 08:55:18 +00:00
commit e6152cdf5b
10 changed files with 140 additions and 146 deletions

View file

@ -107,7 +107,7 @@ impl From<DefId> for ItemId {
}
/// The crate currently being documented.
#[derive(Clone, Debug)]
#[derive(Debug)]
pub(crate) struct Crate {
pub(crate) module: Item,
/// Only here so that they can be filtered through the rustdoc passes.
@ -1655,9 +1655,7 @@ impl Type {
a.def_id() == b.def_id()
&& a.generics()
.zip(b.generics())
.map(|(ag, bg)| {
ag.iter().zip(bg.iter()).all(|(at, bt)| at.is_doc_subtype_of(bt, cache))
})
.map(|(ag, bg)| ag.zip(bg).all(|(at, bt)| at.is_doc_subtype_of(bt, cache)))
.unwrap_or(true)
}
// Other cases, such as primitives, just use recursion.
@ -1730,7 +1728,7 @@ impl Type {
}
}
pub(crate) fn generics(&self) -> Option<Vec<&Type>> {
pub(crate) fn generics<'a>(&'a self) -> Option<impl Iterator<Item = &'a Type>> {
match self {
Type::Path { path, .. } => path.generics(),
_ => None,
@ -2288,17 +2286,13 @@ impl Path {
self.segments.last().map(|seg| &seg.args)
}
pub(crate) fn generics(&self) -> Option<Vec<&Type>> {
pub(crate) fn generics<'a>(&'a self) -> Option<impl Iterator<Item = &'a Type>> {
self.segments.last().and_then(|seg| {
if let GenericArgs::AngleBracketed { ref args, .. } = seg.args {
Some(
args.iter()
.filter_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
})
.collect(),
)
Some(args.iter().filter_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
}))
} else {
None
}

View file

@ -7,13 +7,12 @@
//! some of them support an alternate format that emits text, but that should
//! not be used external to this module.
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt::{self, Display, Write};
use std::iter::{self, once};
use std::slice;
use itertools::Either;
use itertools::{Either, Itertools};
use rustc_abi::ExternAbi;
use rustc_attr_data_structures::{ConstStability, StabilityLevel, StableSince};
use rustc_data_structures::fx::FxHashSet;
@ -483,12 +482,12 @@ fn generate_item_def_id_path(
let mut is_remote = false;
let url_parts = url_parts(cx.cache(), def_id, module_fqp, &cx.current, &mut is_remote)?;
let (url_parts, shortty, fqp) = make_href(root_path, shortty, url_parts, &fqp, is_remote)?;
if def_id == original_def_id {
return Ok((url_parts, shortty, fqp));
}
let kind = ItemType::from_def_kind(original_def_kind, Some(def_kind));
Ok((format!("{url_parts}#{kind}.{}", tcx.item_name(original_def_id)), shortty, fqp))
let mut url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote);
if def_id != original_def_id {
let kind = ItemType::from_def_kind(original_def_kind, Some(def_kind));
url_parts = format!("{url_parts}#{kind}.{}", tcx.item_name(original_def_id))
};
Ok((url_parts, shortty, fqp))
}
fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
@ -510,7 +509,7 @@ fn url_parts(
builder.extend(module_fqp.iter().copied());
Ok(builder)
}
ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to).collect()),
ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to)),
ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt),
}
}
@ -521,7 +520,7 @@ fn make_href(
mut url_parts: UrlPartsBuilder,
fqp: &[Symbol],
is_remote: bool,
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
) -> String {
if !is_remote && let Some(root_path) = root_path {
let root = root_path.trim_end_matches('/');
url_parts.push_front(root);
@ -536,7 +535,7 @@ fn make_href(
url_parts.push_fmt(format_args!("{shortty}.{last}.html"));
}
}
Ok((url_parts.finish(), shortty, fqp.to_vec()))
url_parts.finish()
}
pub(crate) fn href_with_root_path(
@ -587,7 +586,7 @@ pub(crate) fn href_with_root_path(
Some(&(ref fqp, shortty)) => (fqp, shortty, {
let module_fqp = to_module_fqp(shortty, fqp.as_slice());
debug!(?fqp, ?shortty, ?module_fqp);
href_relative_parts(module_fqp, relative_to).collect()
href_relative_parts(module_fqp, relative_to)
}),
None => {
// Associated items are handled differently with "jump to def". The anchor is generated
@ -606,7 +605,8 @@ pub(crate) fn href_with_root_path(
}
}
};
make_href(root_path, shortty, url_parts, fqp, is_remote)
let url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote);
Ok((url_parts, shortty, fqp.clone()))
}
pub(crate) fn href(
@ -619,34 +619,30 @@ pub(crate) fn href(
/// 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.
pub(crate) fn href_relative_parts<'fqp>(
fqp: &'fqp [Symbol],
relative_to_fqp: &[Symbol],
) -> Box<dyn Iterator<Item = Symbol> + 'fqp> {
pub(crate) fn href_relative_parts(fqp: &[Symbol], relative_to_fqp: &[Symbol]) -> UrlPartsBuilder {
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..];
return Box::new(
iter::repeat_n(sym::dotdot, dissimilar_part_count)
.chain(fqp_module.iter().copied()),
);
return iter::repeat_n(sym::dotdot, dissimilar_part_count)
.chain(fqp_module.iter().copied())
.collect();
}
}
match relative_to_fqp.len().cmp(&fqp.len()) {
Ordering::Less => {
// e.g. linking to std::sync::atomic from std::sync
Box::new(fqp[relative_to_fqp.len()..fqp.len()].iter().copied())
fqp[relative_to_fqp.len()..fqp.len()].iter().copied().collect()
}
Ordering::Greater => {
// e.g. linking to std::sync from std::sync::atomic
let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
Box::new(iter::repeat_n(sym::dotdot, dissimilar_part_count))
iter::repeat_n(sym::dotdot, dissimilar_part_count).collect()
}
Ordering::Equal => {
// linking to the same module
Box::new(iter::empty())
UrlPartsBuilder::new()
}
}
}
@ -708,13 +704,13 @@ fn resolved_path(
f,
"{path}::{anchor}",
path = join_with_double_colon(&fqp[..fqp.len() - 1]),
anchor = anchor(did, *fqp.last().unwrap(), cx)
anchor = print_anchor(did, *fqp.last().unwrap(), cx)
)
} else {
write!(f, "{}", last.name)
}
} else {
write!(f, "{}", anchor(did, last.name, cx))
write!(f, "{}", print_anchor(did, last.name, cx))
}
});
write!(w, "{path}{args}", args = last.args.print(cx))?;
@ -800,7 +796,7 @@ fn primitive_link_fragment(
Ok(())
}
fn tybounds(
fn print_tybounds(
bounds: &[clean::PolyTrait],
lt: &Option<clean::Lifetime>,
cx: &Context<'_>,
@ -832,7 +828,7 @@ fn print_higher_ranked_params_with_space(
})
}
pub(crate) fn anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display {
pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| {
let parts = href(did, cx);
if let Ok((url, short_ty, fqp)) = parts {
@ -866,7 +862,7 @@ fn fmt_type(
}
clean::DynTrait(bounds, lt) => {
f.write_str("dyn ")?;
tybounds(bounds, lt, cx).fmt(f)
print_tybounds(bounds, lt, cx).fmt(f)
}
clean::Infer => write!(f, "_"),
clean::Primitive(clean::PrimitiveType::Never) => {
@ -1122,8 +1118,8 @@ impl clean::Impl {
write!(f, "!")?;
}
if self.kind.is_fake_variadic()
&& let generics = ty.generics()
&& let &[inner_type] = generics.as_ref().map_or(&[][..], |v| &v[..])
&& let Some(generics) = ty.generics()
&& let Ok(inner_type) = generics.exactly_one()
{
let last = ty.last();
if f.alternate() {
@ -1131,7 +1127,7 @@ impl clean::Impl {
self.print_type(inner_type, f, use_absolute, cx)?;
write!(f, ">")?;
} else {
write!(f, "{}&lt;", anchor(ty.def_id(), last, cx))?;
write!(f, "{}&lt;", print_anchor(ty.def_id(), last, cx))?;
self.print_type(inner_type, f, use_absolute, cx)?;
write!(f, "&gt;")?;
}
@ -1202,11 +1198,10 @@ impl clean::Impl {
}
} else if let clean::Type::Path { path } = type_
&& let Some(generics) = path.generics()
&& generics.len() == 1
&& let Ok(ty) = generics.exactly_one()
&& self.kind.is_fake_variadic()
{
let ty = generics[0];
let wrapper = anchor(path.def_id(), path.last(), cx);
let wrapper = print_anchor(path.def_id(), path.last(), cx);
if f.alternate() {
write!(f, "{wrapper:#}&lt;")?;
} else {
@ -1394,50 +1389,47 @@ impl clean::FnDecl {
}
pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display {
use std::fmt::Write as _;
let vis: Cow<'static, str> = match item.visibility(cx.tcx()) {
None => "".into(),
Some(ty::Visibility::Public) => "pub ".into(),
Some(ty::Visibility::Restricted(vis_did)) => {
// FIXME(camelid): This may not work correctly if `item_did` is a module.
// However, rustdoc currently never displays a module's
// visibility, so it shouldn't matter.
let parent_module = find_nearest_parent_module(cx.tcx(), item.item_id.expect_def_id());
if vis_did.is_crate_root() {
"pub(crate) ".into()
} else if parent_module == Some(vis_did) {
// `pub(in foo)` where `foo` is the parent module
// is the same as no visibility modifier
"".into()
} else if parent_module.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
== Some(vis_did)
{
"pub(super) ".into()
} else {
let path = cx.tcx().def_path(vis_did);
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, cx);
let mut s = "pub(in ".to_owned();
for seg in &path.data[..path.data.len() - 1] {
let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
}
let _ = write!(s, "{anchor}) ");
s.into()
}
}
};
let is_doc_hidden = item.is_doc_hidden();
fmt::from_fn(move |f| {
if is_doc_hidden {
if item.is_doc_hidden() {
f.write_str("#[doc(hidden)] ")?;
}
f.write_str(&vis)
match item.visibility(cx.tcx()) {
None => {}
Some(ty::Visibility::Public) => f.write_str("pub ")?,
Some(ty::Visibility::Restricted(vis_did)) => {
// FIXME(camelid): This may not work correctly if `item_did` is a module.
// However, rustdoc currently never displays a module's
// visibility, so it shouldn't matter.
let parent_module =
find_nearest_parent_module(cx.tcx(), item.item_id.expect_def_id());
if vis_did.is_crate_root() {
f.write_str("pub(crate) ")?;
} else if parent_module == Some(vis_did) {
// `pub(in foo)` where `foo` is the parent module
// is the same as no visibility modifier; do nothing
} else if parent_module
.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
== Some(vis_did)
{
f.write_str("pub(super) ")?;
} else {
let path = cx.tcx().def_path(vis_did);
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 = print_anchor(vis_did, last_name, cx);
f.write_str("pub(in ")?;
for seg in &path.data[..path.data.len() - 1] {
write!(f, "{}::", seg.data.get_opt_name().unwrap())?;
}
write!(f, "{anchor}) ")?;
}
}
}
Ok(())
})
}

View file

@ -8,7 +8,6 @@ use super::static_files::{STATIC_FILES, StaticFiles};
use crate::externalfiles::ExternalHtml;
use crate::html::render::{StylePath, ensure_trailing_slash};
#[derive(Clone)]
pub(crate) struct Layout {
pub(crate) logo: String,
pub(crate) favicon: String,

View file

@ -195,7 +195,7 @@ fn slugify(c: char) -> Option<char> {
}
}
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Playground {
pub crate_name: Option<Symbol>,
pub url: String,

View file

@ -14,7 +14,7 @@ use rustc_span::edition::Edition;
use rustc_span::{FileName, Symbol, sym};
use tracing::info;
use super::print_item::{full_path, item_path, print_item};
use super::print_item::{full_path, print_item, print_item_path};
use super::sidebar::{ModuleLike, Sidebar, print_sidebar, sidebar_module_like};
use super::{AllTypes, LinkFromSrc, StylePath, collect_spans_and_sources, scrape_examples_help};
use crate::clean::types::ExternalLocation;
@ -266,7 +266,7 @@ impl<'tcx> Context<'tcx> {
for name in &names[..names.len() - 1] {
write!(f, "{name}/")?;
}
write!(f, "{}", item_path(ty, names.last().unwrap().as_str()))
write!(f, "{}", print_item_path(ty, names.last().unwrap().as_str()))
});
match self.shared.redirections {
Some(ref redirections) => {
@ -278,7 +278,7 @@ impl<'tcx> Context<'tcx> {
let _ = write!(
current_path,
"{}",
item_path(ty, names.last().unwrap().as_str())
print_item_path(ty, names.last().unwrap().as_str())
);
redirections.borrow_mut().insert(current_path, path.to_string());
}
@ -847,7 +847,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
if !buf.is_empty() {
let name = item.name.as_ref().unwrap();
let item_type = item.type_();
let file_name = item_path(item_type, name.as_str()).to_string();
let file_name = print_item_path(item_type, name.as_str()).to_string();
self.shared.ensure_dir(&self.dst)?;
let joint_dst = self.dst.join(&file_name);
self.shared.fs.write(joint_dst, buf)?;

View file

@ -2548,7 +2548,7 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
/// types are re-exported, we don't use the corresponding
/// entry from the js file, as inlining will have already
/// picked up the impl
fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String> {
let mut out = Vec::new();
let mut visited = FxHashSet::default();
let mut work = VecDeque::new();
@ -2565,7 +2565,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
work.push_back(first_ty);
while let Some(ty) = work.pop_front() {
if !visited.insert(ty.clone()) {
if !visited.insert(ty) {
continue;
}
@ -2575,16 +2575,16 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
work.extend(tys.into_iter());
}
clean::Type::Slice(ty) => {
work.push_back(*ty);
work.push_back(ty);
}
clean::Type::Array(ty, _) => {
work.push_back(*ty);
work.push_back(ty);
}
clean::Type::RawPointer(_, ty) => {
work.push_back(*ty);
work.push_back(ty);
}
clean::Type::BorrowedRef { type_, .. } => {
work.push_back(*type_);
work.push_back(type_);
}
clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
work.push_back(self_type);

View file

@ -413,7 +413,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
match myitem.kind {
clean::ExternCrateItem { ref src } => {
use crate::html::format::anchor;
use crate::html::format::print_anchor;
match *src {
Some(src) => {
@ -421,7 +421,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
w,
"<dt><code>{}extern crate {} as {};",
visibility_print_with_space(myitem, cx),
anchor(myitem.item_id.expect_def_id(), src, cx),
print_anchor(myitem.item_id.expect_def_id(), src, cx),
EscapeBodyTextWithWbr(myitem.name.unwrap().as_str())
)?;
}
@ -430,7 +430,11 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
w,
"<dt><code>{}extern crate {};",
visibility_print_with_space(myitem, cx),
anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx)
print_anchor(
myitem.item_id.expect_def_id(),
myitem.name.unwrap(),
cx
)
)?;
}
}
@ -439,7 +443,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
clean::ImportItem(ref import) => {
let stab_tags = import.source.did.map_or_else(String::new, |import_def_id| {
extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
print_extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
});
let id = match import.kind {
@ -497,7 +501,9 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
write!(
w,
"<dt>\
<a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
<a class=\"{class}\" href=\"{href}\" title=\"{title1} {title2}\">\
{name}\
</a>\
{visibility_and_hidden}\
{unsafety_flag}\
{stab_tags}\
@ -505,11 +511,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
{docs_before}{docs}{docs_after}",
name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
visibility_and_hidden = visibility_and_hidden,
stab_tags = extra_info_tags(tcx, myitem, item, None),
stab_tags = print_extra_info_tags(tcx, myitem, item, None),
class = myitem.type_(),
unsafety_flag = unsafety_flag,
href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
title = format_args!("{} {}", myitem.type_(), full_path(cx, myitem)),
href = print_item_path(myitem.type_(), myitem.name.unwrap().as_str()),
title1 = myitem.type_(),
title2 = full_path(cx, myitem),
)?;
}
}
@ -524,7 +531,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
/// Render the stability, deprecation and portability tags that are displayed in the item's summary
/// at the module level.
fn extra_info_tags(
fn print_extra_info_tags(
tcx: TyCtxt<'_>,
item: &clean::Item,
parent: &clean::Item,
@ -639,7 +646,7 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp
fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
fmt::from_fn(|w| {
let tcx = cx.tcx();
let bounds = bounds(&t.bounds, false, cx);
let bounds = print_bounds(&t.bounds, false, cx);
let required_types =
t.items.iter().filter(|m| m.is_required_associated_type()).collect::<Vec<_>>();
let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
@ -652,7 +659,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
let count_types = required_types.len() + provided_types.len();
let count_consts = required_consts.len() + provided_consts.len();
let count_methods = required_methods.len() + provided_methods.len();
let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone();
let must_implement_one_of_functions = &tcx.trait_def(t.def_id).must_implement_one_of;
// Output the trait definition
wrap_item(w, |mut w| {
@ -1088,7 +1095,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
it,
&implementor_dups,
&collect_paths_for_type(
implementor.inner_impl().for_.clone(),
&implementor.inner_impl().for_,
&cx.shared.cache,
),
)
@ -1236,7 +1243,7 @@ fn item_trait_alias(
attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
bounds = bounds(&t.bounds, true, cx),
bounds = print_bounds(&t.bounds, true, cx),
where_clause =
print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(),
)
@ -2254,14 +2261,18 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
s
}
pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display {
pub(super) fn print_item_path(ty: ItemType, name: &str) -> impl Display {
fmt::from_fn(move |f| match ty {
ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
_ => write!(f, "{ty}.{name}.html"),
})
}
fn bounds(bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> impl Display {
fn print_bounds(
bounds: &[clean::GenericBound],
trait_alias: bool,
cx: &Context<'_>,
) -> impl Display {
(!bounds.is_empty())
.then_some(fmt::from_fn(move |f| {
let has_lots_of_bounds = bounds.len() > 2;

View file

@ -607,16 +607,9 @@ impl TypeAliasPart {
let cx = type_impl_collector.cx;
let aliased_types = type_impl_collector.aliased_types;
for aliased_type in aliased_types.values() {
let impls = aliased_type
.impl_
.values()
.flat_map(|AliasedTypeImpl { impl_, type_aliases }| {
let mut ret: Vec<AliasSerializableImpl> = Vec::new();
let trait_ = impl_
.inner_impl()
.trait_
.as_ref()
.map(|trait_| format!("{:#}", trait_.print(cx)));
let impls = aliased_type.impl_.values().filter_map(
|AliasedTypeImpl { impl_, type_aliases }| {
let mut ret: Option<AliasSerializableImpl> = None;
// render_impl will filter out "impossible-to-call" methods
// to make that functionality work here, it needs to be called with
// each type alias, and if it gives a different result, split the impl
@ -624,8 +617,8 @@ impl TypeAliasPart {
cx.id_map.borrow_mut().clear();
cx.deref_id_map.borrow_mut().clear();
let type_alias_fqp = (*type_alias_fqp).iter().join("::");
if let Some(last) = ret.last_mut() {
last.aliases.push(type_alias_fqp);
if let Some(ret) = &mut ret {
ret.aliases.push(type_alias_fqp);
} else {
let target_did = impl_
.inner_impl()
@ -660,16 +653,22 @@ impl TypeAliasPart {
},
)
.to_string();
ret.push(AliasSerializableImpl {
// The alternate display prints it as plaintext instead of HTML.
let trait_ = impl_
.inner_impl()
.trait_
.as_ref()
.map(|trait_| format!("{:#}", trait_.print(cx)));
ret = Some(AliasSerializableImpl {
text,
trait_: trait_.clone(),
trait_,
aliases: vec![type_alias_fqp],
})
}
}
ret
})
.collect::<Vec<_>>();
},
);
let mut path = PathBuf::from("type.impl");
for component in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
@ -682,7 +681,7 @@ impl TypeAliasPart {
));
let part = OrderedJson::array_sorted(
impls.iter().map(OrderedJson::serialize).collect::<Result<Vec<_>, _>>().unwrap(),
impls.map(|impl_| OrderedJson::serialize(impl_).unwrap()),
);
path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
}
@ -760,7 +759,7 @@ impl TraitAliasPart {
Some(Implementor {
text: imp.inner_impl().print(false, cx).to_string(),
synthetic: imp.inner_impl().kind.is_auto(),
types: collect_paths_for_type(imp.inner_impl().for_.clone(), cache),
types: collect_paths_for_type(&imp.inner_impl().for_, cache),
})
}
})

View file

@ -1,51 +1,51 @@
use rustc_span::{Symbol, sym};
use rustc_span::{Symbol, create_default_session_globals_then, sym};
use crate::html::format::href_relative_parts;
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<_>>());
fn assert_relative_path(expected: &str, relative_to_fqp: &[Symbol], fqp: &[Symbol]) {
create_default_session_globals_then(|| {
assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).finish());
});
}
#[test]
fn href_relative_parts_basic() {
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);
assert_relative_path("../iter", relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_parent_module() {
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std];
assert_relative_path(&[sym::dotdot], relative_to_fqp, fqp);
assert_relative_path("..", relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_different_crate() {
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);
assert_relative_path("../../core/iter", relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_same_module() {
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std, sym::vec];
assert_relative_path(&[], relative_to_fqp, fqp);
assert_relative_path("", relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_child_module() {
let relative_to_fqp = &[sym::std];
let fqp = &[sym::std, sym::vec];
assert_relative_path(&[sym::vec], relative_to_fqp, fqp);
assert_relative_path("vec", relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_root() {
let relative_to_fqp = &[];
let fqp = &[sym::std];
assert_relative_path(&[sym::std], relative_to_fqp, fqp);
assert_relative_path("std", relative_to_fqp, fqp);
}

View file

@ -14,7 +14,6 @@ pub(crate) struct UrlPartsBuilder {
impl UrlPartsBuilder {
/// Create an empty buffer.
#[allow(dead_code)]
pub(crate) fn new() -> Self {
Self { buf: String::new() }
}