Skip to content

Commit

Permalink
rustfmt --check: handle writing to a broken pipe gracefully
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bertschingert committed Jan 9, 2024
1 parent 6356fca commit e201a42
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 4 deletions.
8 changes: 7 additions & 1 deletion src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down
24 changes: 21 additions & 3 deletions src/rustfmt_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}"),
Expand Down

0 comments on commit e201a42

Please sign in to comment.