Skip to content

Commit 294b29d

Browse files
Auto merge of #148950 - lolbinarycat:rustdoc-fmt-io-write, r=<try>
rustdoc: add fmt_json helper and use it to avoid allocations
2 parents c910098 + 2369da3 commit 294b29d

File tree

6 files changed

+63
-18
lines changed

6 files changed

+63
-18
lines changed

src/librustdoc/display.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Various utilities for working with [`fmt::Display`] implementations.
22
33
use std::fmt::{self, Display, Formatter, FormattingOptions};
4+
use std::io;
45

56
pub(crate) trait Joined: IntoIterator {
67
/// Takes an iterator over elements that implement [`Display`], and format them into `f`, separated by `sep`.
@@ -128,3 +129,37 @@ impl WithOpts {
128129
})
129130
}
130131
}
132+
133+
pub(crate) struct IoWriteToFmt<'a, 'b> {
134+
f: &'a mut Formatter<'b>,
135+
}
136+
137+
impl<'a, 'b> IoWriteToFmt<'a, 'b> {
138+
pub(crate) fn new(f: &'a mut Formatter<'b>) -> Self {
139+
Self { f }
140+
}
141+
}
142+
143+
impl io::Write for IoWriteToFmt<'_, '_> {
144+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
145+
let Ok(s) = str::from_utf8(buf) else {
146+
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid utf8"));
147+
};
148+
self.f.write_str(s).map_err(|e| io::Error::new(io::ErrorKind::Other, e)).map(|_| buf.len())
149+
}
150+
151+
fn flush(&mut self) -> io::Result<()> {
152+
Ok(())
153+
}
154+
155+
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
156+
self.f.write_fmt(args).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
157+
}
158+
}
159+
160+
pub(crate) fn fmt_json<T: ?Sized + serde::Serialize>(
161+
f: &mut Formatter<'_>,
162+
value: &T,
163+
) -> serde_json::Result<()> {
164+
serde_json::to_writer(IoWriteToFmt::new(f), value)
165+
}

src/librustdoc/doctest.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::process::{self, Command, Stdio};
1212
use std::sync::atomic::{AtomicUsize, Ordering};
1313
use std::sync::{Arc, Mutex};
1414
use std::time::{Duration, Instant};
15-
use std::{panic, str};
15+
use std::{fmt, panic, str};
1616

1717
pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder};
1818
pub(crate) use markdown::test as test_markdown;
@@ -34,6 +34,7 @@ use tracing::debug;
3434

3535
use self::rust::HirCollector;
3636
use crate::config::{Options as RustdocOptions, OutputFormat};
37+
use crate::display::fmt_json;
3738
use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine};
3839
use crate::lint::init_lints;
3940

@@ -309,7 +310,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
309310
.unwrap_or("warn")
310311
.to_string();
311312
let uext = UnusedExterns { lint_level, unused_extern_names };
312-
let unused_extern_json = serde_json::to_string(&uext).unwrap();
313+
let unused_extern_json = fmt::from_fn(|f| Ok(fmt_json(f, &uext).unwrap()));
313314
eprintln!("{unused_extern_json}");
314315
}
315316
}

src/librustdoc/html/render/context.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::clean::types::ExternalLocation;
2222
use crate::clean::utils::has_doc_flag;
2323
use crate::clean::{self, ExternalCrate};
2424
use crate::config::{ModuleSorting, RenderOptions, ShouldMerge};
25+
use crate::display::fmt_json;
2526
use crate::docfs::{DocFS, PathError};
2627
use crate::error::Error;
2728
use crate::formats::FormatRenderer;
@@ -826,7 +827,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
826827
};
827828
let items = self.build_sidebar_items(module);
828829
let js_dst = self.dst.join(format!("sidebar-items{}.js", self.shared.resource_suffix));
829-
let v = format!("window.SIDEBAR_ITEMS = {};", serde_json::to_string(&items).unwrap());
830+
let v = format!(
831+
"window.SIDEBAR_ITEMS = {};",
832+
fmt::from_fn(|f| Ok(fmt_json(f, &items).unwrap()))
833+
);
830834
self.shared.fs.write(js_dst, v)?;
831835
}
832836
Ok(())

src/librustdoc/html/render/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ pub(crate) use self::context::*;
6767
pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
6868
pub(crate) use self::write_shared::*;
6969
use crate::clean::{self, ItemId, RenderedLink};
70-
use crate::display::{Joined as _, MaybeDisplay as _};
70+
use crate::display::{Joined as _, MaybeDisplay as _, fmt_json};
7171
use crate::error::Error;
7272
use crate::formats::Impl;
7373
use crate::formats::cache::Cache;
@@ -1730,10 +1730,14 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
17301730
(format!("{:#}", print_type(ty, cx)), out)
17311731
}
17321732

1733-
fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
1733+
fn notable_traits_json<'a>(
1734+
f: &mut fmt::Formatter<'_>,
1735+
tys: impl Iterator<Item = &'a clean::Type>,
1736+
cx: &Context<'_>,
1737+
) {
17341738
let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>();
17351739
mp.sort_unstable_keys();
1736-
serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail")
1740+
fmt_json(f, &mp).expect("serialize (string, string) -> json object cannot fail");
17371741
}
17381742

17391743
#[derive(Clone, Copy, Debug)]

src/librustdoc/html/render/print_item.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,9 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Disp
269269
// Render notable-traits.js used for all methods in this module.
270270
let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut();
271271
if !types_with_notable_traits.is_empty() {
272-
write!(
273-
buf,
274-
r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
275-
notable_traits_json(types_with_notable_traits.iter(), cx),
276-
)?;
272+
write!(buf, r#"<script type="text/json" id="notable-traits-data">"#)?;
273+
notable_traits_json(buf, types_with_notable_traits.iter(), cx);
274+
write!(buf, "</script>")?;
277275
types_with_notable_traits.clear();
278276
}
279277
Ok(())

src/librustdoc/passes/calculate_doc_coverage.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Calculates information used for the --show-coverage flag.
22
33
use std::collections::BTreeMap;
4-
use std::ops;
4+
use std::{fmt, ops};
55

66
use rustc_hir as hir;
77
use rustc_lint::builtin::MISSING_DOCS;
@@ -13,6 +13,7 @@ use tracing::debug;
1313

1414
use crate::clean;
1515
use crate::core::DocContext;
16+
use crate::display::fmt_json;
1617
use crate::html::markdown::{ErrorCodes, find_testable_code};
1718
use crate::passes::Pass;
1819
use crate::passes::check_doc_test_visibility::{Tests, should_have_doc_example};
@@ -119,15 +120,17 @@ fn limit_filename_len(filename: String) -> String {
119120
}
120121

121122
impl CoverageCalculator<'_, '_> {
122-
fn to_json(&self) -> String {
123-
serde_json::to_string(
124-
&self
123+
fn to_json(&self) -> impl fmt::Display {
124+
fmt::from_fn(|f| {
125+
let map = &self
125126
.items
126127
.iter()
127128
.map(|(k, v)| (k.prefer_local().to_string(), v))
128-
.collect::<BTreeMap<String, &ItemCount>>(),
129-
)
130-
.expect("failed to convert JSON data to string")
129+
.collect::<BTreeMap<_, &ItemCount>>();
130+
131+
fmt_json(f, &map).expect("failed to convert JSON data to string");
132+
Ok(())
133+
})
131134
}
132135

133136
fn print_results(&self) {

0 commit comments

Comments
 (0)