Skip to content

Commit e38cd65

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 0cc4f4f + 63abcb0 commit e38cd65

File tree

5 files changed

+138
-146
lines changed

5 files changed

+138
-146
lines changed

src/librustdoc/clean/cfg.rs

+40-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::Joined 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,29 @@ 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+
fmt::from_fn(|f| {
421+
sub_cfgs
422+
.iter()
423+
.map(|sub_cfg| {
424+
fmt::from_fn(move |fmt| {
425+
if let Cfg::Cfg(_, Some(feat)) = sub_cfg
426+
&& short_longhand
427+
{
428+
if self.1.is_html() {
429+
write!(fmt, "<code>{feat}</code>")?;
430+
} else {
431+
write!(fmt, "`{feat}`")?;
432+
}
433+
} else {
434+
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
435+
}
436+
Ok(())
437+
})
438+
})
439+
.joined(separator, f)
440+
})
441+
.fmt(fmt)?;
442+
431443
Ok(())
432444
}
433445
}
@@ -439,11 +451,20 @@ impl fmt::Display for Display<'_> {
439451
Cfg::Any(ref sub_cfgs) => {
440452
let separator =
441453
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(())
454+
fmt.write_str("neither ")?;
455+
456+
sub_cfgs
457+
.iter()
458+
.map(|sub_cfg| {
459+
fmt::from_fn(|fmt| {
460+
write_with_opt_paren(
461+
fmt,
462+
!sub_cfg.is_all(),
463+
Display(sub_cfg, self.1),
464+
)
465+
})
466+
})
467+
.joined(separator, fmt)
447468
}
448469
ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)),
449470
ref c => write!(fmt, "not ({})", Display(c, self.1)),

src/librustdoc/html/format.rs

