Skip to content

Commit df3c141

Browse files
committed
Auto merge of rust-lang#2477 - RalfJung:show-error, r=RalfJung
don't make it quite so easy to get Miri to panic Panicking on incorrect `-Zmiri` flags is a bit embarrassing, so let's finally fix that.
2 parents 654e15b + b99d7bc commit df3c141

File tree

5 files changed

+69
-60
lines changed

5 files changed

+69
-60
lines changed

cargo-miri/src/main.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
#![feature(let_else)]
22
#![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq, rustc::internal)]
33

4+
#[macro_use]
5+
mod util;
6+
47
mod arg;
58
mod phases;
69
mod setup;
7-
mod util;
810
mod version;
911

1012
use std::{env, iter};
1113

12-
use crate::{phases::*, util::*};
14+
use crate::phases::*;
1315

1416
fn main() {
1517
// Rustc does not support non-UTF-8 arguments so we make no attempt either.
@@ -73,9 +75,9 @@ fn main() {
7375
}
7476

7577
let Some(first) = args.next() else {
76-
show_error(format!(
78+
show_error!(
7779
"`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`"
78-
))
80+
)
7981
};
8082
match first.as_str() {
8183
"miri" => phase_cargo_miri(args),

cargo-miri/src/phases.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,15 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
7777
// We cannot know which of those flags take arguments and which do not,
7878
// so we cannot detect subcommands later.
7979
let Some(subcommand) = args.next() else {
80-
show_error(format!("`cargo miri` needs to be called with a subcommand (`run`, `test`)"));
80+
show_error!("`cargo miri` needs to be called with a subcommand (`run`, `test`)");
8181
};
8282
let subcommand = match &*subcommand {
8383
"setup" => MiriCommand::Setup,
8484
"test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand),
8585
_ =>
86-
show_error(format!(
86+
show_error!(
8787
"`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, and `setup`."
88-
)),
88+
),
8989
};
9090
let verbose = num_arg_flag("-v");
9191

@@ -123,7 +123,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
123123
match arg {
124124
Ok(value) => {
125125
if target_dir.is_some() {
126-
show_error(format!("`--target-dir` is provided more than once"));
126+
show_error!("`--target-dir` is provided more than once");
127127
}
128128
target_dir = Some(value.into());
129129
}
@@ -456,16 +456,13 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
456456

457457
let binary = binary_args.next().unwrap();
458458
let file = File::open(&binary)
459-
.unwrap_or_else(|_| show_error(format!(
459+
.unwrap_or_else(|_| show_error!(
460460
"file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`", binary
461-
)));
461+
));
462462
let file = BufReader::new(file);
463463

464464
let info = serde_json::from_reader(file).unwrap_or_else(|_| {
465-
show_error(format!(
466-
"file {:?} contains outdated or invalid JSON; try `cargo clean`",
467-
binary
468-
))
465+
show_error!("file {:?} contains outdated or invalid JSON; try `cargo clean`", binary)
469466
});
470467
let info = match info {
471468
CrateRunInfo::RunWith(info) => info,
@@ -562,7 +559,7 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
562559
// An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support.
563560
// Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag;
564561
// otherwise, we won't be called as rustdoc at all.
565-
show_error(format!("cross-interpreting doctests is not currently supported by Miri."));
562+
show_error!("cross-interpreting doctests is not currently supported by Miri.");
566563
} else {
567564
cmd.arg(arg);
568565
}

cargo-miri/src/setup.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub fn setup(subcommand: &MiriCommand, host: &str, target: &str) {
7373
if xargo_version().map_or(true, |v| v < XARGO_MIN_VERSION) {
7474
if std::env::var_os("XARGO_CHECK").is_some() {
7575
// The user manually gave us a xargo binary; don't do anything automatically.
76-
show_error(format!("xargo is too old; please upgrade to the latest version"))
76+
show_error!("xargo is too old; please upgrade to the latest version")
7777
}
7878
let mut cmd = cargo();
7979
cmd.args(&["install", "xargo"]);
@@ -97,10 +97,10 @@ pub fn setup(subcommand: &MiriCommand, host: &str, target: &str) {
9797
.output()
9898
.expect("failed to determine sysroot");
9999
if !output.status.success() {
100-
show_error(format!(
100+
show_error!(
101101
"Failed to determine sysroot; Miri said:\n{}",
102102
String::from_utf8_lossy(&output.stderr).trim_end()
103-
));
103+
);
104104
}
105105
let sysroot = std::str::from_utf8(&output.stdout).unwrap();
106106
let sysroot = Path::new(sysroot.trim_end_matches('\n'));
@@ -121,14 +121,14 @@ pub fn setup(subcommand: &MiriCommand, host: &str, target: &str) {
121121
}
122122
};
123123
if !rust_src.exists() {
124-
show_error(format!("given Rust source directory `{}` does not exist.", rust_src.display()));
124+
show_error!("given Rust source directory `{}` does not exist.", rust_src.display());
125125
}
126126
if rust_src.file_name().and_then(OsStr::to_str) != Some("library") {
127-
show_error(format!(
127+
show_error!(
128128
"given Rust source directory `{}` does not seem to be the `library` subdirectory of \
129129
a Rust source checkout.",
130130
rust_src.display()
131-
));
131+
);
132132
}
133133

134134
// Next, we need our own libstd. Prepare a xargo project for that purpose.
@@ -226,11 +226,9 @@ path = "lib.rs"
226226
// Finally run it!
227227
if command.status().expect("failed to run xargo").success().not() {
228228
if only_setup {
229-
show_error(format!("failed to run xargo, see error details above"))
229+
show_error!("failed to run xargo, see error details above")
230230
} else {
231-
show_error(format!(
232-
"failed to run xargo; run `cargo miri setup` to see the error details"
233-
))
231+
show_error!("failed to run xargo; run `cargo miri setup` to see the error details")
234232
}
235233
}
236234

cargo-miri/src/util.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ use serde::{Deserialize, Serialize};
1414

1515
pub use crate::arg::*;
1616

17+
pub fn show_error(msg: &impl std::fmt::Display) -> ! {
18+
eprintln!("fatal error: {msg}");
19+
std::process::exit(1)
20+
}
21+
22+
macro_rules! show_error {
23+
($($tt:tt)*) => { crate::util::show_error(&format_args!($($tt)*)) };
24+
}
25+
1726
/// The information to run a crate with the given environment.
1827
#[derive(Clone, Serialize, Deserialize)]
1928
pub struct CrateRunEnv {
@@ -55,10 +64,10 @@ pub enum CrateRunInfo {
5564
impl CrateRunInfo {
5665
pub fn store(&self, filename: &Path) {
5766
let file = File::create(filename)
58-
.unwrap_or_else(|_| show_error(format!("cannot create `{}`", filename.display())));
67+
.unwrap_or_else(|_| show_error!("cannot create `{}`", filename.display()));
5968
let file = BufWriter::new(file);
6069
serde_json::ser::to_writer(file, self)
61-
.unwrap_or_else(|_| show_error(format!("cannot write to `{}`", filename.display())));
70+
.unwrap_or_else(|_| show_error!("cannot write to `{}`", filename.display()));
6271
}
6372
}
6473

@@ -70,11 +79,6 @@ pub enum MiriCommand {
7079
Forward(String),
7180
}
7281

73-
pub fn show_error(msg: String) -> ! {
74-
eprintln!("fatal error: {}", msg);
75-
std::process::exit(1)
76-
}
77-
7882
/// Escapes `s` in a way that is suitable for using it as a string literal in TOML syntax.
7983
pub fn escape_for_toml(s: &str) -> String {
8084
// We want to surround this string in quotes `"`. So we first escape all quotes,
@@ -187,15 +191,15 @@ pub fn ask_to_run(mut cmd: Command, ask: bool, text: &str) {
187191
match buf.trim().to_lowercase().as_ref() {
188192
// Proceed.
189193
"" | "y" | "yes" => {}
190-
"n" | "no" => show_error(format!("aborting as per your request")),
191-
a => show_error(format!("invalid answer `{}`", a)),
194+
"n" | "no" => show_error!("aborting as per your request"),
195+
a => show_error!("invalid answer `{}`", a),
192196
};
193197
} else {
194198
eprintln!("Running `{:?}` to {}.", cmd, text);
195199
}
196200

197201
if cmd.status().unwrap_or_else(|_| panic!("failed to execute {:?}", cmd)).success().not() {
198-
show_error(format!("failed to {}", text));
202+
show_error!("failed to {}", text);
199203
}
200204
}
201205

src/bin/miri.rs

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,15 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
152152
}
153153
}
154154

155-
fn show_error(msg: String) -> ! {
156-
eprintln!("fatal error: {}", msg);
155+
fn show_error(msg: &impl std::fmt::Display) -> ! {
156+
eprintln!("fatal error: {msg}");
157157
std::process::exit(1)
158158
}
159159

160+
macro_rules! show_error {
161+
($($tt:tt)*) => { show_error(&format_args!($($tt)*)) };
162+
}
163+
160164
fn init_early_loggers() {
161165
// Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
162166
// initialize them both, and we always initialize `miri`'s first.
@@ -234,19 +238,19 @@ fn host_sysroot() -> Option<String> {
234238
env::var_os("RUSTUP_TOOLCHAIN").or_else(|| env::var_os("MULTIRUST_TOOLCHAIN"))
235239
{
236240
if toolchain_runtime != toolchain {
237-
show_error(format!(
241+
show_error!(
238242
"This Miri got built with local toolchain `{toolchain}`, but now is being run under a different toolchain. \n\
239243
Make sure to run Miri in the toolchain it got built with, e.g. via `cargo +{toolchain} miri`."
240-
));
244+
)
241245
}
242246
}
243247
format!("{}/toolchains/{}", home, toolchain)
244248
}
245249
_ => option_env!("RUST_SYSROOT")
246250
.unwrap_or_else(|| {
247-
show_error(format!(
251+
show_error!(
248252
"To build Miri without rustup, set the `RUST_SYSROOT` env var at build time",
249-
))
253+
)
250254
})
251255
.to_owned(),
252256
})
@@ -272,9 +276,9 @@ fn run_compiler(
272276
// Using the built-in default here would be plain wrong, so we *require*
273277
// the env var to make sure things make sense.
274278
Some(env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
275-
show_error(format!(
279+
show_error!(
276280
"Miri was invoked in 'target' mode without `MIRI_SYSROOT` or `--sysroot` being set"
277-
))
281+
)
278282
}))
279283
} else {
280284
host_default_sysroot
@@ -379,7 +383,9 @@ fn main() {
379383
miri_config.check_abi = false;
380384
} else if arg == "-Zmiri-disable-isolation" {
381385
if matches!(isolation_enabled, Some(true)) {
382-
panic!("-Zmiri-disable-isolation cannot be used along with -Zmiri-isolation-error");
386+
show_error!(
387+
"-Zmiri-disable-isolation cannot be used along with -Zmiri-isolation-error"
388+
);
383389
} else {
384390
isolation_enabled = Some(false);
385391
}
@@ -390,7 +396,9 @@ fn main() {
390396
miri_config.track_outdated_loads = true;
391397
} else if let Some(param) = arg.strip_prefix("-Zmiri-isolation-error=") {
392398
if matches!(isolation_enabled, Some(false)) {
393-
panic!("-Zmiri-isolation-error cannot be used along with -Zmiri-disable-isolation");
399+
show_error!(
400+
"-Zmiri-isolation-error cannot be used along with -Zmiri-disable-isolation"
401+
);
394402
} else {
395403
isolation_enabled = Some(true);
396404
}
@@ -402,7 +410,7 @@ fn main() {
402410
"warn-nobacktrace" =>
403411
miri::IsolatedOp::Reject(miri::RejectOpWith::WarningWithoutBacktrace),
404412
_ =>
405-
panic!(
413+
show_error!(
406414
"-Zmiri-isolation-error must be `abort`, `hide`, `warn`, or `warn-nobacktrace`"
407415
),
408416
};
@@ -426,11 +434,11 @@ fn main() {
426434
);
427435
} else if let Some(param) = arg.strip_prefix("-Zmiri-seed=") {
428436
if miri_config.seed.is_some() {
429-
panic!("Cannot specify -Zmiri-seed multiple times!");
437+
show_error!("Cannot specify -Zmiri-seed multiple times!");
430438
}
431439
let seed = u64::from_str_radix(param, 16)
432-
.unwrap_or_else(|_| panic!(
433-
"-Zmiri-seed should only contain valid hex digits [0-9a-fA-F] and fit into a u64 (max 16 characters)"
440+
.unwrap_or_else(|_| show_error!(
441+
"-Zmiri-seed should only contain valid hex digits [0-9a-fA-F] and must fit into a u64 (max 16 characters)"
434442
));
435443
miri_config.seed = Some(seed);
436444
} else if let Some(param) = arg.strip_prefix("-Zmiri-env-exclude=") {
@@ -441,7 +449,7 @@ fn main() {
441449
let ids: Vec<u64> = match parse_comma_list(param) {
442450
Ok(ids) => ids,
443451
Err(err) =>
444-
panic!(
452+
show_error!(
445453
"-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {}",
446454
err
447455
),
@@ -450,14 +458,14 @@ fn main() {
450458
if let Some(id) = id {
451459
miri_config.tracked_pointer_tags.insert(id);
452460
} else {
453-
panic!("-Zmiri-track-pointer-tag requires nonzero arguments");
461+
show_error!("-Zmiri-track-pointer-tag requires nonzero arguments");
454462
}
455463
}
456464
} else if let Some(param) = arg.strip_prefix("-Zmiri-track-call-id=") {
457465
let ids: Vec<u64> = match parse_comma_list(param) {
458466
Ok(ids) => ids,
459467
Err(err) =>
460-
panic!(
468+
show_error!(
461469
"-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {}",
462470
err
463471
),
@@ -466,14 +474,14 @@ fn main() {
466474
if let Some(id) = id {
467475
miri_config.tracked_call_ids.insert(id);
468476
} else {
469-
panic!("-Zmiri-track-call-id requires a nonzero argument");
477+
show_error!("-Zmiri-track-call-id requires a nonzero argument");
470478
}
471479
}
472480
} else if let Some(param) = arg.strip_prefix("-Zmiri-track-alloc-id=") {
473481
let ids: Vec<miri::AllocId> = match parse_comma_list::<NonZeroU64>(param) {
474482
Ok(ids) => ids.into_iter().map(miri::AllocId).collect(),
475483
Err(err) =>
476-
panic!(
484+
show_error!(
477485
"-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {}",
478486
err
479487
),
@@ -483,11 +491,11 @@ fn main() {
483491
let rate = match param.parse::<f64>() {
484492
Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
485493
Ok(_) =>
486-
panic!(
494+
show_error!(
487495
"-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"
488496
),
489497
Err(err) =>
490-
panic!(
498+
show_error!(
491499
"-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}",
492500
err
493501
),
@@ -496,9 +504,9 @@ fn main() {
496504
} else if let Some(param) = arg.strip_prefix("-Zmiri-preemption-rate=") {
497505
let rate = match param.parse::<f64>() {
498506
Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
499-
Ok(_) => panic!("-Zmiri-preemption-rate must be between `0.0` and `1.0`"),
507+
Ok(_) => show_error!("-Zmiri-preemption-rate must be between `0.0` and `1.0`"),
500508
Err(err) =>
501-
panic!(
509+
show_error!(
502510
"-Zmiri-preemption-rate requires a `f64` between `0.0` and `1.0`: {}",
503511
err
504512
),
@@ -510,7 +518,7 @@ fn main() {
510518
} else if let Some(param) = arg.strip_prefix("-Zmiri-report-progress=") {
511519
let interval = match param.parse::<u32>() {
512520
Ok(i) => i,
513-
Err(err) => panic!("-Zmiri-report-progress requires a `u32`: {}", err),
521+
Err(err) => show_error!("-Zmiri-report-progress requires a `u32`: {}", err),
514522
};
515523
miri_config.report_progress = Some(interval);
516524
} else if let Some(param) = arg.strip_prefix("-Zmiri-measureme=") {
@@ -520,7 +528,7 @@ fn main() {
520528
"0" => BacktraceStyle::Off,
521529
"1" => BacktraceStyle::Short,
522530
"full" => BacktraceStyle::Full,
523-
_ => panic!("-Zmiri-backtrace may only be 0, 1, or full"),
531+
_ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"),
524532
};
525533
} else {
526534
// Forward to rustc.

0 commit comments

Comments
 (0)