Skip to content

Commit f5cefbb

Browse files
committed
Auto merge of #136244 - yotamofek:pr/rustdoc-join-iter, r=<try>
[WIP: perf] attempt to prevent regressions in that originally happened in #135494 This implements something similar to [`Itertools::format`](https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.format), but on `Fn`s returning iterators instead of directly on iterators, to allow implementing `Display` without the use of a `Cell` (to handle the possibility of `fmt` being called multiple times while receiving `&self`). This is WIP, I just want to get a perf run first to see if the regression I saw in #135494 is fixed
2 parents 61cc3e5 + 66e29e0 commit f5cefbb

File tree

5 files changed

+136
-143
lines changed

5 files changed

+136
-143
lines changed

src/librustdoc/clean/cfg.rs

+38-19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_span::Span;
1414
use rustc_span::symbol::{Symbol, sym};
1515

1616
use crate::html::escape::Escape;
17+
use crate::join::Join as _;
1718

1819
#[cfg(test)]
1920
mod tests;
@@ -396,6 +397,8 @@ impl Display<'_> {
396397
sub_cfgs: &[Cfg],
397398
separator: &str,
398399
) -> fmt::Result {
400+
use fmt::Display as _;
401+
399402
let short_longhand = self.1.is_long() && {
400403
let all_crate_features =
401404
sub_cfgs.iter().all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
@@ -414,20 +417,27 @@ impl Display<'_> {
414417
}
415418
};
416419

417-
for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
418-
if i != 0 {
419-
fmt.write_str(separator)?;
420-
}
421-
if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
422-
if self.1.is_html() {
423-
write!(fmt, "<code>{feat}</code>")?;
424-
} else {
425-
write!(fmt, "`{feat}`")?;
426-
}
427-
} else {
428-
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
429-
}
430-
}
420+
(|| {
421+
sub_cfgs.iter().map(|sub_cfg| {
422+
fmt::from_fn(move |fmt| {
423+
if let Cfg::Cfg(_, Some(feat)) = sub_cfg
424+
&& short_longhand
425+
{
426+
if self.1.is_html() {
427+
write!(fmt, "<code>{feat}</code>")?;
428+
} else {
429+
write!(fmt, "`{feat}`")?;
430+
}
431+
} else {
432+
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
433+
}
434+
Ok(())
435+
})
436+
})
437+
})
438+
.join(separator)
439+
.fmt(fmt)?;
440+
431441
Ok(())
432442
}
433443
}
@@ -439,11 +449,20 @@ impl fmt::Display for Display<'_> {
439449
Cfg::Any(ref sub_cfgs) => {
440450
let separator =
441451
if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " };
442-
for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
443-
fmt.write_str(if i == 0 { "neither " } else { separator })?;
444-
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
445-
}
446-
Ok(())
452+
fmt.write_str("neither ")?;
453+
(|| {
454+
sub_cfgs.iter().map(|sub_cfg| {
455+
fmt::from_fn(|fmt| {
456+
write_with_opt_paren(
457+
fmt,
458+
!sub_cfg.is_all(),
459+
Display(sub_cfg, self.1),
460+
)
461+
})
462+
})
463+
})
464+
.join(separator)
465+
.fmt(fmt)
447466
}
448467
ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)),
449468
ref c => write!(fmt, "not ({})", Display(c, self.1)),

src/librustdoc/html/format.rs

+43-93
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@
88
//! not be used external to this module.
99
1010
use std::borrow::Cow;
11-
use std::cell::Cell;
1211
use std::cmp::Ordering;
1312
use std::fmt::{self, Display, Write};
1413
use std::iter::{self, once};
1514

16-
use itertools::Itertools;
1715
use rustc_abi::ExternAbi;
1816
use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince};
1917
use rustc_data_structures::captures::Captures;
@@ -35,6 +33,7 @@ use crate::formats::cache::Cache;
3533
use crate::formats::item_type::ItemType;
3634
use crate::html::escape::{Escape, EscapeBodyText};
3735
use crate::html::render::Context;
36+
use crate::join::Join as _;
3837
use crate::passes::collect_intra_doc_links::UrlFragment;
3938

4039
pub(crate) trait Print {
@@ -146,36 +145,18 @@ impl Buffer {
146145
}
147146
}
148147

