about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-08-08 15:37:01 +0000
committerbors <bors@rust-lang.org>2022-08-08 15:37:01 +0000
commitdf3c141762b4082ef405bf092b385ca859f1dc8b (patch)
treeb0afd1e7090e6f3cd42e28eae3ec092d1f0fe52b
parent654e15b51c5d177b7d2c2345904a021c4c61f2ef (diff)
parentb99d7bc77f1b7986954ec8bd2f1c9f80664e8fd4 (diff)
downloadrust-df3c141762b4082ef405bf092b385ca859f1dc8b.tar.gz
rust-df3c141762b4082ef405bf092b385ca859f1dc8b.zip
Auto merge of #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.
-rw-r--r--cargo-miri/src/main.rs10
-rw-r--r--cargo-miri/src/phases.rs19
-rw-r--r--cargo-miri/src/setup.rs18
-rw-r--r--cargo-miri/src/util.rs24
-rw-r--r--src/bin/miri.rs58
5 files changed, 69 insertions, 60 deletions
diff --git a/cargo-miri/src/main.rs b/cargo-miri/src/main.rs
index 7b16af6f0c6..09e2cd3c286 100644
--- a/cargo-miri/src/main.rs
+++ b/cargo-miri/src/main.rs
@@ -1,15 +1,17 @@
 #![feature(let_else)]
 #![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq, rustc::internal)]
 
+#[macro_use]
+mod util;
+
 mod arg;
 mod phases;
 mod setup;
-mod util;
 mod version;
 
 use std::{env, iter};
 
