diff options
Diffstat (limited to 'compiler/rustc_session')
| -rw-r--r-- | compiler/rustc_session/src/config.rs | 400 | ||||
| -rw-r--r-- | compiler/rustc_session/src/options.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_session/src/search_paths.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_session/src/session.rs | 99 |
4 files changed, 246 insertions, 276 deletions
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a9668ae4828..480d2478e81 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -5,8 +5,8 @@ pub use crate::options::*; use crate::search_paths::SearchPath; use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; -use crate::{early_error, early_warn, Session}; use crate::{lint, HashStableContext}; +use crate::{EarlyErrorHandler, Session}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -1389,6 +1389,7 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo } pub(super) fn build_target_config( + handler: &EarlyErrorHandler, opts: &Options, target_override: Option<Target>, sysroot: &Path, @@ -1398,27 +1399,21 @@ pub(super) fn build_target_config( |t| Ok((t, TargetWarnings::empty())), ); let (target, target_warnings) = target_result.unwrap_or_else(|e| { - early_error( - opts.error_format, - format!( - "Error loading target specification: {}. \ + handler.early_error(format!( + "Error loading target specification: {}. \ Run `rustc --print target-list` for a list of built-in targets", - e - ), - ) + e + )) }); for warning in target_warnings.warning_messages() { - early_warn(opts.error_format, warning) + handler.early_warn(warning) } if !matches!(target.pointer_width, 16 | 32 | 64) { - early_error( - opts.error_format, - format!( - "target specification was invalid: unrecognized target-pointer-width {}", - target.pointer_width - ), - ) + handler.early_error(format!( + "target specification was invalid: unrecognized target-pointer-width {}", + target.pointer_width + )) } target @@ -1654,8 +1649,8 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { } pub fn get_cmd_lint_options( + handler: &EarlyErrorHandler, matches: &getopts::Matches, - error_format: ErrorOutputType, ) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) { let mut lint_opts_with_position = vec![]; let mut describe_lints = false; @@ -1679,14 +1674,14 @@ pub fn get_cmd_lint_options( let lint_cap = matches.opt_str("cap-lints").map(|cap| { lint::Level::from_str(&cap) - .unwrap_or_else(|| early_error(error_format, format!("unknown lint level: `{cap}`"))) + .unwrap_or_else(|| handler.early_error(format!("unknown lint level: `{cap}`"))) }); (lint_opts, describe_lints, lint_cap) } /// Parses the `--color` flag. -pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { +pub fn parse_color(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> ColorConfig { match matches.opt_str("color").as_deref() { Some("auto") => ColorConfig::Auto, Some("always") => ColorConfig::Always, @@ -1694,13 +1689,10 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { None => ColorConfig::Auto, - Some(arg) => early_error( - ErrorOutputType::default(), - format!( - "argument for `--color` must be auto, \ + Some(arg) => handler.early_error(format!( + "argument for `--color` must be auto, \ always or never (instead was `{arg}`)" - ), - ), + )), } } @@ -1743,7 +1735,7 @@ impl JsonUnusedExterns { /// /// The first value returned is how to render JSON diagnostics, and the second /// is whether or not artifact notifications are enabled. -pub fn parse_json(matches: &getopts::Matches) -> JsonConfig { +pub fn parse_json(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> JsonConfig { let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = HumanReadableErrorType::Default; let mut json_color = ColorConfig::Never; @@ -1755,10 +1747,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig { // won't actually be emitting any colors and anything colorized is // embedded in a diagnostic message anyway. if matches.opt_str("color").is_some() { - early_error( - ErrorOutputType::default(), - "cannot specify the `--color` option with `--json`", - ); + handler.early_error("cannot specify the `--color` option with `--json`"); } for sub_option in option.split(',') { @@ -1769,10 +1758,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig { "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud, "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent, "future-incompat" => json_future_incompat = true, - s => early_error( - ErrorOutputType::default(), - format!("unknown `--json` option `{s}`"), - ), + s => handler.early_error(format!("unknown `--json` option `{s}`")), } } } @@ -1787,6 +1773,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig { /// Parses the `--error-format` flag. pub fn parse_error_format( + handler: &mut EarlyErrorHandler, matches: &getopts::Matches, color: ColorConfig, json_rendered: HumanReadableErrorType, @@ -1807,13 +1794,15 @@ pub fn parse_error_format( Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), - Some(arg) => early_error( - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), - format!( + Some(arg) => { + handler.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable( + HumanReadableErrorType::Default(color), + )); + handler.early_error(format!( "argument for `--error-format` must be `human`, `json` or \ `short` (instead was `{arg}`)" - ), - ), + )) + } } } else { ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) @@ -1826,10 +1815,7 @@ pub fn parse_error_format( // `--error-format=json`. This means that `--json` is specified we // should actually be emitting JSON blobs. _ if !matches.opt_strs("json").is_empty() => { - early_error( - ErrorOutputType::default(), - "using `--json` requires also using `--error-format=json`", - ); + handler.early_error("using `--json` requires also using `--error-format=json`"); } _ => {} @@ -1838,16 +1824,13 @@ pub fn parse_error_format( error_format } -pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { +pub fn parse_crate_edition(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Edition { let edition = match matches.opt_str("edition") { Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { - early_error( - ErrorOutputType::default(), - format!( - "argument for `--edition` must be one of: \ + handler.early_error(format!( + "argument for `--edition` must be one of: \ {EDITION_NAME_LIST}. (instead was `{arg}`)" - ), - ) + )) }), None => DEFAULT_EDITION, }; @@ -1862,39 +1845,42 @@ pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { } else { format!("edition {edition} is unstable and only available with -Z unstable-options") }; - early_error(ErrorOutputType::default(), msg) + handler.early_error(msg) } edition } fn check_error_format_stability( + handler: &mut EarlyErrorHandler, unstable_opts: &UnstableOptions, error_format: ErrorOutputType, json_rendered: HumanReadableErrorType, ) { if !unstable_opts.unstable_options { if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { - early_error( - ErrorOutputType::Json { pretty: false, json_rendered }, - "`--error-format=pretty-json` is unstable", - ); + handler.abort_if_error_and_set_error_format(ErrorOutputType::Json { + pretty: false, + json_rendered, + }); + handler.early_error("`--error-format=pretty-json` is unstable"); } if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = error_format { - early_error( - ErrorOutputType::Json { pretty: false, json_rendered }, - "`--error-format=human-annotate-rs` is unstable", - ); + handler.abort_if_error_and_set_error_format(ErrorOutputType::Json { + pretty: false, + json_rendered, + }); + handler.early_error("`--error-format=human-annotate-rs` is unstable"); } } } fn parse_output_types( + handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions, matches: &getopts::Matches, - error_format: ErrorOutputType, ) -> OutputTypes { let mut output_types = BTreeMap::new(); if !unstable_opts.parse_only { @@ -1908,13 +1894,10 @@ fn parse_output_types( } }; let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { - early_error( - error_format, - format!( - "unknown emission type: `{shorthand}` - expected one of: {display}", - display = OutputType::shorthands_display(), - ), - ) + handler.early_error(format!( + "unknown emission type: `{shorthand}` - expected one of: {display}", + display = OutputType::shorthands_display(), + )) }); output_types.insert(output_type, path); } @@ -1927,9 +1910,9 @@ fn parse_output_types( } fn should_override_cgus_and_disable_thinlto( + handler: &EarlyErrorHandler, output_types: &OutputTypes, matches: &getopts::Matches, - error_format: ErrorOutputType, mut codegen_units: Option<usize>, ) -> (bool, Option<usize>) { let mut disable_local_thinlto = false; @@ -1947,15 +1930,12 @@ fn should_override_cgus_and_disable_thinlto( Some(n) if n > 1 => { if matches.opt_present("o") { for ot in &incompatible { - early_warn( - error_format, - format!( - "`--emit={ot}` with `-o` incompatible with \ + handler.early_warn(format!( + "`--emit={ot}` with `-o` incompatible with \ `-C codegen-units=N` for N > 1", - ), - ); + )); } - early_warn(error_format, "resetting to default -C codegen-units=1"); + handler.early_warn("resetting to default -C codegen-units=1"); codegen_units = Some(1); disable_local_thinlto = true; } @@ -1968,27 +1948,27 @@ fn should_override_cgus_and_disable_thinlto( } if codegen_units == Some(0) { - early_error(error_format, "value for codegen units must be a positive non-zero integer"); + handler.early_error("value for codegen units must be a positive non-zero integer"); } (disable_local_thinlto, codegen_units) } -fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) { +fn check_thread_count(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) { if unstable_opts.threads == 0 { - early_error(error_format, "value for threads must be a positive non-zero integer"); + handler.early_error("value for threads must be a positive non-zero integer"); } if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() { - early_error(error_format, "optimization fuel is incompatible with multiple threads"); + handler.early_error("optimization fuel is incompatible with multiple threads"); } } fn collect_print_requests( + handler: &EarlyErrorHandler, cg: &mut CodegenOptions, unstable_opts: &mut UnstableOptions, matches: &getopts::Matches, - error_format: ErrorOutputType, ) -> Vec<PrintRequest> { let mut prints = Vec::<PrintRequest>::new(); if cg.target_cpu.as_ref().is_some_and(|s| s == "help") { @@ -2028,8 +2008,7 @@ fn collect_print_requests( if unstable_opts.unstable_options { PrintRequest::TargetSpec } else { - early_error( - error_format, + handler.early_error( "the `-Z unstable-options` flag must also be passed to \ enable the target-spec-json print option", ); @@ -2039,8 +2018,7 @@ fn collect_print_requests( if unstable_opts.unstable_options { PrintRequest::AllTargetSpecs } else { - early_error( - error_format, + handler.early_error( "the `-Z unstable-options` flag must also be passed to \ enable the all-target-specs-json print option", ); @@ -2051,10 +2029,9 @@ fn collect_print_requests( let prints = PRINT_REQUESTS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>(); let prints = prints.join(", "); - early_error( - error_format, - format!("unknown print request `{req}`. Valid print requests are: {prints}"), - ); + handler.early_error(format!( + "unknown print request `{req}`. Valid print requests are: {prints}" + )); } } })); @@ -2063,14 +2040,14 @@ fn collect_print_requests( } pub fn parse_target_triple( + handler: &EarlyErrorHandler, matches: &getopts::Matches, - error_format: ErrorOutputType, ) -> TargetTriple { match matches.opt_str("target") { Some(target) if target.ends_with(".json") => { let path = Path::new(&target); TargetTriple::from_path(path).unwrap_or_else(|_| { - early_error(error_format, format!("target file {path:?} does not exist")) + handler.early_error(format!("target file {path:?} does not exist")) }) } Some(target) => TargetTriple::TargetTriple(target), @@ -2079,9 +2056,9 @@ pub fn parse_target_triple( } fn parse_opt_level( + handler: &EarlyErrorHandler, matches: &getopts::Matches, cg: &CodegenOptions, - error_format: ErrorOutputType, ) -> OptLevel { // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able // to use them interchangeably. However, because they're technically different flags, @@ -2109,13 +2086,10 @@ fn parse_opt_level( "s" => OptLevel::Size, "z" => OptLevel::SizeMin, arg => { - early_error( - error_format, - format!( - "optimization level needs to be \ + handler.early_error(format!( + "optimization level needs to be \ between 0-3, s or z (instead was `{arg}`)" - ), - ); + )); } } } @@ -2135,23 +2109,23 @@ fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInf } pub(crate) fn parse_assert_incr_state( + handler: &EarlyErrorHandler, opt_assertion: &Option<String>, - error_format: ErrorOutputType, ) -> Option<IncrementalStateAssertion> { match opt_assertion { Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded), Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded), Some(s) => { - early_error(error_format, format!("unexpected incremental state assertion value: {s}")) + handler.early_error(format!("unexpected incremental state assertion value: {s}")) } None => None, } } fn parse_native_lib_kind( + handler: &EarlyErrorHandler, matches: &getopts::Matches, kind: &str, - error_format: ErrorOutputType, ) -> (NativeLibKind, Option<bool>) { let (kind, modifiers) = match kind.split_once(':') { None => (kind, None), @@ -2169,35 +2143,31 @@ fn parse_native_lib_kind( } else { ", the `-Z unstable-options` flag must also be passed to use it" }; - early_error(error_format, format!("library kind `link-arg` is unstable{why}")) + handler.early_error(format!("library kind `link-arg` is unstable{why}")) } NativeLibKind::LinkArg } - _ => early_error( - error_format, - format!( - "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg" - ), - ), + _ => handler.early_error(format!( + "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg" + )), }; match modifiers { None => (kind, None), - Some(modifiers) => parse_native_lib_modifiers(kind, modifiers, error_format, matches), + Some(modifiers) => parse_native_lib_modifiers(handler, kind, modifiers, matches), } } fn parse_native_lib_modifiers( + handler: &EarlyErrorHandler, mut kind: NativeLibKind, modifiers: &str, - error_format: ErrorOutputType, matches: &getopts::Matches, ) -> (NativeLibKind, Option<bool>) { let mut verbatim = None; for modifier in modifiers.split(',') { let (modifier, value) = match modifier.strip_prefix(['+', '-']) { Some(m) => (m, modifier.starts_with('+')), - None => early_error( - error_format, + None => handler.early_error( "invalid linking modifier syntax, expected '+' or '-' prefix \ before one of: bundle, verbatim, whole-archive, as-needed", ), @@ -2210,21 +2180,20 @@ fn parse_native_lib_modifiers( } else { ", the `-Z unstable-options` flag must also be passed to use it" }; - early_error(error_format, format!("linking modifier `{modifier}` is unstable{why}")) + handler.early_error(format!("linking modifier `{modifier}` is unstable{why}")) } }; let assign_modifier = |dst: &mut Option<bool>| { if dst.is_some() { let msg = format!("multiple `{modifier}` modifiers in a single `-l` option"); - early_error(error_format, msg) + handler.early_error(msg) } else { *dst = Some(value); } }; match (modifier, &mut kind) { ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle), - ("bundle", _) => early_error( - error_format, + ("bundle", _) => handler.early_error( "linking modifier `bundle` is only compatible with `static` linking kind", ), @@ -2233,8 +2202,7 @@ fn parse_native_lib_modifiers( ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { assign_modifier(whole_archive) } - ("whole-archive", _) => early_error( - error_format, + ("whole-archive", _) => handler.early_error( "linking modifier `whole-archive` is only compatible with `static` linking kind", ), @@ -2243,28 +2211,24 @@ fn parse_native_lib_modifiers( report_unstable_modifier(); assign_modifier(as_needed) } - ("as-needed", _) => early_error( - error_format, + ("as-needed", _) => handler.early_error( "linking modifier `as-needed` is only compatible with \ `dylib` and `framework` linking kinds", ), // Note: this error also excludes the case with empty modifier // string, like `modifiers = ""`. - _ => early_error( - error_format, - format!( - "unknown linking modifier `{modifier}`, expected one \ + _ => handler.early_error(format!( + "unknown linking modifier `{modifier}`, expected one \ of: bundle, verbatim, whole-archive, as-needed" - ), - ), + )), } } (kind, verbatim) } -fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> { +fn parse_libs(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Vec<NativeLib> { matches .opt_strs("l") .into_iter() @@ -2278,7 +2242,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec< let (name, kind, verbatim) = match s.split_once('=') { None => (s, NativeLibKind::Unspecified, None), Some((kind, name)) => { - let (kind, verbatim) = parse_native_lib_kind(matches, kind, error_format); + let (kind, verbatim) = parse_native_lib_kind(handler, matches, kind); (name.to_string(), kind, verbatim) } }; @@ -2288,7 +2252,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec< Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())), }; if name.is_empty() { - early_error(error_format, "library name must not be empty"); + handler.early_error("library name must not be empty"); } NativeLib { name, new_name, kind, verbatim } }) @@ -2296,9 +2260,9 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec< } pub fn parse_externs( + handler: &EarlyErrorHandler, matches: &getopts::Matches, unstable_opts: &UnstableOptions, - error_format: ErrorOutputType, ) -> Externs { let is_unstable_enabled = unstable_opts.unstable_options; let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new(); @@ -2362,8 +2326,7 @@ pub fn parse_externs( let mut force = false; if let Some(opts) = options { if !is_unstable_enabled { - early_error( - error_format, + handler.early_error( "the `-Z unstable-options` flag must also be passed to \ enable `--extern` options", ); @@ -2375,15 +2338,14 @@ pub fn parse_externs( if let ExternLocation::ExactPaths(_) = &entry.location { add_prelude = false; } else { - early_error( - error_format, + handler.early_error( "the `noprelude` --extern option requires a file path", ); } } "nounused" => nounused_dep = true, "force" => force = true, - _ => early_error(error_format, format!("unknown --extern option `{opt}`")), + _ => handler.early_error(format!("unknown --extern option `{opt}`")), } } } @@ -2402,18 +2364,15 @@ pub fn parse_externs( } fn parse_remap_path_prefix( + handler: &EarlyErrorHandler, matches: &getopts::Matches, unstable_opts: &UnstableOptions, - error_format: ErrorOutputType, ) -> Vec<(PathBuf, PathBuf)> { let mut mapping: Vec<(PathBuf, PathBuf)> = matches .opt_strs("remap-path-prefix") .into_iter() .map(|remap| match remap.rsplit_once('=') { - None => early_error( - error_format, - "--remap-path-prefix must contain '=' between FROM and TO", - ), + None => handler.early_error("--remap-path-prefix must contain '=' between FROM and TO"), Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), }) .collect(); @@ -2429,86 +2388,75 @@ fn parse_remap_path_prefix( // JUSTIFICATION: before wrapper fn is available #[allow(rustc::bad_opt_access)] -pub fn build_session_options(matches: &getopts::Matches) -> Options { - let color = parse_color(matches); +pub fn build_session_options( + handler: &mut EarlyErrorHandler, + matches: &getopts::Matches, +) -> Options { + let color = parse_color(handler, matches); - let edition = parse_crate_edition(matches); + let edition = parse_crate_edition(handler, matches); let JsonConfig { json_rendered, json_artifact_notifications, json_unused_externs, json_future_incompat, - } = parse_json(matches); + } = parse_json(handler, matches); - let error_format = parse_error_format(matches, color, json_rendered); + let error_format = parse_error_format(handler, matches, color, json_rendered); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { - early_error(error_format, "`--diagnostic-width` must be an positive integer"); + handler.early_error("`--diagnostic-width` must be an positive integer"); }); let unparsed_crate_types = matches.opt_strs("crate-type"); let crate_types = parse_crate_types_from_list(unparsed_crate_types) - .unwrap_or_else(|e| early_error(error_format, e)); + .unwrap_or_else(|e| handler.early_error(e)); - let mut unstable_opts = UnstableOptions::build(matches, error_format); - let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); + let mut unstable_opts = UnstableOptions::build(handler, matches); + let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(handler, matches); - check_error_format_stability(&unstable_opts, error_format, json_rendered); + check_error_format_stability(handler, &unstable_opts, error_format, json_rendered); if !unstable_opts.unstable_options && json_unused_externs.is_enabled() { - early_error( - error_format, + handler.early_error( "the `-Z unstable-options` flag must also be passed to enable \ the flag `--json=unused-externs`", ); } - let output_types = parse_output_types(&unstable_opts, matches, error_format); + let output_types = parse_output_types(handler, &unstable_opts, matches); - let mut cg = CodegenOptions::build(matches, error_format); - let (disable_local_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( - &output_types, - matches, - error_format, - cg.codegen_units, - ); + let mut cg = CodegenOptions::build(handler, matches); + let (disable_local_thinlto, mut codegen_units) = + should_override_cgus_and_disable_thinlto(handler, &output_types, matches, cg.codegen_units); - check_thread_count(&unstable_opts, error_format); + check_thread_count(handler, &unstable_opts); let incremental = cg.incremental.as_ref().map(PathBuf::from); - let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format); + let assert_incr_state = parse_assert_incr_state(handler, &unstable_opts.assert_incr_state); if unstable_opts.profile && incremental.is_some() { - early_error( - error_format, - "can't instrument with gcov profiling when compiling incrementally", - ); + handler.early_error("can't instrument with gcov profiling when compiling incrementally"); } if unstable_opts.profile { match codegen_units { Some(1) => {} None => codegen_units = Some(1), - Some(_) => early_error( - error_format, - "can't instrument with gcov profiling with multiple codegen units", - ), + Some(_) => handler + .early_error("can't instrument with gcov profiling with multiple codegen units"), } } if cg.profile_generate.enabled() && cg.profile_use.is_some() { - early_error( - error_format, - "options `-C profile-generate` and `-C profile-use` are exclusive", - ); + handler.early_error("options `-C profile-generate` and `-C profile-use` are exclusive"); } if unstable_opts.profile_sample_use.is_some() && (cg.profile_generate.enabled() || cg.profile_use.is_some()) { - early_error( - error_format, + handler.early_error( "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`", ); } @@ -2517,23 +2465,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { // precedence. match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) { (Some(smv_c), Some(smv_z)) if smv_c != smv_z => { - early_error( - error_format, + handler.early_error( "incompatible values passed for `-C symbol-mangling-version` \ and `-Z symbol-mangling-version`", ); } (Some(SymbolManglingVersion::V0), _) => {} (Some(_), _) if !unstable_opts.unstable_options => { - early_error( - error_format, - "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`", - ); + handler + .early_error("`-C symbol-mangling-version=legacy` requires `-Z unstable-options`"); } (None, None) => {} (None, smv) => { - early_warn( - error_format, + handler.early_warn( "`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`", ); cg.symbol_mangling_version = smv; @@ -2545,25 +2489,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { // precedence. match (cg.instrument_coverage, unstable_opts.instrument_coverage) { (Some(ic_c), Some(ic_z)) if ic_c != ic_z => { - early_error( - error_format, + handler.early_error( "incompatible values passed for `-C instrument-coverage` \ and `-Z instrument-coverage`", ); } (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {} (Some(_), _) if !unstable_opts.unstable_options => { - early_error( - error_format, - "`-C instrument-coverage=except-*` requires `-Z unstable-options`", - ); + handler.early_error("`-C instrument-coverage=except-*` requires `-Z unstable-options`"); } (None, None) => {} (None, ic) => { - early_warn( - error_format, - "`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`", - ); + handler + .early_warn("`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`"); cg.instrument_coverage = ic; } _ => {} @@ -2571,8 +2509,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) { if cg.profile_generate.enabled() || cg.profile_use.is_some() { - early_error( - error_format, + handler.early_error( "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ or `-C profile-generate`", ); @@ -2585,8 +2522,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { match cg.symbol_mangling_version { None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0), Some(SymbolManglingVersion::Legacy) => { - early_warn( - error_format, + handler.early_warn( "-C instrument-coverage requires symbol mangling version `v0`, \ but `-C symbol-mangling-version=legacy` was specified", ); @@ -2602,10 +2538,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { if !cg.embed_bitcode { match cg.lto { LtoCli::No | LtoCli::Unspecified => {} - LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( - error_format, - "options `-C embed-bitcode=no` and `-C lto` are incompatible", - ), + LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => { + handler.early_error("options `-C embed-bitcode=no` and `-C lto` are incompatible") + } } } @@ -2618,17 +2553,17 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { flag must also be passed to explicitly use it", flavor.desc() ); - early_error(error_format, msg); + handler.early_error(msg); } } - let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format); + let prints = collect_print_requests(handler, &mut cg, &mut unstable_opts, matches); let cg = cg; let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); - let target_triple = parse_target_triple(matches, error_format); - let opt_level = parse_opt_level(matches, &cg, error_format); + let target_triple = parse_target_triple(handler, matches); + let opt_level = parse_opt_level(handler, matches, &cg); // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) // for more details. @@ -2637,28 +2572,28 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let mut search_paths = vec![]; for s in &matches.opt_strs("L") { - search_paths.push(SearchPath::from_cli_opt(s, error_format)); + search_paths.push(SearchPath::from_cli_opt(handler, s)); } - let libs = parse_libs(matches, error_format); + let libs = parse_libs(handler, matches); let test = matches.opt_present("test"); if !cg.remark.is_empty() && debuginfo == DebugInfo::None { - early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); + handler.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations"); } - let externs = parse_externs(matches, &unstable_opts, error_format); + let externs = parse_externs(handler, matches, &unstable_opts); let crate_name = matches.opt_str("crate-name"); - let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format); + let remap_path_prefix = parse_remap_path_prefix(handler, matches, &unstable_opts); - let pretty = parse_pretty(&unstable_opts, error_format); + let pretty = parse_pretty(handler, &unstable_opts); // query-dep-graph is required if dump-dep-graph is given #106736 if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph { - early_error(error_format, "can't dump dependency graph without `-Z query-dep-graph`"); + handler.early_error("can't dump dependency graph without `-Z query-dep-graph`"); } // Try to find a directory containing the Rust `src`, for more details see @@ -2690,7 +2625,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { }; let working_dir = std::env::current_dir().unwrap_or_else(|e| { - early_error(error_format, format!("Current directory is invalid: {e}")); + handler.early_error(format!("Current directory is invalid: {e}")); }); let remap = FilePathMapping::new(remap_path_prefix.clone()); @@ -2741,7 +2676,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } } -fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Option<PpMode> { +fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) -> Option<PpMode> { use PpMode::*; let first = match unstable_opts.unpretty.as_deref()? { @@ -2760,16 +2695,13 @@ fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Optio "thir-flat" => ThirFlat, "mir" => Mir, "mir-cfg" => MirCFG, - name => early_error( - efmt, - format!( - "argument to `unpretty` must be one of `normal`, `identified`, \ + name => handler.early_error(format!( + "argument to `unpretty` must be one of `normal`, `identified`, \ `expanded`, `expanded,identified`, `expanded,hygiene`, \ `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir` or \ `mir-cfg`; got {name}" - ), - ), + )), }; debug!("got unpretty option: {first:?}"); Some(first) @@ -2809,8 +2741,8 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy } pub mod nightly_options { - use super::{ErrorOutputType, OptionStability, RustcOptGroup}; - use crate::early_error; + use super::{OptionStability, RustcOptGroup}; + use crate::EarlyErrorHandler; use rustc_feature::UnstableFeatures; pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { @@ -2826,7 +2758,11 @@ pub mod nightly_options { UnstableFeatures::from_environment(krate).is_nightly_build() } - pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { + pub fn check_nightly_options( + handler: &EarlyErrorHandler, + matches: &getopts::Matches, + flags: &[RustcOptGroup], + ) { let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); let really_allows_unstable_options = match_is_nightly_build(matches); @@ -2838,14 +2774,11 @@ pub mod nightly_options { continue; } if opt.name != "Z" && !has_z_unstable_option { - early_error( - ErrorOutputType::default(), - format!( - "the `-Z unstable-options` flag must also be passed to enable \ + handler.early_error(format!( + "the `-Z unstable-options` flag must also be passed to enable \ the flag `{}`", - opt.name - ), - ); + opt.name + )); } if really_allows_unstable_options { continue; @@ -2856,7 +2789,12 @@ pub mod nightly_options { "the option `{}` is only accepted on the nightly compiler", opt.name ); - early_error(ErrorOutputType::default(), msg); + let _ = handler.early_error_no_abort(msg); + handler.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>"); + handler.early_help( + "consider switching to a nightly toolchain: `rustup default nightly`", + ); + handler.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>"); } OptionStability::Stable => {} } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 270d8331602..e5063eef47a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1,9 +1,8 @@ use crate::config::*; -use crate::early_error; -use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLib; +use crate::{lint, EarlyErrorHandler}; use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{LanguageIdentifier, TerminalUrl}; use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet}; @@ -245,10 +244,10 @@ macro_rules! options { impl $struct_name { pub fn build( + handler: &EarlyErrorHandler, matches: &getopts::Matches, - error_format: ErrorOutputType, ) -> $struct_name { - build_options(matches, $stat, $prefix, $outputname, error_format) + build_options(handler, matches, $stat, $prefix, $outputname) } fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 { @@ -309,11 +308,11 @@ type OptionSetter<O> = fn(&mut O, v: Option<&str>) -> bool; type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)]; fn build_options<O: Default>( + handler: &EarlyErrorHandler, matches: &getopts::Matches, descrs: OptionDescrs<O>, prefix: &str, outputname: &str, - error_format: ErrorOutputType, ) -> O { let mut op = O::default(); for option in matches.opt_strs(prefix) { @@ -327,15 +326,13 @@ fn build_options<O: Default>( Some((_, setter, type_desc, _)) => { if !setter(&mut op, value) { match value { - None => early_error( - error_format, + None => handler.early_error( format!( "{0} option `{1}` requires {2} ({3} {1}=<value>)", outputname, key, type_desc, prefix ), ), - Some(value) => early_error( - error_format, + Some(value) => handler.early_error( format!( "incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected" ), @@ -343,7 +340,7 @@ fn build_options<O: Default>( } } } - None => early_error(error_format, format!("unknown {outputname} option: `{key}`")), + None => handler.early_error(format!("unknown {outputname} option: `{key}`")), } } return op; diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index 56a6b6f3b03..07e78d1760e 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -1,5 +1,5 @@ use crate::filesearch::make_target_lib_path; -use crate::{config, early_error}; +use crate::EarlyErrorHandler; use std::path::{Path, PathBuf}; #[derive(Clone, Debug)] @@ -46,7 +46,7 @@ impl PathKind { } impl SearchPath { - pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self { + pub fn from_cli_opt(handler: &EarlyErrorHandler, path: &str) -> Self { let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") { (PathKind::Native, stripped) } else if let Some(stripped) = path.strip_prefix("crate=") { @@ -61,7 +61,7 @@ impl SearchPath { (PathKind::All, path) }; if path.is_empty() { - early_error(output, "empty search path given via `-L`"); + handler.early_error("empty search path given via `-L`"); } let dir = PathBuf::from(path); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ea5beb6f8be..5be122ffbde 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,10 +1,10 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; -use crate::config::Input; use crate::config::{ self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath, }; +use crate::config::{ErrorOutputType, Input}; use crate::errors; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; @@ -25,7 +25,7 @@ use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, - ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted, + ErrorGuaranteed, FluentBundle, Handler, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted, TerminalUrl, }; use rustc_macros::HashStable_Generic; @@ -1382,6 +1382,7 @@ fn default_emitter( // JUSTIFICATION: literally session construction #[allow(rustc::bad_opt_access)] pub fn build_session( + handler: &EarlyErrorHandler, sopts: config::Options, io: CompilerIO, bundle: Option<Lrc<rustc_errors::FluentBundle>>, @@ -1408,13 +1409,12 @@ pub fn build_session( None => filesearch::get_or_default_sysroot().expect("Failed finding sysroot"), }; - let target_cfg = config::build_target_config(&sopts, target_override, &sysroot); + let target_cfg = config::build_target_config(handler, &sopts, target_override, &sysroot); let host_triple = TargetTriple::from_triple(config::host_triple()); - let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| { - early_error(sopts.error_format, format!("Error loading host specification: {e}")) - }); + let (host, target_warnings) = Target::search(&host_triple, &sysroot) + .unwrap_or_else(|e| handler.early_error(format!("Error loading host specification: {e}"))); for warning in target_warnings.warning_messages() { - early_warn(sopts.error_format, warning) + handler.early_warn(warning) } let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); @@ -1456,7 +1456,7 @@ pub fn build_session( match profiler { Ok(profiler) => Some(Arc::new(profiler)), Err(e) => { - early_warn(sopts.error_format, format!("failed to create profiler: {e}")); + handler.early_warn(format!("failed to create profiler: {e}")); None } } @@ -1723,7 +1723,64 @@ pub enum IncrCompSession { InvalidBecauseOfErrors { session_directory: PathBuf }, } -fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler { +/// A wrapper around an [`Handler`] that is used for early error emissions. +pub struct EarlyErrorHandler { + handler: Handler, +} + +impl EarlyErrorHandler { + pub fn new(output: ErrorOutputType) -> Self { + let emitter = mk_emitter(output); + Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter) } + } + + pub fn abort_if_errors(&self) { + self.handler.abort_if_errors() + } + + /// Swap out the underlying handler once we acquire the user's preference on error emission + /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the + /// previous handler will be emitted. + pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) { + self.handler.abort_if_errors(); + + let emitter = mk_emitter(output); + self.handler = Handler::with_emitter(true, None, emitter); + } + + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + pub fn early_note(&self, msg: impl Into<DiagnosticMessage>) { + self.handler.struct_note_without_error(msg).emit() + } + + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + pub fn early_help(&self, msg: impl Into<DiagnosticMessage>) { + self.handler.struct_help(msg).emit() + } + + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"] + pub fn early_error_no_abort(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { + self.handler.struct_err(msg).emit() + } + + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + pub fn early_error(&self, msg: impl Into<DiagnosticMessage>) -> ! { + self.handler.struct_fatal(msg).emit() + } + + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + pub fn early_warn(&self, msg: impl Into<DiagnosticMessage>) { + self.handler.struct_warn(msg).emit() + } +} + +fn mk_emitter(output: ErrorOutputType) -> Box<dyn Emitter + sync::Send + 'static> { // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will // need to reference every crate that might emit an early error for translation to work. let fallback_bundle = @@ -1755,27 +1812,5 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler TerminalUrl::No, )), }; - rustc_errors::Handler::with_emitter(true, None, emitter) -} - -#[allow(rustc::untranslatable_diagnostic)] -#[allow(rustc::diagnostic_outside_of_impl)] -#[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"] -pub fn early_error_no_abort( - output: config::ErrorOutputType, - msg: impl Into<DiagnosticMessage>, -) -> ErrorGuaranteed { - early_error_handler(output).struct_err(msg).emit() -} - -#[allow(rustc::untranslatable_diagnostic)] -#[allow(rustc::diagnostic_outside_of_impl)] -pub fn early_error(output: config::ErrorOutputType, msg: impl Into<DiagnosticMessage>) -> ! { - early_error_handler(output).struct_fatal(msg).emit() -} - -#[allow(rustc::untranslatable_diagnostic)] -#[allow(rustc::diagnostic_outside_of_impl)] -pub fn early_warn(output: config::ErrorOutputType, msg: impl Into<DiagnosticMessage>) { - early_error_handler(output).struct_warn(msg).emit() + emitter } |