149-
pub(crate) fn comma_sep<T: Display>(
150-
items: impl Iterator<Item = T>,
151-
space_after_comma: bool,
152-
) -> impl Display {
153-
let items = Cell::new(Some(items));
154-
fmt::from_fn(move |f| {
155-
for (i, item) in items.take().unwrap().enumerate() {
156-
if i != 0 {
157-
write!(f, ",{}", if space_after_comma { " " } else { "" })?;
158-
}
159-
item.fmt(f)?;
160-
}
161-
Ok(())
162-
})
163-
}
164-
165148
pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
166149
bounds: &'a [clean::GenericBound],
167150
cx: &'a Context<'tcx>,
168151
) -> impl Display + 'a + Captures<'tcx> {
169152
fmt::from_fn(move |f| {
170-
let mut bounds_dup = FxHashSet::default();
153+
(|| {
154+
let mut bounds_dup = FxHashSet::default();
171155

172-
for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(*b)).enumerate() {
173-
if i > 0 {
174-
f.write_str(" + ")?;
175-
}
176-
bound.print(cx).fmt(f)?;
177-
}
178-
Ok(())
156+
bounds.iter().filter(move |b| bounds_dup.insert(*b)).map(|bound| bound.print(cx))
157+
})
158+
.join(" + ")
159+
.fmt(f)
179160
})
180161
}
181162