-use crate::{phases::*, util::*};
+use crate::phases::*;
 
 fn main() {
     // Rustc does not support non-UTF-8 arguments so we make no attempt either.
@@ -73,9 +75,9 @@ fn main() {
     }
 
     let Some(first) = args.next() else {
-        show_error(format!(
+        show_error!(
             "`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`"
-        ))
+        )
     };
     match first.as_str() {
         "miri" => phase_cargo_miri(args),
diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs
index 8635661323d..93eb3cb1746 100644
--- a/cargo-miri/src/phases.rs
+++ b/cargo-miri/src/phases.rs
@@ -77,15 +77,15 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
     // We cannot know which of those flags take arguments and which do not,
     // so we cannot detect subcommands later.
     let Some(subcommand) = args.next() else {
-        show_error(format!("`cargo miri` needs to be called with a subcommand (`run`, `test`)"));
+        show_error!("`cargo miri` needs to be called with a subcommand (`run`, `test`)");
     };
     let subcommand = match &*subcommand {
         "setup" => MiriCommand::Setup,
         "test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand),
         _ =>
-            show_error(format!(
+            show_error!(
                 "`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, and `setup`."
-            )),
+            ),
     };
     let verbose = num_arg_flag("-v");
 
@@ -123,7 +123,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
         match arg {
             Ok(value) => {
                 if target_dir.is_some() {
-                    show_error(format!("`--target-dir` is provided more than once"));
+                    show_error!("`--target-dir` is provided more than once");
                 }
                 target_dir = Some(value.into());
             }
@@ -456,16 +456,13 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
 
     let binary = binary_args.next().unwrap();
     let file = File::open(&binary)
-        .unwrap_or_else(|_| show_error(format!(
+        .unwrap_or_else(|_| show_error!(
             "file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`", binary
-        )));
+        ));
     let file = BufReader::new(file);
 
     let info = serde_json::from_reader(file).unwrap_or_else(|_| {
-        show_error(format!(
-            "file {:?} contains outdated or invalid JSON; try `cargo clean`",
-            binary
-        ))
+        show_error!("file {:?} contains outdated or invalid JSON; try `cargo clean`", binary)
     });
     let info = match info {
         CrateRunInfo::RunWith(info) => info,
@@ -562,7 +559,7 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
             // An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support.
             // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag;
             // otherwise, we won't be called as rustdoc at all.
-            show_error(format!("cross-interpreting doctests is not currently supported by Miri."));
+            show_error!("cross-interpreting doctests is not currently supported by Miri.");
         } else {
             cmd.arg(arg);
         }
diff --git a/cargo-miri/src/setup.rs b/cargo-miri/src/setup.rs
index 1211b47e3ba..62d6e25a53e 100644
--- a/cargo-miri/src/setup.rs
+++ b/cargo-miri/src/setup.rs
@@ -73,7 +73,7 @@ pub fn setup(subcommand: &MiriCommand, host: &str, target: &str) {
     if xargo_version().map_or(true, |v| v < XARGO_MIN_VERSION) {
         if std::env::var_os("XARGO_CHECK").is_some() {
             // The user manually gave us a xargo binary; don't do anything automatically.
-            show_error(format!("xargo is too old; please upgrade to the latest version"))
+            show_error!("xargo is too old; please upgrade to the latest version")
         }
         let mut cmd = cargo();
         cmd.args(&["install", "xargo"]);
@@ -97,10 +97,10 @@ pub fn setup(subcommand: &MiriCommand, host: &str, target: &str) {
                 .output()
                 .expect("failed to determine sysroot");
             if !output.status.success() {
-                show_error(format!(
+                show_error!(
                     "Failed to determine sysroot; Miri said:\n{}",
                     String::from_utf8_lossy(&output.stderr).trim_end()
-                ));
+                );
             }
             let sysroot = std::str::from_utf8(&output.stdout).unwrap();
             let sysroot = Path::new(sysroot.trim_end_matches('\n'));
@@ -121,14 +121,14 @@ pub fn setup(subcommand: &MiriCommand, host: &str, target: &str) {
         }
     };
     if !rust_src.exists() {
-        show_error(format!("given Rust source directory `{}` does not exist.", rust_src.display()));
+        show_error!("given Rust source directory `{}` does not exist.", rust_src.display());
     }
     if rust_src.file_name().and_then(OsStr::to_str) != Some("library") {
-        show_error(format!(
+        show_error!(
             "given Rust source directory `{}` does not seem to be the `library` subdirectory of \
              a Rust source checkout.",
             rust_src.display()
-        ));
+        );
     }
 
     // Next, we need our own libstd. Prepare a xargo project for that purpose.
@@ -226,11 +226,9 @@ path = "lib.rs"
     // Finally run it!
     if command.status().expect("failed to run xargo").success().not() {
         if only_setup {
-            show_error(format!("failed to run xargo, see error details above"))
+            show_error!("failed to run xargo, see error details above")
         } else {
-            show_error(format!(
-                "failed to run xargo; run `cargo miri setup` to see the error details"
-            ))
+            show_error!("failed to run xargo; run `cargo miri setup` to see the error details")
         }
     }
 
diff --git a/cargo-miri/src/util.rs b/cargo-miri/src/util.rs
index b5401d71671..8f29eebaac1 100644
--- a/cargo-miri/src/util.rs
+++ b/cargo-miri/src/util.rs
@@ -14,6 +14,15 @@ use serde::{Deserialize, Serialize};
 
 pub use crate::arg::*;
 
+pub fn show_error(msg: &impl std::fmt::Display) -> ! {
+    eprintln!("fatal error: {msg}");
+    std::process::exit(1)
+}
+
+macro_rules! show_error {
+    ($($tt:tt)*) => { crate::util::show_error(&format_args!($($tt)*)) };
+}
+
 /// The information to run a crate with the given environment.
 #[derive(Clone, Serialize, Deserialize)]
 pub struct CrateRunEnv {
@@ -55,10 +64,10 @@ pub enum CrateRunInfo {
 impl CrateRunInfo {
     pub fn store(&self, filename: &Path) {
         let file = File::create(filename)
-            .unwrap_or_else(|_| show_error(format!("cannot create `{}`", filename.display())));
+            .unwrap_or_else(|_| show_error!("cannot create `{}`", filename.display()));
         let file = BufWriter::new(file);
         serde_json::ser::to_writer(file, self)
-            .unwrap_or_else(|_| show_error(format!("cannot write to `{}`", filename.display())));
+            .unwrap_or_else(|_| show_error!("cannot write to `{}`", filename.display()));
     }
 }
 
@@ -70,11 +79,6 @@ pub enum MiriCommand {
     Forward(String),
 }
 
-pub fn show_error(msg: String) -> ! {
-    eprintln!("fatal error: {}", msg);
-    std::process::exit(1)
-}
-
 /// Escapes `s` in a way that is suitable for using it as a string literal in TOML syntax.
 pub fn escape_for_toml(s: &str) -> String {
     // 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) {
         match buf.trim().to_lowercase().as_ref() {
             // Proceed.
             "" | "y" | "yes" => {}
-            "n" | "no" => show_error(format!("aborting as per your request")),
-            a => show_error(format!("invalid answer `{}`", a)),
+            "n" | "no" => show_error!("aborting as per your request"),
+            a => show_error!("invalid answer `{}`", a),
         };
     } else {
         eprintln!("Running `{:?}` to {}.", cmd, text);
     }
 
     if cmd.status().unwrap_or_else(|_| panic!("failed to execute {:?}", cmd)).success().not() {
-        show_error(format!("failed to {}", text));
+        show_error!("failed to {}", text);
     }
 }
 
diff --git a/src/bin/miri.rs b/src/bin/miri.rs
index b5a45d162f2..ca0787b2298 100644
--- a/src/bin/miri.rs
+++ b/src/bin/miri.rs
@@ -152,11 +152,15 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
     }
 }
 
-fn show_error(msg: String) -> ! {
-    eprintln!("fatal error: {}", msg);
+fn show_error(msg: &impl std::fmt::Display) -> ! {
+    eprintln!("fatal error: {msg}");
     std::process::exit(1)
 }
 
+macro_rules! show_error {
+    ($($tt:tt)*) => { show_error(&format_args!($($tt)*)) };
+}
+
 fn init_early_loggers() {
     // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
     // initialize them both, and we always initialize `miri`'s first.
@@ -234,19 +238,19 @@ fn host_sysroot() -> Option<String> {
                 env::var_os("RUSTUP_TOOLCHAIN").or_else(|| env::var_os("MULTIRUST_TOOLCHAIN"))
             {
                 if toolchain_runtime != toolchain {
-                    show_error(format!(
+                    show_error!(
                         "This Miri got built with local toolchain `{toolchain}`, but now is being run under a different toolchain. \n\
                         Make sure to run Miri in the toolchain it got built with, e.g. via `cargo +{toolchain} miri`."
-                    ));
+                    )
                 }
             }
             format!("{}/toolchains/{}", home, toolchain)
         }
         _ => option_env!("RUST_SYSROOT")
             .unwrap_or_else(|| {
-                show_error(format!(
+                show_error!(
                     "To build Miri without rustup, set the `RUST_SYSROOT` env var at build time",
-                ))
+                )
             })
             .to_owned(),
     })
@@ -272,9 +276,9 @@ fn run_compiler(
             // Using the built-in default here would be plain wrong, so we *require*
             // the env var to make sure things make sense.
             Some(env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
-                show_error(format!(
+                show_error!(
                     "Miri was invoked in 'target' mode without `MIRI_SYSROOT` or `--sysroot` being set"
-                ))
+                )
             }))
         } else {
             host_default_sysroot
@@ -379,7 +383,9 @@ fn main() {
             miri_config.check_abi = false;
         } else if arg == "-Zmiri-disable-isolation" {
             if matches!(isolation_enabled, Some(true)) {
-                panic!("-Zmiri-disable-isolation cannot be used along with -Zmiri-isolation-error");
+                show_error!(
+                    "-Zmiri-disable-isolation cannot be used along with -Zmiri-isolation-error"
+                );
             } else {
                 isolation_enabled = Some(false);
             }
@@ -390,7 +396,9 @@ fn main() {
             miri_config.track_outdated_loads = true;
         } else if let Some(param) = arg.strip_prefix("-Zmiri-isolation-error=") {
             if matches!(isolation_enabled, Some(false)) {
-                panic!("-Zmiri-isolation-error cannot be used along with -Zmiri-disable-isolation");
+                show_error!(
+                    "-Zmiri-isolation-error cannot be used along with -Zmiri-disable-isolation"
+                );
             } else {
                 isolation_enabled = Some(true);
             }
@@ -402,7 +410,7 @@ fn main() {
                 "warn-nobacktrace" =>
                     miri::IsolatedOp::Reject(miri::RejectOpWith::WarningWithoutBacktrace),
                 _ =>
-                    panic!(
+                    show_error!(
                         "-Zmiri-isolation-error must be `abort`, `hide`, `warn`, or `warn-nobacktrace`"
                     ),
             };
@@ -426,11 +434,11 @@ fn main() {
             );
         } else if let Some(param) = arg.strip_prefix("-Zmiri-seed=") {
             if miri_config.seed.is_some() {
-                panic!("Cannot specify -Zmiri-seed multiple times!");
+                show_error!("Cannot specify -Zmiri-seed multiple times!");
             }
             let seed = u64::from_str_radix(param, 16)
-                        .unwrap_or_else(|_| panic!(
-                            "-Zmiri-seed should only contain valid hex digits [0-9a-fA-F] and fit into a u64 (max 16 characters)"
+                        .unwrap_or_else(|_| show_error!(
+                            "-Zmiri-seed should only contain valid hex digits [0-9a-fA-F] and must fit into a u64 (max 16 characters)"
                         ));
             miri_config.seed = Some(seed);
         } else if let Some(param) = arg.strip_prefix("-Zmiri-env-exclude=") {
@@ -441,7 +449,7 @@ fn main() {
             let ids: Vec<u64> = match parse_comma_list(param) {
                 Ok(ids) => ids,
                 Err(err) =>
-                    panic!(
+                    show_error!(
                         "-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {}",
                         err
                     ),
@@ -450,14 +458,14 @@ fn main() {
                 if let Some(id) = id {
                     miri_config.tracked_pointer_tags.insert(id);
                 } else {
-                    panic!("-Zmiri-track-pointer-tag requires nonzero arguments");
+                    show_error!("-Zmiri-track-pointer-tag requires nonzero arguments");
                 }
             }
         } else if let Some(param) = arg.strip_prefix("-Zmiri-track-call-id=") {
             let ids: Vec<u64> = match parse_comma_list(param) {
                 Ok(ids) => ids,
                 Err(err) =>
-                    panic!(
+                    show_error!(
                         "-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {}",
                         err
                     ),
@@ -466,14 +474,14 @@ fn main() {
                 if let Some(id) = id {
                     miri_config.tracked_call_ids.insert(id);
                 } else {
-                    panic!("-Zmiri-track-call-id requires a nonzero argument");
+                    show_error!("-Zmiri-track-call-id requires a nonzero argument");
                 }
             }
         } else if let Some(param) = arg.strip_prefix("-Zmiri-track-alloc-id=") {
             let ids: Vec<miri::AllocId> = match parse_comma_list::<NonZeroU64>(param) {
                 Ok(ids) => ids.into_iter().map(miri::AllocId).collect(),
                 Err(err) =>
-                    panic!(
+                    show_error!(
                         "-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {}",
                         err
                     ),
@@ -483,11 +491,11 @@ fn main() {
             let rate = match param.parse::<f64>() {
                 Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
                 Ok(_) =>
-                    panic!(
+                    show_error!(
                         "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"
                     ),
                 Err(err) =>
-                    panic!(
+                    show_error!(
                         "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}",
                         err
                     ),
@@ -496,9 +504,9 @@ fn main() {
         } else if let Some(param) = arg.strip_prefix("-Zmiri-preemption-rate=") {
             let rate = match param.parse::<f64>() {
                 Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
-                Ok(_) => panic!("-Zmiri-preemption-rate must be between `0.0` and `1.0`"),
+                Ok(_) => show_error!("-Zmiri-preemption-rate must be between `0.0` and `1.0`"),
                 Err(err) =>
-                    panic!(
+                    show_error!(
                         "-Zmiri-preemption-rate requires a `f64` between `0.0` and `1.0`: {}",
                         err
                     ),
@@ -510,7 +518,7 @@ fn main() {
         } else if let Some(param) = arg.strip_prefix("-Zmiri-report-progress=") {
             let interval = match param.parse::<u32>() {
                 Ok(i) => i,
-                Err(err) => panic!("-Zmiri-report-progress requires a `u32`: {}", err),
+                Err(err) => show_error!("-Zmiri-report-progress requires a `u32`: {}", err),
             };
             miri_config.report_progress = Some(interval);
         } else if let Some(param) = arg.strip_prefix("-Zmiri-measureme=") {
@@ -520,7 +528,7 @@ fn main() {
                 "0" => BacktraceStyle::Off,
                 "1" => BacktraceStyle::Short,
                 "full" => BacktraceStyle::Full,
-                _ => panic!("-Zmiri-backtrace may only be 0, 1, or full"),
+                _ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"),
             };
         } else {
             // Forward to rustc.