]> git.lizzy.rs Git - rust.git/blobdiff - cargo-miri/bin.rs
no need for an exhaustive enum of subcommands
[rust.git] / cargo-miri / bin.rs
index 19d235cf67d71cb327160aaa751d277d325c4187..2ab854b906a4f0fd27e2bbd8badbcb41d064bd95 100644 (file)
@@ -1,18 +1,22 @@
+#![feature(let_else)]
+#![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq)]
+
+mod version;
+
 use std::env;
-use std::ffi::OsString;
+use std::ffi::{OsStr, OsString};
 use std::fmt::Write as _;
 use std::fs::{self, File};
 use std::io::{self, BufRead, BufReader, BufWriter, Read, Write};
 use std::iter::TakeWhile;
 use std::ops::Not;
 use std::path::{Path, PathBuf};
-use std::process::Command;
-
-use serde::{Deserialize, Serialize};
+use std::process::{self, Command};
 
 use rustc_version::VersionMeta;
+use serde::{Deserialize, Serialize};
 
-const XARGO_MIN_VERSION: (u32, u32, u32) = (0, 3, 22);
+use version::*;
 
 const CARGO_MIRI_HELP: &str = r#"Runs binary crates and tests in Miri
 
@@ -20,8 +24,8 @@
     cargo miri [subcommand] [<cargo options>...] [--] [<program/test suite options>...]
 
 Subcommands:
-    run                      Run binaries
-    test                     Run tests
+    run, r                   Run binaries
+    test, t                  Run tests
     setup                    Only perform automatic setup, but without asking questions (for getting a proper libstd)
 
 The cargo options are exactly the same as for `cargo run` and `cargo test`, respectively.
     cargo miri test -- test-suite-filter
 "#;
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug)]
 enum MiriCommand {
-    Run,
-    Test,
+    /// Our own special 'setup' command.
     Setup,
+    /// A command to be forwarded to cargo.
+    Forward(String),
 }
 
 /// The information to run a crate with the given environment.
@@ -95,6 +100,9 @@ fn show_version() {
     // Only use `option_env` on vergen variables to ensure the build succeeds
     // when vergen failed to find the git info.
     if let Some(sha) = option_env!("VERGEN_GIT_SHA_SHORT") {
+        // This `unwrap` can never fail because if VERGEN_GIT_SHA_SHORT exists, then so does
+        // VERGEN_GIT_COMMIT_DATE.
+        #[allow(clippy::option_env_unwrap)]
         write!(&mut version, " ({} {})", sha, option_env!("VERGEN_GIT_COMMIT_DATE").unwrap())
             .unwrap();
     }
@@ -112,40 +120,58 @@ fn has_arg_flag(name: &str) -> bool {
     args.any(|val| val == name)
 }
 
-/// Yields all values of command line flag `name`.
-struct ArgFlagValueIter<'a> {
-    args: TakeWhile<env::Args, fn(&String) -> bool>,
+/// Yields all values of command line flag `name` as `Ok(arg)`, and all other arguments except
+/// the flag as `Err(arg)`. (The flag `name` itself is not yielded at all, only its values are.)
+struct ArgSplitFlagValue<'a, I> {
+    args: TakeWhile<I, fn(&String) -> bool>,
     name: &'a str,
 }
 
