From 2bd352376b5fe9877148d0f7a66ccdc41446d9c2 Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Wed, 31 Jul 2024 11:47:07 -0700 Subject: [PATCH] Add -Zerror-metrics=PATH to save diagnostic metadata to disk --- .gitignore | 1 + compiler/rustc_driver_impl/src/lib.rs | 49 +++++++++--- compiler/rustc_session/src/options.rs | 2 + tests/run-make/dump-ice-to-disk/rmake.rs | 86 ++++++++++++++++++---- tests/rustdoc-ui/intra-doc/warning-crlf.rs | 52 ++++++------- 5 files changed, 139 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index aabec0988a99e..f2cdd8762f230 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 627a0ebb4e5e3..19bbdd8a9b5c0 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(), @@ -357,7 +360,11 @@ fn run_compiler( // printing some information without compiling, or exiting immediately // after parsing, etc. let early_exit = || { - if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) } + if let Some(guar) = sess.dcx().has_errors() { + Err(guar) + } else { + Ok(()) + } }; // This implements `-Whelp`. It should be handled very early, like @@ -567,7 +574,11 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col fn show_md_content_with_pager(content: &str, color: ColorConfig) { let mut fallback_to_println = false; let pager_name = env::var_os("PAGER").unwrap_or_else(|| { - if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") } + if cfg!(windows) { + OsString::from("more.com") + } else { + OsString::from("less") + } }); let mut cmd = Command::new(&pager_name); @@ -1306,25 +1317,43 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { static ICE_PATH: OnceLock> = 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 { + ice_path_with_config(None) +} + +fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option { + 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.error_metrics.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.error_metrics.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 bf54aae1cfeb0..71edf9a7219d1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1714,6 +1714,8 @@ options! { "emit the bc module with thin LTO info (default: yes)"), enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], "enforce the type length limit when monomorphizing instances in codegen"), + error_metrics: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "stores metrics about the errors being emitted by rustc to disk"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], "export symbols from executables, as if they were dynamic libraries"), external_clangrt: bool = (false, parse_bool, [UNTRACKED], diff --git a/tests/run-make/dump-ice-to-disk/rmake.rs b/tests/run-make/dump-ice-to-disk/rmake.rs index 2fb5c825064b0..282604aff84c6 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 -Zerror-metrics 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. + + test_error_metrics_flag(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 test_error_metrics_flag(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!("-Zerror-metrics={}", 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!("-Zerror-metrics={}", cwd().display()); + let real_dir = cwd().join("actually_put_ice_here"); + rfs::create_dir(real_dir.clone()).unwrap(); + 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() { diff --git a/tests/rustdoc-ui/intra-doc/warning-crlf.rs b/tests/rustdoc-ui/intra-doc/warning-crlf.rs index ab096b860f819..9d3a4673c9d01 100644 --- a/tests/rustdoc-ui/intra-doc/warning-crlf.rs +++ b/tests/rustdoc-ui/intra-doc/warning-crlf.rs @@ -1,26 +1,26 @@ -// ignore-tidy-cr -//@ check-pass - -// This file checks the spans of intra-link warnings in a file with CRLF line endings. The -// .gitattributes file in this directory should enforce it. - -/// [error] -pub struct A; -//~^^ WARNING `error` - -/// -/// docs [error1] -//~^ WARNING `error1` - -/// docs [error2] -/// -pub struct B; -//~^^^ WARNING `error2` - -/** - * This is a multi-line comment. - * - * It also has an [error]. - */ -pub struct C; -//~^^^ WARNING `error` +// ignore-tidy-cr +//@ check-pass + +// This file checks the spans of intra-link warnings in a file with CRLF line endings. The +// .gitattributes file in this directory should enforce it. + +/// [error] +pub struct A; +//~^^ WARNING `error` + +/// +/// docs [error1] +//~^ WARNING `error1` + +/// docs [error2] +/// +pub struct B; +//~^^^ WARNING `error2` + +/** + * This is a multi-line comment. + * + * It also has an [error]. + */ +pub struct C; +//~^^^ WARNING `error`