about summary refs log tree commit diff
path: root/compiler/rustc_session
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_session')
-rw-r--r--compiler/rustc_session/src/config.rs400
-rw-r--r--compiler/rustc_session/src/options.rs17
-rw-r--r--compiler/rustc_session/src/search_paths.rs6
-rw-r--r--compiler/rustc_session/src/session.rs99
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
 }