+45-92
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::Joined 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| {
170153
let mut bounds_dup = FxHashSet::default();
171154

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(())
155+
bounds
156+
.iter()
157+
.filter(move |b| bounds_dup.insert(*b))
158+
.map(|bound| bound.print(cx))
159+
.joined(" + ", 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+
outlives.iter().map(|lt| lt.print()).joined(" + ", f)?; // FIXME: keep fmt options?
199175
}
200176

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

224+
let real_params =
225+
fmt::from_fn(|f| real_params.clone().map(|g| g.print(cx)).joined(", ", f));
248226
if f.alternate() {
249-
write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true))
227+
write!(f, "<{:#}>", real_params)
250228
} else {
251-
write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx)), true))
229+
write!(f, "&lt;{}&gt;", real_params)
252230
}
253231
})
254232
}
@@ -270,10 +248,12 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
270248
ending: Ending,
271249
) -> impl Display + 'a + Captures<'tcx> {
272250
fmt::from_fn(move |f| {
273-
let mut where_predicates = gens
274-
.where_predicates
275-
.iter()
276-
.map(|pred| {
251+
if gens.where_predicates.is_empty() {
252+
return Ok(());
253+
}
254+
255+
let where_predicates = || {
256+
gens.where_predicates.iter().map(|pred| {
277257
fmt::from_fn(move |f| {
278258
if f.alternate() {
279259
f.write_str(" ")?;
@@ -312,13 +292,9 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
312292
}
313293
})
314294
})
315-
.peekable();
316-
317-
if where_predicates.peek().is_none() {
318-
return Ok(());
319-
}
295+
};
320296

321-
let where_preds = comma_sep(where_predicates, false);
297+
let where_preds = fmt::from_fn(|f| where_predicates().joined(",", f));
322298
let clause = if f.alternate() {
323299
if ending == Ending::Newline {
324300
format!(" where{where_preds},")
@@ -415,12 +391,7 @@ impl clean::GenericBound {
415391
} else {
416392
f.write_str("use&lt;")?;
417393
}
418-
for (i, arg) in args.iter().enumerate() {
419-
if i > 0 {
420-
write!(f, ", ")?;
421-
}
422-
arg.fmt(f)?;
423-
}
394+
args.iter().joined(", ", f)?;
424395
if f.alternate() { f.write_str(">") } else { f.write_str("&gt;") }
425396
}
426397
})
@@ -524,11 +495,7 @@ pub(crate) enum HrefError {
524495
// Panics if `syms` is empty.
525496
pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
526497
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-
}
498+
write!(s, "{}", fmt::from_fn(|f| syms.iter().joined("::", f))).unwrap();
532499
s
533500
}
534501

@@ -572,20 +539,20 @@ fn generate_macro_def_id_path(
572539
}
573540

574541
if let Some(last) = path.last_mut() {
575-
*last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
542+
*last = Symbol::intern(&format!("macro.{last}.html"));
576543
}
577544

578545
let url = match cache.extern_locations[&def_id.krate] {
579546
ExternalLocation::Remote(ref s) => {
580547
// `ExternalLocation::Remote` always end with a `/`.
581-
format!("{s}{path}", path = path.iter().map(|p| p.as_str()).join("/"))
548+
format!("{s}{path}", path = fmt::from_fn(|f| path.iter().joined("/", f)))
582549
}
583550
ExternalLocation::Local => {
584551
// `root_path` always end with a `/`.
585552
format!(
586553
"{root_path}{path}",
587554
root_path = root_path.unwrap_or(""),
588-
path = path.iter().map(|p| p.as_str()).join("/")
555+
path = fmt::from_fn(|f| path.iter().joined("/", f))
589556
)
590557
}
591558
ExternalLocation::Unknown => {
@@ -682,9 +649,8 @@ fn make_href(
682649
url_parts.push("index.html");
683650
}
684651
_ => {
685-
let prefix = shortty.as_str();
686652
let last = fqp.last().unwrap();
687-
url_parts.push_fmt(format_args!("{prefix}.{last}.html"));
653+
url_parts.push_fmt(format_args!("{shortty}.{last}.html"));
688654
}
689655
}
690656
Ok((url_parts.finish(), shortty, fqp.to_vec()))
@@ -950,12 +916,7 @@ fn tybounds<'a, 'tcx: 'a>(
950916
cx: &'a Context<'tcx>,
951917
) -> impl Display + 'a + Captures<'tcx> {
952918
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-
}
919+
bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?;
959920
if let Some(lt) = lt {
960921
// We don't need to check `alternate` since we can be certain that
961922
// the lifetime doesn't contain any characters which need escaping.
@@ -974,7 +935,7 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>(
974935
if !params.is_empty() {
975936
f.write_str(keyword)?;
976937
f.write_str(if f.alternate() { "<" } else { "&lt;" })?;
977-
comma_sep(params.iter().map(|lt| lt.print(cx)), true).fmt(f)?;
938+
params.iter().map(|lt| lt.print(cx)).joined(", ", f)?;
978939
f.write_str(if f.alternate() { "> " } else { "&gt; " })?;
979940
}
980941
Ok(())
@@ -1025,9 +986,7 @@ fn fmt_type(
1025986
clean::Primitive(clean::PrimitiveType::Never) => {
1026987
primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
1027988
}
1028-
clean::Primitive(prim) => {
1029-
primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
1030-
}
989+
clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx),
1031990
clean::BareFunction(ref decl) => {
1032991
print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?;
1033992
decl.safety.print_with_space().fmt(f)?;
@@ -1067,18 +1026,16 @@ fn fmt_type(
10671026
primitive_link(
10681027
f,
10691028
PrimitiveType::Tuple,
1070-
format_args!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
1029+
format_args!(
1030+
"({})",
1031+
fmt::from_fn(|f| generic_names.iter().joined(", ", f))
1032+
),
10711033
cx,
10721034
)
10731035
} 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, ")")
1036+
f.write_str("(")?;
1037+
many.iter().map(|item| item.print(cx)).joined(", ", f)?;
1038+
f.write_str(")")
10821039
}
10831040
}
10841041
},
@@ -1407,14 +1364,15 @@ impl clean::Arguments {
14071364
cx: &'a Context<'tcx>,
14081365
) -> impl Display + 'a + Captures<'tcx> {
14091366
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(())
1367+
self.values
1368+
.iter()
1369+
.map(|input| {
1370+
fmt::from_fn(|f| {
1371+
write!(f, "{}: ", input.name)?;
1372+
input.type_.print(cx).fmt(f)
1373+
})
1374+
})
1375+
.joined(", ", f)
14181376
})
14191377
}
14201378
}
@@ -1723,12 +1681,7 @@ impl clean::ImportSource {
17231681
}
17241682
let name = self.path.last();
17251683
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-
)?;
1684+
primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?;
17321685
} else {
17331686
f.write_str(name.as_str())?;
17341687
}

0 commit comments

Comments
 (0)