From e201a42d8a6309cc9e1c28c8aa727cede1fe6a4c Mon Sep 17 00:00:00 2001 From: Thomas Bertschinger Date: Mon, 8 Jan 2024 20:34:30 -0700 Subject: [PATCH] rustfmt --check: handle writing to a broken pipe gracefully The rustfmt tool panics when any writes to the output file descriptor fail, whether sending color codes to the terminal, regular writes, or flushing stdout. It's fairly easy to hit this by accidentally writing to a broken pipe. For example, I hit this when I tried to pipe into `less` but made a typo: $ rustfmt --check src/main.rs | les panic This patch updates rustfmt to handle BrokenPipe / EPIPE failures more gracefully. Any failures lead to the program immediately exiting, since there is no sense in continuing to write to a broken pipe, except for the final flush() which doesn't need to exit early since it's already at the end of program execution. Any write errors aside from BrokenPipe still result in panic!(), since any other errors are unexpected. This leaves the program behaving the same as before for any other write errors. --- src/bin/main.rs | 8 +++++++- src/rustfmt_diff.rs | 24 +++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 88281d296be..a3adc06b0f6 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -43,7 +43,13 @@ fn main() { } }; // Make sure standard output is flushed before we exit. - std::io::stdout().flush().unwrap(); + // Silently ignore broken pipe errors, but any other errors are unexpected. + match std::io::stdout().flush() { + Err(e) if e.kind() != io::ErrorKind::BrokenPipe => { + panic!("flushing stdout failed: unexpected error: {:?}", e) + } + _ => {} + }; // Exit with given exit code. // diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs index c9883452185..0c5a4bb505c 100644 --- a/src/rustfmt_diff.rs +++ b/src/rustfmt_diff.rs @@ -164,11 +164,29 @@ impl OutputWriter { match &mut self.terminal { Some(ref mut t) => { if let Some(color) = color { - t.fg(color).unwrap(); + match t.fg(color) { + // If writing to a broken pipe, there is no reason to continue. + // Any other error is unexpected, however. + Err(term::Error::Io(e)) if e.kind() == io::ErrorKind::BrokenPipe => { + std::process::exit(1); + }, + Err(e) => panic!("write failed: unexpected error: {:?}", e), + _ => {}, + }; + } + match writeln!(t, "{msg}") { + Err(e) if e.kind() == io::ErrorKind::BrokenPipe => std::process::exit(1), + Err(e) => panic!("write failed: unexpected error: {:?}", e), + _ => {} } - writeln!(t, "{msg}").unwrap(); if color.is_some() { - t.reset().unwrap(); + match t.reset() { + Err(term::Error::Io(e)) if e.kind() == io::ErrorKind::BrokenPipe => { + std::process::exit(1); + }, + Err(e) => panic!("write failed: unexpected error: {:?}", e), + _ => {}, + }; } } None => println!("{msg}"),