-impl<'a> ArgFlagValueIter<'a> {
-    fn new(name: &'a str) -> Self {
+impl<'a, I: Iterator<Item = String>> ArgSplitFlagValue<'a, I> {
+    fn new(args: I, name: &'a str) -> Self {
         Self {
             // Stop searching at `--`.
-            args: env::args().take_while(|val| val != "--"),
+            args: args.take_while(|val| val != "--"),
             name,
         }
     }
 }
 
-impl Iterator for ArgFlagValueIter<'_> {
-    type Item = String;
+impl<I: Iterator<Item = String>> Iterator for ArgSplitFlagValue<'_, I> {
+    type Item = Result<String, String>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            let arg = self.args.next()?;
-            if !arg.starts_with(self.name) {
-                continue;
-            }
+        let arg = self.args.next()?;
+        if let Some(suffix) = arg.strip_prefix(self.name) {
             // Strip leading `name`.
-            let suffix = &arg[self.name.len()..];
             if suffix.is_empty() {
                 // This argument is exactly `name`; the next one is the value.
-                return self.args.next();
-            } else if suffix.starts_with('=') {
+                return self.args.next().map(Ok);
+            } else if let Some(suffix) = suffix.strip_prefix('=') {
                 // This argument is `name=value`; get the value.
-                // Strip leading `=`.
-                return Some(suffix[1..].to_owned());
+                return Some(Ok(suffix.to_owned()));
+            }
+        }
+        Some(Err(arg))
+    }
+}
+
+/// Yields all values of command line flag `name`.
+struct ArgFlagValueIter<'a>(ArgSplitFlagValue<'a, env::Args>);
+
+impl<'a> ArgFlagValueIter<'a> {
+    fn new(name: &'a str) -> Self {
+        Self(ArgSplitFlagValue::new(env::args(), name))
+    }
+}
+
+impl Iterator for ArgFlagValueIter<'_> {
+    type Item = String;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        loop {
+            if let Ok(value) = self.0.next()? {
+                return Some(value);
             }
         }
     }