@@ -190,12 +171,7 @@ impl clean::GenericParamDef {
190171

191172
if !outlives.is_empty() {
192173
f.write_str(": ")?;
193-
for (i, lt) in outlives.iter().enumerate() {
194-
if i != 0 {
195-
f.write_str(" + ")?;
196-
}
197-
write!(f, "{}", lt.print())?;
198-
}
174+
write!(f, "{}", (|| outlives.iter().map(|lt| lt.print())).join(" + "))?;
199175
}
200176

201177
Ok(())
@@ -245,10 +221,11 @@ impl clean::Generics {
245221
return Ok(());
246222
}
247223

224+
let real_params = (|| real_params.clone().map(|g| g.print(cx))).join(", ");
248225
if f.alternate() {
249-
write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true))
226+
write!(f, "<{:#}>", real_params)
250227
} else {
251-
write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx)), true))
228+
write!(f, "&lt;{}&gt;", real_params)
252229
}
253230
})
254231
}
@@ -270,10 +247,12 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
270247
ending: Ending,
271248
) -> impl Display + 'a + Captures<'tcx> {
272249
fmt::from_fn(move |f| {
273-
let mut where_predicates = gens
274-
.where_predicates
275-
.iter()
276-
.map(|pred| {
250+
if gens.where_predicates.is_empty() {
251+
return Ok(());
252+
}
253+
254+
let where_predicates = || {
255+
gens.where_predicates.iter().map(|pred| {
277256
fmt::from_fn(move |f| {
278257
if f.alternate() {
279258
f.write_str(" ")?;
@@ -312,13 +291,9 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
312291
}
313292
})
314293
})
315-
.peekable();
316-
317-
if where_predicates.peek().is_none() {
318-
return Ok(());
319-
}
294+
};
320295

321-
let where_preds = comma_sep(where_predicates, false);
296+
let where_preds = where_predicates.join(",");
322297
let clause = if f.alternate() {
323298
if ending == Ending::Newline {
324299
format!(" where{where_preds},")
@@ -415,12 +390,7 @@ impl clean::GenericBound {
415390
} else {
416391
f.write_str("use&lt;")?;
417392
}
418-
for (i, arg) in args.iter().enumerate() {
419-
if i > 0 {
420-
write!(f, ", ")?;
421-
}
422-
arg.fmt(f)?;
423-
}
393+
(|| args.iter()).join(", ").fmt(f)?;
424394
if f.alternate() { f.write_str(">") } else { f.write_str("&gt;") }
425395
}
426396
})
@@ -524,11 +494,7 @@ pub(crate) enum HrefError {
524494
// Panics if `syms` is empty.
525495
pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
526496
let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len()));
527-
s.push_str(syms[0].as_str());
528-
for sym in &syms[1..] {
529-
s.push_str("::");
530-
s.push_str(sym.as_str());
531-
}
497+
write!(s, "{}", (|| syms.iter()).join("::")).unwrap();
532498
s
533499
}
534500

@@ -572,20 +538,20 @@ fn generate_macro_def_id_path(
572538
}
573539

574540
if let Some(last) = path.last_mut() {
575-
*last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
541+
*last = Symbol::intern(&format!("macro.{last}.html"));
576542
}
577543

578544
let url = match cache.extern_locations[&def_id.krate] {
579545
ExternalLocation::Remote(ref s) => {
580546
// `ExternalLocation::Remote` always end with a `/`.
581-
format!("{s}{path}", path = path.iter().map(|p| p.as_str()).join("/"))
547+
format!("{s}{path}", path = (|| path.iter()).join("/"))
582548
}
583549
ExternalLocation::Local => {
584550
// `root_path` always end with a `/`.
585551
format!(
586552
"{root_path}{path}",
587553
root_path = root_path.unwrap_or(""),
588-
path = path.iter().map(|p| p.as_str()).join("/")
554+
path = (|| path.iter()).join("/")
589555
)
590556
}
591557
ExternalLocation::Unknown => {
@@ -682,9 +648,8 @@ fn make_href(
682648
url_parts.push("index.html");
683649
}
684650
_ => {
685-
let prefix = shortty.as_str();
686651
let last = fqp.last().unwrap();
687-
url_parts.push_fmt(format_args!("{prefix}.{last}.html"));
652+
url_parts.push_fmt(format_args!("{shortty}.{last}.html"));
688653
}
689654
}
690655
Ok((url_parts.finish(), shortty, fqp.to_vec()))
@@ -950,12 +915,7 @@ fn tybounds<'a, 'tcx: 'a>(
950915
cx: &'a Context<'tcx>,
951916
) -> impl Display + 'a + Captures<'tcx> {
952917
fmt::from_fn(move |f| {
953-
for (i, bound) in bounds.iter().enumerate() {
954-
if i > 0 {
955-
write!(f, " + ")?;
956-
}
957-
bound.print(cx).fmt(f)?;
958-
}
918+
(|| bounds.iter().map(|bound| bound.print(cx))).join(" + ").fmt(f)?;
959919
if let Some(lt) = lt {
960920
// We don't need to check `alternate` since we can be certain that
961921
// the lifetime doesn't contain any characters which need escaping.
@@ -974,7 +934,7 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>(
974934
if !params.is_empty() {
975935
f.write_str(keyword)?;
976936
f.write_str(if f.alternate() { "<" } else { "&lt;" })?;
977-
comma_sep(params.iter().map(|lt| lt.print(cx)), true).fmt(f)?;
937+
(|| params.iter().map(|lt| lt.print(cx))).join(", ").fmt(f)?;
978938
f.write_str(if f.alternate() { "> " } else { "&gt; " })?;
979939
}
980940
Ok(())
@@ -1025,9 +985,7 @@ fn fmt_type(
1025985
clean::Primitive(clean::PrimitiveType::Never) => {
1026986
primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
1027987
}
1028-
clean::Primitive(prim) => {
1029-
primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
1030-
}
988+
clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx),
1031989
clean::BareFunction(ref decl) => {
1032990
print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?;
1033991
decl.safety.print_with_space().fmt(f)?;
@@ -1067,18 +1025,13 @@ fn fmt_type(
10671025
primitive_link(
10681026
f,
10691027
PrimitiveType::Tuple,
1070-
format_args!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
1028+
format_args!("({})", (|| generic_names.iter()).join(", ")),
10711029
cx,
10721030
)
10731031
} else {
1074-
write!(f, "(")?;
1075-
for (i, item) in many.iter().enumerate() {
1076-
if i != 0 {
1077-
write!(f, ", ")?;
1078-
}
1079-
item.print(cx).fmt(f)?;
1080-
}
1081-
write!(f, ")")
1032+
f.write_str("(")?;
1033+
(|| many.iter().map(|item| item.print(cx))).join(", ").fmt(f)?;
1034+
f.write_str(")")
10821035
}
10831036
}
10841037
},
@@ -1407,14 +1360,16 @@ impl clean::Arguments {
14071360
cx: &'a Context<'tcx>,
14081361
) -> impl Display + 'a + Captures<'tcx> {
14091362
fmt::from_fn(move |f| {
1410-
for (i, input) in self.values.iter().enumerate() {
1411-
write!(f, "{}: ", input.name)?;
1412-
input.type_.print(cx).fmt(f)?;
1413-
if i + 1 < self.values.len() {
1414-
write!(f, ", ")?;
1415-
}
1416-
}
1417-
Ok(())
1363+
(|| {
1364+
self.values.iter().map(|input| {
1365+
fmt::from_fn(|f| {
1366+
write!(f, "{}: ", input.name)?;
1367+
input.type_.print(cx).fmt(f)
1368+
})
1369+
})
1370+
})
1371+
.join(", ")
1372+
.fmt(f)
14181373
})
14191374
}
14201375
}
@@ -1723,12 +1678,7 @@ impl clean::ImportSource {
17231678
}
17241679
let name = self.path.last();
17251680
if let hir::def::Res::PrimTy(p) = self.path.res {
1726-
primitive_link(
1727-
f,
1728-
PrimitiveType::from(p),
1729-
format_args!("{}", name.as_str()),
1730-
cx,
1731-
)?;
1681+
primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?;
17321682
} else {
17331683
f.write_str(name.as_str())?;
17341684
}

0 commit comments

Comments
 (0)