From a4917df571aedac31e9e322d317550de6ee493fd Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:22:23 +0000 Subject: [PATCH 1/2] clippy.toml: recommend `bstr::BStr` Display only for diagnostics The reasons of the `String::from_utf8` and `String::from_utf8_lossy` lints told authors to use `bstr::BStr` for display. `Display for BStr` writes U+FFFD for each invalid UTF-8 sequence, the same bytes that `from_utf8_lossy` produces. The reasons now say so. They keep `BStr` for diagnostics and name the byte API of each sink for everything else. A comment in the help text printer said that `Display for BStr` writes raw bytes and ignores the formatter width. bstr 1.12.1 writes U+FFFD for invalid UTF-8, and it pads when the spec has an alignment flag, as `{:<16}` does. The comment keeps only the statement about `&str`. --- clippy.toml | 4 ++-- src/runtime/cli/mod.rs | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clippy.toml b/clippy.toml index bf4d170052de..f1d8e44b9957 100644 --- a/clippy.toml +++ b/clippy.toml @@ -28,8 +28,8 @@ disallowed-methods = [ { path = "bun_core::output::pretty_errorln", reason = "renders args then tag-walks the rendered bytes; use the pretty_errorln! macro — allow only for runtime-built payloads, with justification" }, { path = "bun_core::output::warn", reason = "renders args then tag-walks the rendered bytes; use the warn! macro — allow only for runtime-built payloads, with justification" }, { path = "bun_core::output::debug_warn", reason = "renders args then tag-walks the rendered bytes; use the debug_warn! macro — allow only for runtime-built payloads, with justification" }, - { path = "alloc::string::String::from_utf8", reason = "keep data as bytes; for display use bstr::BStr, for JS-visible strings use bun_core::String::clone_utf8" }, - { path = "alloc::string::String::from_utf8_lossy", reason = "silently corrupts non-UTF-8 bytes and allocates; keep data as bytes (bstr::BStr for Display)" }, + { path = "alloc::string::String::from_utf8", reason = "keep data as bytes; in a diagnostic format the bytes with bstr::BStr (its Display writes U+FFFD for invalid UTF-8), for JS-visible strings use bun_core::String::clone_utf8, everywhere else (child process output, a path to open or to print for another program, hash input) use the sink's byte API (Vec::extend_from_slice, write_all, bun_core::output::print_bytes for stdout, bun_core::strings::concat_buf_t for a fixed buffer)" }, + { path = "alloc::string::String::from_utf8_lossy", reason = "silently corrupts non-UTF-8 bytes and allocates; keep data as bytes: in a diagnostic format the bytes with bstr::BStr (its Display writes the same U+FFFD, without the allocation), everywhere else (child process output, a path to open or to print for another program, hash input) use the sink's byte API (Vec::extend_from_slice, write_all, bun_core::output::print_bytes for stdout, bun_core::strings::concat_buf_t for a fixed buffer)" }, # == byte/substring search must go through bun_core::strings (highway, runtime-dispatched SIMD) == # libcore's searchers are scalar or compile-time-gated SSE2/NEON (we build with # -Ctarget-cpu=nehalem on x64); highway picks AVX2/AVX-512 at runtime. The diff --git a/src/runtime/cli/mod.rs b/src/runtime/cli/mod.rs index af5c7e3718cf..77c692cf6c2a 100644 --- a/src/runtime/cli/mod.rs +++ b/src/runtime/cli/mod.rs @@ -507,9 +507,7 @@ pub(crate) mod help_command { let package_remove_i = pick(PACKAGES_TO_REMOVE_FILLER.len()); let package_create_i = pick(PACKAGES_TO_CREATE_FILLER.len()); - // The filler tables are `&str` (not `&[u8]`) so the `{:<16}` - // width spec actually pads — `Display for BStr` writes raw bytes and - // ignores formatter width/alignment. + // The filler tables are `&str` (not `&[u8]`) so the `{:<16}` width spec pads. let args = ( PACKAGES_TO_X_FILLER[package_x_i], PACKAGES_TO_ADD_FILLER[package_add_i], From 826fbf21d4f084a608e1efe084dae5177ffe48b7 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:06:27 +0000 Subject: [PATCH 2/2] clippy.toml: say that `from_utf8_lossy` allocates only to replace bytes `String::from_utf8_lossy` returns a borrowed `Cow` for valid UTF-8. It allocates only when it writes U+FFFD for an invalid sequence. --- clippy.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy.toml b/clippy.toml index f1d8e44b9957..b737f11d62ea 100644 --- a/clippy.toml +++ b/clippy.toml @@ -29,7 +29,7 @@ disallowed-methods = [ { path = "bun_core::output::warn", reason = "renders args then tag-walks the rendered bytes; use the warn! macro — allow only for runtime-built payloads, with justification" }, { path = "bun_core::output::debug_warn", reason = "renders args then tag-walks the rendered bytes; use the debug_warn! macro — allow only for runtime-built payloads, with justification" }, { path = "alloc::string::String::from_utf8", reason = "keep data as bytes; in a diagnostic format the bytes with bstr::BStr (its Display writes U+FFFD for invalid UTF-8), for JS-visible strings use bun_core::String::clone_utf8, everywhere else (child process output, a path to open or to print for another program, hash input) use the sink's byte API (Vec::extend_from_slice, write_all, bun_core::output::print_bytes for stdout, bun_core::strings::concat_buf_t for a fixed buffer)" }, - { path = "alloc::string::String::from_utf8_lossy", reason = "silently corrupts non-UTF-8 bytes and allocates; keep data as bytes: in a diagnostic format the bytes with bstr::BStr (its Display writes the same U+FFFD, without the allocation), everywhere else (child process output, a path to open or to print for another program, hash input) use the sink's byte API (Vec::extend_from_slice, write_all, bun_core::output::print_bytes for stdout, bun_core::strings::concat_buf_t for a fixed buffer)" }, + { path = "alloc::string::String::from_utf8_lossy", reason = "silently corrupts non-UTF-8 bytes, and allocates to do it; keep data as bytes: in a diagnostic format the bytes with bstr::BStr (its Display writes the same U+FFFD, without the allocation), everywhere else (child process output, a path to open or to print for another program, hash input) use the sink's byte API (Vec::extend_from_slice, write_all, bun_core::output::print_bytes for stdout, bun_core::strings::concat_buf_t for a fixed buffer)" }, # == byte/substring search must go through bun_core::strings (highway, runtime-dispatched SIMD) == # libcore's searchers are scalar or compile-time-gated SSE2/NEON (we build with # -Ctarget-cpu=nehalem on x64); highway picks AVX2/AVX-512 at runtime. The