summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs37
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--tests/run-make/dump-ice-to-disk/rmake.rs86
4 files changed, 103 insertions, 23 deletions
diff --git a/.gitignore b/.gitignore
index aabec0988a9..f2cdd8762f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ Session.vim
 *.iml
 .vscode
 .project
+.vim/
 .favorites.json
 .settings/
 .vs/
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 627a0ebb4e5..b014fc2dc58 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -51,7 +51,8 @@ use rustc_metadata::creader::MetadataLoader;
 use rustc_metadata::locator;
 use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
 use rustc_session::config::{
-    nightly_options, ErrorOutputType, Input, OutFileName, OutputType, CG_OPTIONS, Z_OPTIONS,
+    nightly_options, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, CG_OPTIONS,
+    Z_OPTIONS,
 };
 use rustc_session::getopts::{self, Matches};
 use rustc_session::lint::{Lint, LintId};
@@ -301,6 +302,8 @@ fn run_compiler(
     let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) };
 
     let sopts = config::build_session_options(&mut default_early_dcx, &matches);
+    // fully initialize ice path static once unstable options are available as context
+    let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone();
 
     if let Some(ref code) = matches.opt_str("explain") {
         handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color);
@@ -315,7 +318,7 @@ fn run_compiler(
         input: Input::File(PathBuf::new()),
         output_file: ofile,
         output_dir: odir,
-        ice_file: ice_path().clone(),
+        ice_file,
         file_loader,
         locale_resources: DEFAULT_LOCALE_RESOURCES,
         lint_caps: Default::default(),
@@ -1306,25 +1309,43 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
 
 static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
 
+// This function should only be called from the ICE hook.
+//
+// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the
+// initialization process to properly initialize the ICE_PATH static based on parsed CLI flags.
+//
+// Subsequent calls to either function will then return the proper ICE path as configured by
+// the environment and cli flags
 fn ice_path() -> &'static Option<PathBuf> {
+    ice_path_with_config(None)
+}
+
+fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option<PathBuf> {
+    if ICE_PATH.get().is_some() && config.is_some() && cfg!(debug_assertions) {
+        tracing::warn!(
+            "ICE_PATH has already been initialized -- files may be emitted at unintended paths"
+        )
+    }
+
     ICE_PATH.get_or_init(|| {
         if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
             return None;
         }
-        if let Some(s) = std::env::var_os("RUST_BACKTRACE")
-            && s == "0"
-        {
-            return None;
-        }
         let mut path = match std::env::var_os("RUSTC_ICE") {
             Some(s) => {
                 if s == "0" {
                     // Explicitly opting out of writing ICEs to disk.
                     return None;
                 }
+                if let Some(unstable_opts) = config && unstable_opts.metrics_dir.is_some() {
+                    tracing::warn!("ignoring -Zerror-metrics in favor of RUSTC_ICE for destination of ICE report files");
+                }
                 PathBuf::from(s)
             }
-            None => std::env::current_dir().unwrap_or_default(),
+            None => config
+                .and_then(|unstable_opts| unstable_opts.metrics_dir.to_owned())
+                .or_else(|| std::env::current_dir().ok())
+                .unwrap_or_default(),
         };
         let now: OffsetDateTime = SystemTime::now().into();
         let file_now = now
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index bf54aae1cfe..4b87f5d62b2 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1827,6 +1827,8 @@ options! {
         the same values as the target option of the same name"),
     meta_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather metadata statistics (default: no)"),
+    metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
+        "stores metrics about the errors being emitted by rustc to disk"),
     mir_emit_retag: bool = (false, parse_bool, [TRACKED],
         "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
         (default: no)"),
diff --git a/tests/run-make/dump-ice-to-disk/rmake.rs b/tests/run-make/dump-ice-to-disk/rmake.rs
index 2fb5c825064..a64103fa516 100644
--- a/tests/run-make/dump-ice-to-disk/rmake.rs
+++ b/tests/run-make/dump-ice-to-disk/rmake.rs
@@ -4,6 +4,10 @@
 // or full.
 // - Check that disabling ICE logging results in zero files created.
 // - Check that the ICE files contain some of the expected strings.
+// - exercise the -Zmetrics-dir nightly flag
+// - verify what happens when both the nightly flag and env variable are set
+// - test the RUST_BACKTRACE=0 behavior against the file creation
+
 // See https://github.com/rust-lang/rust/pull/108714
 
 use run_make_support::{cwd, has_extension, has_prefix, rfs, rustc, shallow_find_files};
@@ -11,8 +15,8 @@ use run_make_support::{cwd, has_extension, has_prefix, rfs, rustc, shallow_find_
 fn main() {
     rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
     let default = get_text_from_ice(".").lines().count();
-    clear_ice_files();
 
+    clear_ice_files();
     rustc().env("RUSTC_ICE", cwd()).input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
     let ice_text = get_text_from_ice(cwd());
     let default_set = ice_text.lines().count();
@@ -25,7 +29,28 @@ fn main() {
         ice_files.first().and_then(|f| f.file_name()).and_then(|n| n.to_str()).unwrap();
     // Ensure that the ICE dump path doesn't contain `:`, because they cause problems on Windows.
     assert!(!ice_file_name.contains(":"), "{ice_file_name}");
+    assert_eq!(default, default_set);
+    assert!(default > 0);
+    // Some of the expected strings in an ICE file should appear.
+    assert!(content.contains("thread 'rustc' panicked at"));
+    assert!(content.contains("stack backtrace:"));
+
+    test_backtrace_short(default);
+    test_backtrace_full(default);
+    test_backtrace_disabled(default);
+
+    clear_ice_files();
+    // The ICE dump is explicitly disabled. Therefore, this should produce no files.
+    rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
+    let ice_files = shallow_find_files(cwd(), |path| {
+        has_prefix(path, "rustc-ice") && has_extension(path, "txt")
+    });
+    assert!(ice_files.is_empty()); // There should be 0 ICE files.
+
+    metrics_dir(default);
+}
 
+fn test_backtrace_short(baseline: usize) {
     clear_ice_files();
     rustc()
         .env("RUSTC_ICE", cwd())
@@ -34,6 +59,11 @@ fn main() {
         .arg("-Ztreat-err-as-bug=1")
         .run_fail();
     let short = get_text_from_ice(cwd()).lines().count();
+    // backtrace length in dump shouldn't be changed by RUST_BACKTRACE
+    assert_eq!(short, baseline);
+}
+
+fn test_backtrace_full(baseline: usize) {
     clear_ice_files();
     rustc()
         .env("RUSTC_ICE", cwd())
@@ -42,23 +72,49 @@ fn main() {
         .arg("-Ztreat-err-as-bug=1")
         .run_fail();
     let full = get_text_from_ice(cwd()).lines().count();
+    // backtrace length in dump shouldn't be changed by RUST_BACKTRACE
+    assert_eq!(full, baseline);
+}
+
+fn test_backtrace_disabled(baseline: usize) {
     clear_ice_files();
+    rustc()
+        .env("RUSTC_ICE", cwd())
+        .input("lib.rs")
+        .env("RUST_BACKTRACE", "0")
+        .arg("-Ztreat-err-as-bug=1")
+        .run_fail();
+    let disabled = get_text_from_ice(cwd()).lines().count();
+    // backtrace length in dump shouldn't be changed by RUST_BACKTRACE
+    assert_eq!(disabled, baseline);
+}
 
-    // The ICE dump is explicitly disabled. Therefore, this should produce no files.
-    rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail();
-    let ice_files = shallow_find_files(cwd(), |path| {
-        has_prefix(path, "rustc-ice") && has_extension(path, "txt")
-    });
-    assert!(ice_files.is_empty()); // There should be 0 ICE files.
+fn metrics_dir(baseline: usize) {
+    test_flag_only(baseline);
+    test_flag_and_env(baseline);
+}
 
-    // The line count should not change.
-    assert_eq!(short, default_set);
-    assert_eq!(short, default);
-    assert_eq!(full, default_set);
-    assert!(default > 0);
-    // Some of the expected strings in an ICE file should appear.
-    assert!(content.contains("thread 'rustc' panicked at"));
-    assert!(content.contains("stack backtrace:"));
+fn test_flag_only(baseline: usize) {
+    clear_ice_files();
+    let metrics_arg = format!("-Zmetrics-dir={}", cwd().display());
+    rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").arg(metrics_arg).run_fail();
+    let output = get_text_from_ice(cwd()).lines().count();
+    assert_eq!(output, baseline);
+}
+
+fn test_flag_and_env(baseline: usize) {
+    clear_ice_files();
+    let metrics_arg = format!("-Zmetrics-dir={}", cwd().display());
+    let real_dir = cwd().join("actually_put_ice_here");
+    rfs::create_dir(real_dir.clone());
+    rustc()
+        .input("lib.rs")
+        .env("RUSTC_ICE", real_dir.clone())
+        .arg("-Ztreat-err-as-bug=1")
+        .arg(metrics_arg)
+        .run_fail();
+    let output = get_text_from_ice(real_dir).lines().count();
+    assert_eq!(output, baseline);
 }
 
 fn clear_ice_files() {