Skip to content

Commit 0506d8d

Browse files
committed
Auto merge of #8427 - davidtwco:terminal-width, r=ehuss
Add support for rustc's `-Z terminal-width`. This PR continues the work started in #7315, adding support for rustc's `-Z terminal-width` flag, which is used to trim diagnostic output to fit within the current terminal and was added in rust-lang/rust#63402 (with JSON emitter support in rust-lang/rust#73763). At the time of writing, rust-lang/rust#73763 isn't in nightly, so the test added in this PR will fail, but it should pass tomorrow (I've confirmed that it works with a local rustc build). cc @estebank
2 parents 62123e4 + af924d4 commit 0506d8d

File tree

7 files changed

+142
-20
lines changed

7 files changed

+142
-20
lines changed

src/bin/cargo/cli.rs

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Available unstable (nightly-only) flags:
3737
-Z timings -- Display concurrency information
3838
-Z doctest-xcompile -- Compile and run doctests for non-host target using runner config
3939
-Z crate-versions -- Add crate versions to generated docs
40+
-Z terminal-width -- Provide a terminal width to rustc for error truncation
4041
4142
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
4243
);

src/cargo/core/compiler/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub use self::lto::Lto;
4747
use self::output_depinfo::output_depinfo;
4848
use self::unit_graph::UnitDep;
4949
pub use crate::core::compiler::unit::{Unit, UnitInterner};
50+
use crate::core::features::nightly_features_allowed;
5051
use crate::core::manifest::TargetSourcePath;
5152
use crate::core::profiles::{PanicStrategy, Profile, Strip};
5253
use crate::core::{Edition, Feature, PackageId, Target};
@@ -709,13 +710,34 @@ fn add_error_format_and_color(
709710
// to emit a message that cargo will intercept.
710711
json.push_str(",artifacts");
711712
}
713+
712714
match cx.bcx.build_config.message_format {
713715
MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
714716
json.push_str(",diagnostic-short");
715717
}
716718
_ => {}
717719
}
718720
cmd.arg(json);
721+
722+
if nightly_features_allowed() {
723+
let config = cx.bcx.config;
724+
match (
725+
config.cli_unstable().terminal_width,
726+
config.shell().err_width().diagnostic_terminal_width(),
727+
) {
728+
// Terminal width explicitly provided - only useful for testing.
729+
(Some(Some(width)), _) => {
730+
cmd.arg(format!("-Zterminal-width={}", width));
731+
}
732+
// Terminal width was not explicitly provided but flag was provided - common case.
733+
(Some(None), Some(width)) => {
734+
cmd.arg(format!("-Zterminal-width={}", width));
735+
}
736+
// User didn't opt-in.
737+
_ => (),
738+
}
739+
}
740+
719741
Ok(())
720742
}
721743

src/cargo/core/features.rs

+12
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ pub struct CliUnstable {
357357
pub separate_nightlies: bool,
358358
pub multitarget: bool,
359359
pub rustdoc_map: bool,
360+
pub terminal_width: Option<Option<usize>>,
360361
}
361362