@@ -213,7 +239,7 @@ fn exec(mut cmd: Command) {
 /// If it fails, fail this process with the same exit code.
 /// Otherwise, continue.
 fn exec_with_pipe(mut cmd: Command, input: &[u8]) {
-    cmd.stdin(std::process::Stdio::piped());
+    cmd.stdin(process::Stdio::piped());
     let mut child = cmd.spawn().expect("failed to spawn process");
     {
         let stdin = child.stdin.as_mut().expect("failed to open stdin");
@@ -234,7 +260,7 @@ fn xargo_version() -> Option<(u32, u32, u32)> {
     let line = out
         .stderr
         .lines()
-        .nth(0)
+        .next()
         .expect("malformed `xargo --version` output: not at least one line")
         .expect("malformed `xargo --version` output: error reading first line");
     let (name, version) = {
@@ -264,7 +290,7 @@ fn xargo_version() -> Option<(u32, u32, u32)> {
         .expect("malformed `xargo --version` output: not a patch version piece")
         .parse()
         .expect("malformed `xargo --version` output: patch version is not an integer");
-    if !version_pieces.next().is_none() {
+    if version_pieces.next().is_some() {
         panic!("malformed `xargo --version` output: more than three pieces in version");
     }
     Some((major, minor, patch))
@@ -290,17 +316,35 @@ fn ask_to_run(mut cmd: Command, ask: bool, text: &str) {
         println!("Running `{:?}` to {}.", cmd, text);
     }
 
-    if cmd.status().expect(&format!("failed to execute {:?}", cmd)).success().not() {
+    if cmd.status().unwrap_or_else(|_| panic!("failed to execute {:?}", cmd)).success().not() {
         show_error(format!("failed to {}", text));
     }
 }
 
+/// Writes the given content to the given file *cross-process atomically*, in the sense that another
+/// process concurrently reading that file will see either the old content or the new content, but
+/// not some intermediate (e.g., empty) state.
+///
+/// We assume no other parts of this same process are trying to read or write that file.
+fn write_to_file(filename: &Path, content: &str) {
+    // Create a temporary file with the desired contents.
+    let mut temp_filename = filename.as_os_str().to_os_string();
+    temp_filename.push(&format!(".{}", std::process::id()));
+    let mut temp_file = File::create(&temp_filename).unwrap();
+    temp_file.write_all(content.as_bytes()).unwrap();
+    drop(temp_file);
+
+    // Move file to the desired location.
+    fs::rename(temp_filename, filename).unwrap();
+}
+
 /// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
 /// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
 /// done all this already.
-fn setup(subcommand: MiriCommand) {
+fn setup(subcommand: &MiriCommand) {
+    let only_setup = matches!(subcommand, MiriCommand::Setup);
     if std::env::var_os("MIRI_SYSROOT").is_some() {
-        if subcommand == MiriCommand::Setup {
+        if only_setup {
             println!("WARNING: MIRI_SYSROOT already set, not doing anything.")
         }
         return;
@@ -308,7 +352,7 @@ fn setup(subcommand: MiriCommand) {
 
     // Subcommands other than `setup` will do a setup if necessary, but
     // interactively confirm first.
-    let ask_user = subcommand != MiriCommand::Setup;
+    let ask_user = !only_setup;
 
     // First, we need xargo.
     if xargo_version().map_or(true, |v| v < XARGO_MIN_VERSION) {
@@ -321,8 +365,11 @@ fn setup(subcommand: MiriCommand) {
         ask_to_run(cmd, ask_user, "install a recent enough xargo");
     }
 
-    // Determine where the rust sources are located.  `XARGO_RUST_SRC` env var trumps everything.
-    let rust_src = match std::env::var_os("XARGO_RUST_SRC") {
+    // Determine where the rust sources are located.  The env vars manually setting the source
+    // (`MIRI_LIB_SRC`, `XARGO_RUST_SRC`) trump auto-detection.
+    let rust_src_env_var =
+        std::env::var_os("MIRI_LIB_SRC").or_else(|| std::env::var_os("XARGO_RUST_SRC"));
+    let rust_src = match rust_src_env_var {
         Some(path) => {
             let path = PathBuf::from(path);
             // Make path absolute if possible.
@@ -330,12 +377,15 @@ fn setup(subcommand: MiriCommand) {
         }
         None => {
             // Check for `rust-src` rustup component.
-            let sysroot = miri()
-                .args(&["--print", "sysroot"])
-                .output()
-                .expect("failed to determine sysroot")
-                .stdout;
-            let sysroot = std::str::from_utf8(&sysroot).unwrap();
+            let output =
+                miri().args(&["--print", "sysroot"]).output().expect("failed to determine sysroot");
+            if !output.status.success() {
+                show_error(format!(
+                    "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'));
             // Check for `$SYSROOT/lib/rustlib/src/rust/library`; test if that contains `std/Cargo.toml`.
             let rustup_src =
@@ -356,6 +406,13 @@ fn setup(subcommand: MiriCommand) {
     if !rust_src.exists() {
         show_error(format!("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!(
+            "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.
     // We will do this work in whatever is a good cache dir for this platform.
@@ -364,26 +421,25 @@ fn setup(subcommand: MiriCommand) {
     if !dir.exists() {
         fs::create_dir_all(&dir).unwrap();
     }
-    // The interesting bit: Xargo.toml
-    File::create(dir.join("Xargo.toml"))
-        .unwrap()
-        .write_all(
-            br#"
+    // The interesting bit: Xargo.toml (only needs content if we actually need std)
+    let xargo_toml = if std::env::var_os("MIRI_NO_STD").is_some() {
+        ""
+    } else {
+        r#"
 [dependencies.std]
 default_features = false
 # We support unwinding, so enable that panic runtime.
 features = ["panic_unwind", "backtrace"]
 
 [dependencies.test]
-"#,
-        )
-        .unwrap();
+"#
+    };
+    write_to_file(&dir.join("Xargo.toml"), xargo_toml);
     // The boring bits: a dummy project for xargo.
     // FIXME: With xargo-check, can we avoid doing this?
-    File::create(dir.join("Cargo.toml"))
-        .unwrap()
-        .write_all(
-            br#"
+    write_to_file(
+        &dir.join("Cargo.toml"),
+        r#"
 [package]
 name = "miri-xargo"
 description = "A dummy project for building libstd with xargo."
@@ -392,9 +448,8 @@ fn setup(subcommand: MiriCommand) {
 [lib]
 path = "lib.rs"
 "#,
-        )
-        .unwrap();
-    File::create(dir.join("lib.rs")).unwrap();
+    );
+    write_to_file(&dir.join("lib.rs"), "#![no_std]");
 
     // Determine architectures.
     // We always need to set a target so rustc bootstrap can tell apart host from target crates.
@@ -443,15 +498,70 @@ fn setup(subcommand: MiriCommand) {
     let sysroot = if target == &host { dir.join("HOST") } else { PathBuf::from(dir) };
     std::env::set_var("MIRI_SYSROOT", &sysroot); // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags
     // Figure out what to print.
-    let print_sysroot = subcommand == MiriCommand::Setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
+    let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
     if print_sysroot {
         // Print just the sysroot and nothing else; this way we do not need any escaping.
         println!("{}", sysroot.display());
-    } else if subcommand == MiriCommand::Setup {
+    } else if only_setup {
         println!("A libstd for Miri is now available in `{}`.", sysroot.display());
     }
 }
 
+#[derive(Deserialize)]
+struct Metadata {
+    target_directory: PathBuf,
+    workspace_members: Vec<String>,
+}
+
+fn get_cargo_metadata() -> Metadata {
+    let mut cmd = cargo();
+    // `-Zunstable-options` is required by `--config`.
+    cmd.args(["metadata", "--no-deps", "--format-version=1", "-Zunstable-options"]);
+    // The `build.target-dir` config can be passed by `--config` flags, so forward them to
+    // `cargo metadata`.
+    let config_flag = "--config";
+    for arg in ArgSplitFlagValue::new(
+        env::args().skip(3), // skip the program name, "miri" and "run" / "test"
+        config_flag,
+    )
+    // Only look at `Ok`
+    .flatten()
+    {
+        cmd.arg(config_flag).arg(arg);
+    }
+    let mut child = cmd
+        .stdin(process::Stdio::null())
+        .stdout(process::Stdio::piped())
+        .spawn()
+        .expect("failed ro run `cargo metadata`");
+    // Check this `Result` after `status.success()` is checked, so we don't print the error
+    // to stderr if `cargo metadata` is also printing to stderr.
+    let metadata: Result<Metadata, _> = serde_json::from_reader(child.stdout.take().unwrap());
+    let status = child.wait().expect("failed to wait for `cargo metadata` to exit");
+    if !status.success() {
+        std::process::exit(status.code().unwrap_or(-1));
+    }
+    metadata.unwrap_or_else(|e| show_error(format!("invalid `cargo metadata` output: {}", e)))
+}
+
+/// Pulls all the crates in this workspace from the cargo metadata.
+/// Workspace members are emitted like "miri 0.1.0 (path+file:///path/to/miri)"
+/// Additionally, somewhere between cargo metadata and TyCtxt, '-' gets replaced with '_' so we
+/// make that same transformation here.
+fn local_crates(metadata: &Metadata) -> String {
+    assert!(!metadata.workspace_members.is_empty());
+    let mut local_crates = String::new();
+    for member in &metadata.workspace_members {
+        let name = member.split(' ').next().unwrap();
+        let name = name.replace('-', "_");
+        local_crates.push_str(&name);
+        local_crates.push(',');
+    }
+    local_crates.pop(); // Remove the trailing ','
+
+    local_crates
+}
+
 fn phase_cargo_miri(mut args: env::Args) {
     // Check for version and help flags even when invoked as `cargo-miri`.
     if has_arg_flag("--help") || has_arg_flag("-h") {
@@ -466,19 +576,22 @@ fn phase_cargo_miri(mut args: env::Args) {
     // Require a subcommand before any flags.
     // We cannot know which of those flags take arguments and which do not,
     // so we cannot detect subcommands later.
-    let subcommand = match args.next().as_deref() {
-        Some("test") => MiriCommand::Test,
-        Some("run") => MiriCommand::Run,
-        Some("setup") => MiriCommand::Setup,
+    let Some(subcommand) = args.next() else {
+        show_error(format!("`cargo miri` needs to be called with a subcommand (`run`, `test`)"));
+    };
+    let subcommand = match &*subcommand {
+        "setup" => MiriCommand::Setup,
+        "test" | "t" | "run" | "r" => MiriCommand::Forward(subcommand),
         // Invalid command.
-        _ => show_error(format!(
-            "`cargo miri` supports the following subcommands: `run`, `test`, and `setup`."
-        )),
+        _ =>
+            show_error(format!(
+                "`cargo miri` supports the following subcommands: `run`, `test`, and `setup`."
+            )),
     };
     let verbose = has_arg_flag("-v");
 
     // We always setup.
-    setup(subcommand);
+    setup(&subcommand);
 
     // Invoke actual cargo for the job, but with different flags.
     // We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
@@ -488,8 +601,7 @@ fn phase_cargo_miri(mut args: env::Args) {
     // harder.
     let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
     let cargo_cmd = match subcommand {
-        MiriCommand::Test => "test",
-        MiriCommand::Run => "run",
+        MiriCommand::Forward(s) => s,
         MiriCommand::Setup => return, // `cargo miri setup` stops here.
     };
     let mut cmd = cargo();
@@ -510,8 +622,34 @@ fn phase_cargo_miri(mut args: env::Args) {
         &host
     };
 
-    // Forward all further arguments to cargo.
-    cmd.args(args);
+    let mut target_dir = None;
+
+    // Forward all arguments before `--` other than `--target-dir` and its value to Cargo.
+    for arg in ArgSplitFlagValue::new(&mut args, "--target-dir") {
+        match arg {
+            Ok(value) => {
+                if target_dir.is_some() {
+                    show_error(format!("`--target-dir` is provided more than once"));
+                }
+                target_dir = Some(value.into());
+            }
+            Err(arg) => {
+                cmd.arg(arg);
+            }
+        }
+    }
+
+    let metadata = get_cargo_metadata();
+
+    // Detect the target directory if it's not specified via `--target-dir`.
+    let target_dir = target_dir.get_or_insert_with(|| metadata.target_directory.clone());
+
+    // Set `--target-dir` to `miri` inside the original target directory.
+    target_dir.push("miri");
+    cmd.arg("--target-dir").arg(target_dir);
+
+    // Forward all further arguments after `--` to cargo.
+    cmd.arg("--").args(args);
 
     // Set `RUSTC_WRAPPER` to ourselves.  Cargo will prepend that binary to its usual invocation,
     // i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
@@ -522,6 +660,14 @@ fn phase_cargo_miri(mut args: env::Args) {
         );
     }
     cmd.env("RUSTC_WRAPPER", &cargo_miri_path);
+    // Having both `RUSTC_WRAPPER` and `RUSTC` set does some odd things, so let's avoid that.
+    // See <https://github.com/rust-lang/miri/issues/2238>.
+    if env::var_os("RUSTC").is_some() && env::var_os("MIRI").is_none() {
+        println!(
+            "WARNING: Ignoring `RUSTC` environment variable; set `MIRI` if you want to control the binary used as the driver."
+        );
+    }
+    cmd.env_remove("RUSTC");
 
     let runner_env_name =
         |triple: &str| format!("CARGO_TARGET_{}_RUNNER", triple.to_uppercase().replace('-', "_"));
@@ -536,6 +682,8 @@ fn phase_cargo_miri(mut args: env::Args) {
     // Set rustdoc to us as well, so we can run doctests.
     cmd.env("RUSTDOC", &cargo_miri_path);
 
+    cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata));
+
     // Run cargo.
     if verbose {
         eprintln!("[cargo-miri miri] RUSTC_WRAPPER={:?}", cargo_miri_path);
@@ -594,7 +742,7 @@ fn out_filename(prefix: &str, suffix: &str) -> PathBuf {
                 get_arg_flag_value("--crate-name").unwrap(),
                 // This is technically a `-C` flag but the prefix seems unique enough...
                 // (and cargo passes this before the filename so it should be unique)
-                get_arg_flag_value("extra-filename").unwrap_or(String::new()),
+                get_arg_flag_value("extra-filename").unwrap_or_default(),
                 suffix,
             ));
             path
@@ -694,11 +842,10 @@ fn out_filename(prefix: &str, suffix: &str) -> PathBuf {
         // Forward arguments, but remove "link" from "--emit" to make this a check-only build.
         let emit_flag = "--emit";
         while let Some(arg) = args.next() {
-            if arg.starts_with(emit_flag) {
+            if let Some(val) = arg.strip_prefix(emit_flag) {
                 // Patch this argument. First, extract its value.
-                let val = &arg[emit_flag.len()..];
-                assert!(val.starts_with("="), "`cargo` should pass `--emit=X` as one argument");
-                let val = &val[1..];
+                let val =
+                    val.strip_prefix('=').expect("`cargo` should pass `--emit=X` as one argument");
                 let mut val: Vec<_> = val.split(',').collect();
                 // Now make sure "link" is not in there, but "metadata" is.
                 if let Some(i) = val.iter().position(|&s| s == "link") {
@@ -823,12 +970,10 @@ fn phase_runner(binary: &Path, binary_args: env::Args, phase: RunnerPhase) {
     while let Some(arg) = args.next() {
         if arg == "--extern" {
             forward_patched_extern_arg(&mut args, &mut cmd);
-        } else if arg.starts_with(error_format_flag) {
-            let suffix = &arg[error_format_flag.len()..];
+        } else if let Some(suffix) = arg.strip_prefix(error_format_flag) {
             assert!(suffix.starts_with('='));
             // Drop this argument.
-        } else if arg.starts_with(json_flag) {
-            let suffix = &arg[json_flag.len()..];
+        } else if let Some(suffix) = arg.strip_prefix(json_flag) {
             assert!(suffix.starts_with('='));
             // Drop this argument.
         } else {
@@ -900,17 +1045,17 @@ fn phase_rustdoc(fst_arg: &str, mut args: env::Args) {
     }
 
     if crossmode {
-        show_error(format!("cross-interpreting doc-tests is not currently supported by Miri."));
+        show_error(format!("cross-interpreting doctests is not currently supported by Miri."));
     }
 
-    // Doc-tests of `proc-macro` crates (and their dependencies) are always built for the host,
+    // Doctests of `proc-macro` crates (and their dependencies) are always built for the host,
     // so we are not able to run them in Miri.
     if ArgFlagValueIter::new("--crate-type").any(|crate_type| crate_type == "proc-macro") {
-        eprintln!("Running doc-tests of `proc-macro` crates is not currently supported by Miri.");
+        eprintln!("Running doctests of `proc-macro` crates is not currently supported by Miri.");
         return;
     }
 
-    // For each doc-test, rustdoc starts two child processes: first the test is compiled,
+    // For each doctest, rustdoc starts two child processes: first the test is compiled,
     // then the produced executable is invoked. We want to reroute both of these to cargo-miri,
     // such that the first time we'll enter phase_cargo_rustc, and phase_cargo_runner second.
     //
@@ -926,6 +1071,8 @@ fn phase_rustdoc(fst_arg: &str, mut args: env::Args) {
 
     // rustdoc needs to know the right sysroot.
     forward_miri_sysroot(&mut cmd);
+    // make sure the 'miri' flag is set for rustdoc
+    cmd.arg("--cfg").arg("miri");
 
     // Make rustdoc call us back.
     let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
@@ -946,6 +1093,19 @@ fn main() {
     // Skip binary name.
     args.next().unwrap();
 
+    // Dispatch to `cargo-miri` phase. There are four phases:
+    // - When we are called via `cargo miri`, we run as the frontend and invoke the underlying
+    //   cargo. We set RUSTDOC, RUSTC_WRAPPER and CARGO_TARGET_RUNNER to ourselves.
+    // - When we are executed due to RUSTDOC, we run rustdoc and set both `--test-builder` and
+    //   `--runtool` to ourselves.
+    // - When we are executed due to RUSTC_WRAPPER (or as the rustdoc test builder), we build crates
+    //   or store the flags of binary crates for later interpretation.
+    // - When we are executed due to CARGO_TARGET_RUNNER (or as the rustdoc runtool), we start
+    //   interpretation based on the flags that were stored earlier.
+    //
+    // Additionally, we also set ourselves as RUSTC when calling xargo to build the sysroot, which
+    // has to be treated slightly differently than when we build regular crates.
+
     // Dispatch running as part of sysroot compilation.
     if env::var_os("MIRI_CALLED_FROM_XARGO").is_some() {
         phase_rustc(args, RustcPhase::Setup);
@@ -977,14 +1137,6 @@ fn main() {
         return;
     }
 
-    // Dispatch to `cargo-miri` phase. There are three phases:
-    // - When we are called via `cargo miri`, we run as the frontend and invoke the underlying
-    //   cargo. We set RUSTC_WRAPPER and CARGO_TARGET_RUNNER to ourselves.
-    // - When we are executed due to RUSTC_WRAPPER, we build crates or store the flags of
-    //   binary crates for later interpretation.
-    // - When we are executed due to CARGO_TARGET_RUNNER, we start interpretation based on the
-    //   flags that were stored earlier.
-    // On top of that, we are also called as RUSTDOC, but that is just a stub currently.
     match args.next().as_deref() {
         Some("miri") => phase_cargo_miri(args),
         Some("rustc") => phase_rustc(args, RustcPhase::Build),
@@ -1005,8 +1157,9 @@ fn main() {
                 ));
             }
         }
-        _ => show_error(format!(
-            "`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`"
-        )),
+        _ =>
+            show_error(format!(
+                "`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`"
+            )),
     }
 }