362363
impl CliUnstable {
@@ -411,6 +412,16 @@ impl CliUnstable {
411412
Ok(true)
412413
};
413414

415+
fn parse_usize_opt(value: Option<&str>) -> CargoResult<Option<usize>> {
416+
Ok(match value {
417+
Some(value) => match value.parse::<usize>() {
418+
Ok(value) => Some(value),
419+
Err(e) => bail!("expected a number, found: {}", e),
420+
},
421+
None => None,
422+
})
423+
}
424+
414425
match k {
415426
"print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
416427
"unstable-options" => self.unstable_options = parse_empty(k, v)?,
@@ -437,6 +448,7 @@ impl CliUnstable {
437448
"separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
438449
"multitarget" => self.multitarget = parse_empty(k, v)?,
439450
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
451+
"terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
440452
_ => bail!("unknown `-Z` flag specified: {}", k),
441453
}
442454

src/cargo/core/shell.rs

+45-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@ use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor};
66

77
use crate::util::errors::CargoResult;
88

9+
pub enum TtyWidth {
10+
NoTty,
11+
Known(usize),
12+
Guess(usize),
13+
}
14+
15+
impl TtyWidth {
16+
/// Returns the width provided with `-Z terminal-width` to rustc to truncate diagnostics with
17+
/// long lines.
18+
pub fn diagnostic_terminal_width(&self) -> Option<usize> {
19+
match *self {
20+
TtyWidth::NoTty | TtyWidth::Guess(_) => None,
21+
TtyWidth::Known(width) => Some(width),
22+
}
23+
}
24+
25+
/// Returns the width used by progress bars for the tty.
26+
pub fn progress_max_width(&self) -> Option<usize> {
27+
match *self {
28+
TtyWidth::NoTty => None,
29+
TtyWidth::Known(width) | TtyWidth::Guess(width) => Some(width),
30+
}
31+
}
32+
}
33+
934
/// The requested verbosity of output.
1035
#[derive(Debug, Clone, Copy, PartialEq)]
1136
pub enum Verbosity {
@@ -125,12 +150,12 @@ impl Shell {
125150
}
126151

127152
/// Returns the width of the terminal in spaces, if any.
128-
pub fn err_width(&self) -> Option<usize> {
153+
pub fn err_width(&self) -> TtyWidth {
129154
match self.output {
130155
ShellOut::Stream {
131156
stderr_tty: true, ..
132157
} => imp::stderr_width(),
133-
_ => None,
158+
_ => TtyWidth::NoTty,
134159
}
135160
}
136161

@@ -408,21 +433,21 @@ impl ColorChoice {
408433

409434
#[cfg(unix)]
410435
mod imp {
411-
use super::Shell;
436+
use super::{Shell, TtyWidth};
412437
use std::mem;
413438

414-
pub fn stderr_width() -> Option<usize> {
439+
pub fn stderr_width() -> TtyWidth {
415440
unsafe {
416441
let mut winsize: libc::winsize = mem::zeroed();
417442
// The .into() here is needed for FreeBSD which defines TIOCGWINSZ
418443
// as c_uint but ioctl wants c_ulong.
419444
if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ.into(), &mut winsize) < 0 {
420-
return None;
445+
return TtyWidth::NoTty;
421446
}
422447
if winsize.ws_col > 0 {
423-
Some(winsize.ws_col as usize)
448+
TtyWidth::Known(winsize.ws_col as usize)
424449
} else {
425-
None
450+
TtyWidth::NoTty
426451
}
427452
}
428453
}
@@ -445,14 +470,14 @@ mod imp {
445470
use winapi::um::wincon::*;
446471
use winapi::um::winnt::*;
447472

448-
pub(super) use super::default_err_erase_line as err_erase_line;
473+
pub(super) use super::{default_err_erase_line as err_erase_line, TtyWidth};
449474

450-
pub fn stderr_width() -> Option<usize> {
475+
pub fn stderr_width() -> TtyWidth {
451476
unsafe {
452477
let stdout = GetStdHandle(STD_ERROR_HANDLE);
453478
let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
454479
if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 {
455-
return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize);
480+
return TtyWidth::Known((csbi.srWindow.Right - csbi.srWindow.Left) as usize);
456481
}
457482

458483
// On mintty/msys/cygwin based terminals, the above fails with
@@ -468,7 +493,7 @@ mod imp {
468493
ptr::null_mut(),
469494
);
470495
if h == INVALID_HANDLE_VALUE {
471-
return None;
496+
return TtyWidth::NoTty;
472497
}
473498

474499
let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
@@ -484,17 +509,21 @@ mod imp {
484509
// resize the console correctly, but there's no reasonable way
485510
// to detect which kind of terminal we are running in, or if
486511
// GetConsoleScreenBufferInfo returns accurate information.
487-
return Some(cmp::min(60, width));
512+
return TtyWidth::Guess(cmp::min(60, width));
488513
}
489-
None
514+
515+
TtyWidth::NoTty
490516
}
491517
}
492518
}
493519

494520
#[cfg(windows)]
495521
fn default_err_erase_line(shell: &mut Shell) {
496-
if let Some(max_width) = imp::stderr_width() {
497-
let blank = " ".repeat(max_width);
498-
drop(write!(shell.output.stderr(), "{}\r", blank));
522+
match imp::stderr_width() {
523+
TtyWidth::Known(max_width) | TtyWidth::Guess(max_width) => {
524+
let blank = " ".repeat(max_width);
525+
drop(write!(shell.output.stderr(), "{}\r", blank));
526+
}
527+
_ => (),
499528
}
500529
}

src/cargo/util/progress.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl<'cfg> Progress<'cfg> {
5050
}
5151

5252
Progress {
53-
state: cfg.shell().err_width().map(|n| State {
53+
state: cfg.shell().err_width().progress_max_width().map(|n| State {
5454
config: cfg,
5555
format: Format {
5656
style,
@@ -216,7 +216,7 @@ impl<'cfg> State<'cfg> {
216216
}
217217

218218
fn try_update_max_width(&mut self) {
219-
if let Some(n) = self.config.shell().err_width() {
219+
if let Some(n) = self.config.shell().err_width().progress_max_width() {
220220
self.format.max_width = n;
221221
}
222222
}

src/doc/src/reference/unstable.md

+34
Original file line numberDiff line numberDiff line change
@@ -826,3 +826,37 @@ sysroot. If you are using rustup, this documentation can be installed with
826826
The default value is `"remote"`.
827827

828828
The value may also take a URL for a custom location.
829+
830+
### terminal-width
831+
This feature provides a new flag, `-Z terminal-width`, which is used to pass
832+
a terminal width to `rustc` so that error messages containing long lines
833+
can be intelligently truncated.
834+
835+
For example, passing `-Z terminal-width=20` (an arbitrarily low value) might
836+
produce the following error:
837+
838+
```text
839+
error[E0308]: mismatched types
840+
--> src/main.rs:2:17
841+
|
842+
2 | ..._: () = 42;
843+
| -- ^^ expected `()`, found integer
844+
| |
845+
| expected due to this
846+
847+
error: aborting due to previous error
848+
```
849+
850+
In contrast, without `-Z terminal-width`, the error would look as shown below:
851+
852+
```text
853+
error[E0308]: mismatched types
854+
--> src/main.rs:2:17
855+
|
856+
2 | let _: () = 42;
857+
| -- ^^ expected `()`, found integer
858+
| |
859+
| expected due to this
860+
861+
error: aborting due to previous error
862+
```

tests/testsuite/build.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use cargo::{
77
use cargo_test_support::paths::{root, CargoPathExt};
88
use cargo_test_support::registry::Package;
99
use cargo_test_support::{
10-
basic_bin_manifest, basic_lib_manifest, basic_manifest, lines_match, main_file, project,
11-
rustc_host, sleep_ms, symlink_supported, t, Execs, ProjectBuilder,
10+
basic_bin_manifest, basic_lib_manifest, basic_manifest, is_nightly, lines_match, main_file,
11+
project, rustc_host, sleep_ms, symlink_supported, t, Execs, ProjectBuilder,
1212
};
1313
use std::env;
1414
use std::fs;
@@ -5103,3 +5103,27 @@ fn target_directory_backup_exclusion() {
51035103
p.cargo("build").run();
51045104
assert!(!&cachedir_tag.is_file());
51055105
}
5106+
5107+
#[cargo_test]
5108+
fn simple_terminal_width() {
5109+
if !is_nightly() {
5110+
// --terminal-width is unstable
5111+
return;
5112+
}
5113+
let p = project()
5114+
.file(
5115+
"src/lib.rs",
5116+
r#"
5117+
fn main() {
5118+
let _: () = 42;
5119+
}
5120+
"#,
5121+
)
5122+
.build();
5123+
5124+
p.cargo("build -Zterminal-width=20")
5125+
.masquerade_as_nightly_cargo()
5126+
.with_status(101)
5127+
.with_stderr_contains("3 | ..._: () = 42;")
5128+
.run();
5129+
}

0 commit comments

Comments
 (0)