about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-09-03 10:33:05 +0530
committerGitHub <noreply@github.com>2022-09-03 10:33:05 +0530
commita0056795da7a51f0ebebf0d5ad8f0b663d089420 (patch)
treefcbeee4805d8b23bdbbe9b39425d28a45446ff2a
parent2ed716a81d36c0779bb132031fc9ef0b9e7f4212 (diff)
parent30adfd6a17bd0d7c4d1302cc4a0f92962577de4a (diff)
downloadrust-a0056795da7a51f0ebebf0d5ad8f0b663d089420.tar.gz
rust-a0056795da7a51f0ebebf0d5ad8f0b663d089420.zip
Rollup merge of #100928 - CleanCut:rustc_metadata_diagnostics, r=davidtwco
Migrate rustc_metadata to SessionDiagnostics

Migrate rustc_metadata to SessionDiagnostics.

Part of https://github.com/rust-lang/rust/issues/100717
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_error_messages/locales/en-US/metadata.ftl272
-rw-r--r--compiler/rustc_error_messages/src/lib.rs1
-rw-r--r--compiler/rustc_errors/Cargo.toml5
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs7
-rw-r--r--compiler/rustc_metadata/src/creader.rs53
-rw-r--r--compiler/rustc_metadata/src/dependency_format.rs73
-rw-r--r--compiler/rustc_metadata/src/errors.rs672
-rw-r--r--compiler/rustc_metadata/src/fs.rs25
-rw-r--r--compiler/rustc_metadata/src/lib.rs3
-rw-r--r--compiler/rustc_metadata/src/locator.rs328
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs201
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs7
13 files changed, 1195 insertions, 453 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 08b377d8214..2d143025241 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3509,6 +3509,7 @@ dependencies = [
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
+ "rustc_target",
  "serde",
  "serde_json",
  "termcolor",
diff --git a/compiler/rustc_error_messages/locales/en-US/metadata.ftl b/compiler/rustc_error_messages/locales/en-US/metadata.ftl
new file mode 100644
index 00000000000..00067a1bf6a
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/metadata.ftl
@@ -0,0 +1,272 @@
+metadata_rlib_required =
+    crate `{$crate_name}` required to be available in rlib format, but was not found in this form
+
+metadata_lib_required =
+    crate `{$crate_name}` required to be available in {$kind} format, but was not found in this form
+
+metadata_crate_dep_multiple =
+    cannot satisfy dependencies so `{$crate_name}` only shows up once
+    .help = having upstream crates all available in one format will likely make this go away
+
+metadata_two_panic_runtimes =
+    cannot link together two panic runtimes: {$prev_name} and {$cur_name}
+
+metadata_bad_panic_strategy =
+    the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}`
+
+metadata_required_panic_strategy =
+    the crate `{$crate_name}` requires panic strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}`
+
+metadata_incompatible_panic_in_drop_strategy =
+    the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}`
+
+metadata_multiple_names_in_link =
+    multiple `name` arguments in a single `#[link]` attribute
+
+metadata_multiple_kinds_in_link =
+    multiple `kind` arguments in a single `#[link]` attribute
+
+metadata_link_name_form =
+    link name must be of the form `name = "string"`
+
+metadata_link_kind_form =
+    link kind must be of the form `kind = "string"`
+
+metadata_link_modifiers_form =
+    link modifiers must be of the form `modifiers = "string"`
+
+metadata_link_cfg_form =
+    link cfg must be of the form `cfg(/* predicate */)`
+
+metadata_wasm_import_form =
+    wasm import module must be of the form `wasm_import_module = "string"`
+
+metadata_empty_link_name =
+    link name must not be empty
+    .label = empty link name
+
+metadata_link_framework_apple =
+    link kind `framework` is only supported on Apple targets
+
+metadata_framework_only_windows =
+    link kind `raw-dylib` is only supported on Windows targets
+
+metadata_unknown_link_kind =
+    unknown link kind `{$kind}`, expected one of: static, dylib, framework, raw-dylib
+    .label = unknown link kind
+
+metadata_multiple_link_modifiers =
+    multiple `modifiers` arguments in a single `#[link]` attribute
+
+metadata_multiple_cfgs =
+    multiple `cfg` arguments in a single `#[link]` attribute
+
+metadata_link_cfg_single_predicate =
+    link cfg must have a single predicate argument
+
+metadata_multiple_wasm_import =
+    multiple `wasm_import_module` arguments in a single `#[link]` attribute
+
+metadata_unexpected_link_arg =
+    unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module, import_name_type
+
+metadata_invalid_link_modifier =
+    invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
+
+metadata_multiple_modifiers =
+    multiple `{$modifier}` modifiers in a single `modifiers` argument
+
+metadata_bundle_needs_static =
+    linking modifier `bundle` is only compatible with `static` linking kind
+
+metadata_whole_archive_needs_static =
+    linking modifier `whole-archive` is only compatible with `static` linking kind
+
+metadata_as_needed_compatibility =
+    linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds
+
+metadata_unknown_link_modifier =
+    unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed
+
+metadata_incompatible_wasm_link =
+    `wasm_import_module` is incompatible with other arguments in `#[link]` attributes
+
+metadata_link_requires_name =
+    `#[link]` attribute requires a `name = "string"` argument
+    .label = missing `name` argument
+
+metadata_raw_dylib_no_nul =
+    link name must not contain NUL characters if link kind is `raw-dylib`
+
+metadata_link_ordinal_raw_dylib =
+    `#[link_ordinal]` is only supported if link kind is `raw-dylib`
+
+metadata_lib_framework_apple =
+    library kind `framework` is only supported on Apple targets
+
+metadata_empty_renaming_target =
+    an empty renaming target was specified for library `{$lib_name}`
+
+metadata_renaming_no_link =
+    renaming of the library `{$lib_name}` was specified, however this crate contains no `#[link(...)]` attributes referencing this library
+
+metadata_multiple_renamings =
+    multiple renamings were specified for library `{$lib_name}`
+
+metadata_no_link_mod_override =
+    overriding linking modifiers from command line is not supported
+
+metadata_unsupported_abi_i686 =
+    ABI not supported by `#[link(kind = "raw-dylib")]` on i686
+
+metadata_unsupported_abi =
+    ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
+
+metadata_fail_create_file_encoder =
+    failed to create file encoder: {$err}
+
+metadata_fail_seek_file =
+    failed to seek the file: {$err}
+
+metadata_fail_write_file =
+    failed to write to the file: {$err}
+
+metadata_crate_not_panic_runtime =
+    the crate `{$crate_name}` is not a panic runtime
+
+metadata_no_panic_strategy =
+    the crate `{$crate_name}` does not have the panic strategy `{$strategy}`
+
+metadata_profiler_builtins_needs_core =
+    `profiler_builtins` crate (required by compiler options) is not compatible with crate attribute `#![no_core]`
+
+metadata_not_profiler_runtime =
+    the crate `{$crate_name}` is not a profiler runtime
+
+metadata_no_multiple_global_alloc =
+    cannot define multiple global allocators
+    .label = cannot define a new global allocator
+
+metadata_prev_global_alloc =
+    previous global allocator defined here
+
+metadata_conflicting_global_alloc =
+    the `#[global_allocator]` in {$other_crate_name} conflicts with global allocator in: {$crate_name}
+
+metadata_global_alloc_required =
+    no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait
+
+metadata_no_transitive_needs_dep =
+    the crate `{$crate_name}` cannot depend on a crate that needs {$needs_crate_name}, but it depends on `{$deps_crate_name}`
+
+metadata_failed_write_error =
+    failed to write {$filename}: {$err}
+
+metadata_failed_create_tempdir =
+    couldn't create a temp dir: {$err}
+
+metadata_failed_create_file =
+    failed to create the file {$filename}: {$err}
+
+metadata_failed_create_encoded_metadata =
+    failed to create encoded metadata from file: {$err}
+
+metadata_non_ascii_name =
+    cannot load a crate with a non-ascii name `{$crate_name}`
+
+metadata_extern_location_not_exist =
+    extern location for {$crate_name} does not exist: {$location}
+
+metadata_extern_location_not_file =
+    extern location for {$crate_name} is not a file: {$location}
+
+metadata_multiple_candidates =
+    multiple {$flavor} candidates for `{$crate_name}` found
+
+metadata_multiple_matching_crates =
+    multiple matching crates for `{$crate_name}`
+    .note = candidates:{$candidates}
+
+metadata_symbol_conflicts_current =
+    the current crate is indistinguishable from one of its dependencies: it has the same crate-name `{$crate_name}` and was compiled with the same `-C metadata` arguments. This will result in symbol conflicts between the two.
+
+metadata_symbol_conflicts_others =
+    found two different crates with name `{$crate_name}` that are not distinguished by differing `-C metadata`. This will result in symbol conflicts between the two.
+
+metadata_stable_crate_id_collision =
+    found crates (`{$crate_name0}` and `{$crate_name1}`) with colliding StableCrateId values.
+
+metadata_dl_error =
+    {$err}
+
+metadata_newer_crate_version =
+    found possibly newer version of crate `{$crate_name}`{$add_info}
+    .note = perhaps that crate needs to be recompiled?
+
+metadata_found_crate_versions =
+    the following crate versions were found:{$found_crates}
+
+metadata_no_crate_with_triple =
+    couldn't find crate `{$crate_name}` with expected target triple {$locator_triple}{$add_info}
+
+metadata_found_staticlib =
+    found staticlib `{$crate_name}` instead of rlib or dylib{$add_info}
+    .help = please recompile that crate using --crate-type lib
+
+metadata_incompatible_rustc =
+    found crate `{$crate_name}` compiled by an incompatible version of rustc{$add_info}
+    .help = please recompile that crate using this compiler ({$rustc_version}) (consider running `cargo clean` first)
+
+metadata_invalid_meta_files =
+    found invalid metadata files for crate `{$crate_name}`{$add_info}
+
+metadata_cannot_find_crate =
+    can't find crate for `{$crate_name}`{$add_info}
+
+metadata_no_dylib_plugin =
+    plugin `{$crate_name}` only found in rlib format, but must be available in dylib format
+
+metadata_target_not_installed =
+    the `{$locator_triple}` target may not be installed
+
+metadata_target_no_std_support =
+    the `{$locator_triple}` target may not support the standard library
+
+metadata_consider_downloading_target =
+    consider downloading the target with `rustup target add {$locator_triple}`
+
+metadata_std_required =
+    `std` is required by `{$current_crate}` because it does not declare `#![no_std]`
+
+metadata_consider_building_std =
+    consider building the standard library from source with `cargo build -Zbuild-std`
+
+metadata_compiler_missing_profiler =
+    the compiler may have been built without the profiler runtime
+
+metadata_install_missing_components =
+    maybe you need to install the missing components with: `rustup component add rust-src rustc-dev llvm-tools-preview`
+
+metadata_cant_find_crate =
+    can't find crate
+
+metadata_crate_location_unknown_type =
+    extern location for {$crate_name} is of an unknown type: {$path}
+
+metadata_lib_filename_form =
+    file name should be lib*.rlib or {dll_prefix}*.{dll_suffix}
+
+metadata_multiple_import_name_type =
+    multiple `import_name_type` arguments in a single `#[link]` attribute
+
+metadata_import_name_type_form =
+    import name type must be of the form `import_name_type = "string"`
+
+metadata_import_name_type_x86 =
+    import name type is only supported on x86
+
+metadata_unknown_import_name_type =
+    unknown import name type `{$import_name_type}`, expected one of: decorated, noprefix, undecorated
+
+metadata_import_name_type_raw =
+    import name type can only be used with link kind `raw-dylib`
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 05d0a2ba82c..5281a287b0d 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -48,6 +48,7 @@ fluent_messages! {
     infer => "../locales/en-US/infer.ftl",
     lint => "../locales/en-US/lint.ftl",
     monomorphize => "../locales/en-US/monomorphize.ftl",
+    metadata => "../locales/en-US/metadata.ftl",
     parser => "../locales/en-US/parser.ftl",
     passes => "../locales/en-US/passes.ftl",
     plugin_impl => "../locales/en-US/plugin_impl.ftl",
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index 36805aa874f..4d207fd17fb 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -15,13 +15,14 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
+rustc_target = { path = "../rustc_target" }
 unicode-width = "0.1.4"
 atty = "0.2"
 termcolor = "1.0"
 annotate-snippets = "0.9"
 termize = "0.1.1"
-serde = { version = "1.0.125", features = ["derive"] }
+serde = { version = "1.0.125", features = [ "derive" ] }
 serde_json = "1.0.59"
 
 [target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3", features = ["handleapi", "synchapi", "winbase"] }
+winapi = { version = "0.3", features = [ "handleapi", "synchapi", "winbase" ] }
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 95ae9765a48..a052aaee047 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -10,6 +10,7 @@ use rustc_lint_defs::{Applicability, LintExpectationId};
 use rustc_span::edition::LATEST_STABLE_EDITION;
 use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol};
 use rustc_span::{edition::Edition, Span, DUMMY_SP};
+use rustc_target::spec::PanicStrategy;
 use std::borrow::Cow;
 use std::fmt;
 use std::hash::{Hash, Hasher};
@@ -144,6 +145,12 @@ impl IntoDiagnosticArg for usize {
     }
 }
 
+impl IntoDiagnosticArg for PanicStrategy {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::Owned(self.desc().to_string()))
+    }
+}
+
 impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> {
     fn into(self) -> FluentValue<'source> {
         match self {
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 6a5716600b3..cfcceecbef4 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -1,5 +1,9 @@
 //! Validates all used crates and extern libraries and loads their metadata
 
+use crate::errors::{
+    ConflictingGlobalAlloc, CrateNotPanicRuntime, GlobalAllocRequired, NoMultipleGlobalAlloc,
+    NoPanicStrategy, NoTransitiveNeedsDep, NotProfilerRuntime, ProfilerBuiltinsNeedsCore,
+};
 use crate::locator::{CrateError, CrateLocator, CratePaths};
 use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
 
@@ -745,15 +749,10 @@ impl<'a> CrateLoader<'a> {
         // Sanity check the loaded crate to ensure it is indeed a panic runtime
         // and the panic strategy is indeed what we thought it was.
         if !data.is_panic_runtime() {
-            self.sess.err(&format!("the crate `{}` is not a panic runtime", name));
+            self.sess.emit_err(CrateNotPanicRuntime { crate_name: name });
         }
         if data.required_panic_strategy() != Some(desired_strategy) {
-            self.sess.err(&format!(
-                "the crate `{}` does not have the panic \
-                                    strategy `{}`",
-                name,
-                desired_strategy.desc()
-            ));
+            self.sess.emit_err(NoPanicStrategy { crate_name: name, strategy: desired_strategy });
         }
 
         self.cstore.injected_panic_runtime = Some(cnum);
@@ -773,10 +772,7 @@ impl<'a> CrateLoader<'a> {
 
         let name = Symbol::intern(&self.sess.opts.unstable_opts.profiler_runtime);
         if name == sym::profiler_builtins && self.sess.contains_name(&krate.attrs, sym::no_core) {
-            self.sess.err(
-                "`profiler_builtins` crate (required by compiler options) \
-                        is not compatible with crate attribute `#![no_core]`",
-            );
+            self.sess.emit_err(ProfilerBuiltinsNeedsCore);
         }
 
         let Some(cnum) = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit) else { return; };
@@ -784,18 +780,14 @@ impl<'a> CrateLoader<'a> {
 
         // Sanity check the loaded crate to ensure it is indeed a profiler runtime
         if !data.is_profiler_runtime() {
-            self.sess.err(&format!("the crate `{}` is not a profiler runtime", name));
+            self.sess.emit_err(NotProfilerRuntime { crate_name: name });
         }
     }
 
     fn inject_allocator_crate(&mut self, krate: &ast::Crate) {
         self.cstore.has_global_allocator = match &*global_allocator_spans(&self.sess, krate) {
             [span1, span2, ..] => {
-                self.sess
-                    .struct_span_err(*span2, "cannot define multiple global allocators")
-                    .span_label(*span2, "cannot define a new global allocator")
-                    .span_label(*span1, "previous global allocator defined here")
-                    .emit();
+                self.sess.emit_err(NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });
                 true
             }
             spans => !spans.is_empty(),
@@ -831,11 +823,10 @@ impl<'a> CrateLoader<'a> {
             if data.has_global_allocator() {
                 match global_allocator {
                     Some(other_crate) => {
-                        self.sess.err(&format!(
-                        "the `#[global_allocator]` in {} conflicts with global allocator in: {}",
-                        other_crate,
-                        data.name()
-                    ));
+                        self.sess.emit_err(ConflictingGlobalAlloc {
+                            crate_name: data.name(),
+                            other_crate_name: other_crate,
+                        });
                     }
                     None => global_allocator = Some(data.name()),
                 }
@@ -854,10 +845,7 @@ impl<'a> CrateLoader<'a> {
         if !self.sess.contains_name(&krate.attrs, sym::default_lib_allocator)
             && !self.cstore.iter_crate_data().any(|(_, data)| data.has_default_lib_allocator())
         {
-            self.sess.err(
-                "no global memory allocator found but one is required; link to std or add \
-                 `#[global_allocator]` to a static item that implements the GlobalAlloc trait",
-            );
+            self.sess.emit_err(GlobalAllocRequired);
         }
         self.cstore.allocator_kind = Some(AllocatorKind::Default);
     }
@@ -881,14 +869,11 @@ impl<'a> CrateLoader<'a> {
         for dep in self.cstore.crate_dependencies_in_reverse_postorder(krate) {
             let data = self.cstore.get_crate_data(dep);
             if needs_dep(&data) {
-                self.sess.err(&format!(
-                    "the crate `{}` cannot depend \
-                                        on a crate that needs {}, but \
-                                        it depends on `{}`",
-                    self.cstore.get_crate_data(krate).name(),
-                    what,
-                    data.name()
-                ));
+                self.sess.emit_err(NoTransitiveNeedsDep {
+                    crate_name: self.cstore.get_crate_data(krate).name(),
+                    needs_crate_name: what,
+                    deps_crate_name: data.name(),
+                });
             }
         }
 
diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs
index 1a25e987d3a..6112ec9e4e9 100644
--- a/compiler/rustc_metadata/src/dependency_format.rs
+++ b/compiler/rustc_metadata/src/dependency_format.rs
@@ -52,6 +52,10 @@
 //! than finding a number of solutions (there are normally quite a few).
 
 use crate::creader::CStore;
+use crate::errors::{
+    BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, LibRequired,
+    RequiredPanicStrategy, RlibRequired, TwoPanicRuntimes,
+};
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::CrateNum;
@@ -136,11 +140,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
                 if src.rlib.is_some() {
                     continue;
                 }
-                sess.err(&format!(
-                    "crate `{}` required to be available in rlib format, \
-                                   but was not found in this form",
-                    tcx.crate_name(cnum)
-                ));
+                sess.emit_err(RlibRequired { crate_name: tcx.crate_name(cnum) });
             }
             return Vec::new();
         }
@@ -224,12 +224,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
                     Linkage::Static => "rlib",
                     _ => "dylib",
                 };
-                sess.err(&format!(
-                    "crate `{}` required to be available in {} format, \
-                                   but was not found in this form",
-                    tcx.crate_name(cnum),
-                    kind
-                ));
+                sess.emit_err(LibRequired { crate_name: tcx.crate_name(cnum), kind: kind });
             }
         }
     }
@@ -253,17 +248,7 @@ fn add_library(
             // This error is probably a little obscure, but I imagine that it
             // can be refined over time.
             if link2 != link || link == RequireStatic {
-                tcx.sess
-                    .struct_err(&format!(
-                        "cannot satisfy dependencies so `{}` only \
-                                              shows up once",
-                        tcx.crate_name(cnum)
-                    ))
-                    .help(
-                        "having upstream crates all available in one format \
-                           will likely make this go away",
-                    )
-                    .emit();
+                tcx.sess.emit_err(CrateDepMultiple { crate_name: tcx.crate_name(cnum) });
             }
         }
         None => {
@@ -360,11 +345,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) {
             if let Some((prev, _)) = panic_runtime {
                 let prev_name = tcx.crate_name(prev);
                 let cur_name = tcx.crate_name(cnum);
-                sess.err(&format!(
-                    "cannot link together two \
-                                   panic runtimes: {} and {}",
-                    prev_name, cur_name
-                ));
+                sess.emit_err(TwoPanicRuntimes { prev_name, cur_name });
             }
             panic_runtime = Some((
                 cnum,
@@ -384,13 +365,10 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) {
         // First up, validate that our selected panic runtime is indeed exactly
         // our same strategy.
         if found_strategy != desired_strategy {
-            sess.err(&format!(
-                "the linked panic runtime `{}` is \
-                               not compiled with this crate's \
-                               panic strategy `{}`",
-                tcx.crate_name(runtime_cnum),
-                desired_strategy.desc()
-            ));
+            sess.emit_err(BadPanicStrategy {
+                runtime: tcx.crate_name(runtime_cnum),
+                strategy: desired_strategy,
+            });
         }
 
         // Next up, verify that all other crates are compatible with this panic
@@ -407,28 +385,19 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) {
             }
 
             if let Some(found_strategy) = tcx.required_panic_strategy(cnum) && desired_strategy != found_strategy {
-                sess.err(&format!(
-                    "the crate `{}` requires \
-                               panic strategy `{}` which is \
-                               incompatible with this crate's \
-                               strategy of `{}`",
-                    tcx.crate_name(cnum),
-                    found_strategy.desc(),
-                    desired_strategy.desc()
-                ));
+                sess.emit_err(RequiredPanicStrategy {
+                    crate_name: tcx.crate_name(cnum),
+                    found_strategy,
+                    desired_strategy});
             }
 
             let found_drop_strategy = tcx.panic_in_drop_strategy(cnum);
             if tcx.sess.opts.unstable_opts.panic_in_drop != found_drop_strategy {
-                sess.err(&format!(
-                    "the crate `{}` is compiled with the \
-                               panic-in-drop strategy `{}` which is \
-                               incompatible with this crate's \
-                               strategy of `{}`",
-                    tcx.crate_name(cnum),
-                    found_drop_strategy.desc(),
-                    tcx.sess.opts.unstable_opts.panic_in_drop.desc()
-                ));
+                sess.emit_err(IncompatiblePanicInDropStrategy {
+                    crate_name: tcx.crate_name(cnum),
+                    found_strategy: found_drop_strategy,
+                    desired_strategy: tcx.sess.opts.unstable_opts.panic_in_drop,
+                });
             }
         }
     }
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
new file mode 100644
index 00000000000..18d0248333a
--- /dev/null
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -0,0 +1,672 @@
+use std::{
+    io::Error,
+    path::{Path, PathBuf},
+};
+
+use rustc_errors::{error_code, ErrorGuaranteed};
+use rustc_macros::SessionDiagnostic;
+use rustc_session::{config, SessionDiagnostic};
+use rustc_span::{sym, Span, Symbol};
+use rustc_target::spec::{PanicStrategy, TargetTriple};
+
+use crate::locator::CrateFlavor;
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::rlib_required)]
+pub struct RlibRequired {
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::lib_required)]
+pub struct LibRequired<'a> {
+    pub crate_name: Symbol,
+    pub kind: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::crate_dep_multiple)]
+#[help]
+pub struct CrateDepMultiple {
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::two_panic_runtimes)]
+pub struct TwoPanicRuntimes {
+    pub prev_name: Symbol,
+    pub cur_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::bad_panic_strategy)]
+pub struct BadPanicStrategy {
+    pub runtime: Symbol,
+    pub strategy: PanicStrategy,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::required_panic_strategy)]
+pub struct RequiredPanicStrategy {
+    pub crate_name: Symbol,
+    pub found_strategy: PanicStrategy,
+    pub desired_strategy: PanicStrategy,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::incompatible_panic_in_drop_strategy)]
+pub struct IncompatiblePanicInDropStrategy {
+    pub crate_name: Symbol,
+    pub found_strategy: PanicStrategy,
+    pub desired_strategy: PanicStrategy,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_names_in_link)]
+pub struct MultipleNamesInLink {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_kinds_in_link)]
+pub struct MultipleKindsInLink {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_name_form)]
+pub struct LinkNameForm {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_kind_form)]
+pub struct LinkKindForm {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_modifiers_form)]
+pub struct LinkModifiersForm {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_cfg_form)]
+pub struct LinkCfgForm {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::wasm_import_form)]
+pub struct WasmImportForm {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::empty_link_name, code = "E0454")]
+pub struct EmptyLinkName {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_framework_apple, code = "E0455")]
+pub struct LinkFrameworkApple {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::framework_only_windows, code = "E0455")]
+pub struct FrameworkOnlyWindows {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::unknown_link_kind, code = "E0458")]
+pub struct UnknownLinkKind<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub kind: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_link_modifiers)]
+pub struct MultipleLinkModifiers {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_cfgs)]
+pub struct MultipleCfgs {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_cfg_single_predicate)]
+pub struct LinkCfgSinglePredicate {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_wasm_import)]
+pub struct MultipleWasmImport {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::unexpected_link_arg)]
+pub struct UnexpectedLinkArg {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::invalid_link_modifier)]
+pub struct InvalidLinkModifier {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_modifiers)]
+pub struct MultipleModifiers<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub modifier: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::bundle_needs_static)]
+pub struct BundleNeedsStatic {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::whole_archive_needs_static)]
+pub struct WholeArchiveNeedsStatic {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::as_needed_compatibility)]
+pub struct AsNeededCompatibility {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::unknown_link_modifier)]
+pub struct UnknownLinkModifier<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub modifier: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::incompatible_wasm_link)]
+pub struct IncompatibleWasmLink {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_requires_name, code = "E0459")]
+pub struct LinkRequiresName {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::raw_dylib_no_nul)]
+pub struct RawDylibNoNul {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::link_ordinal_raw_dylib)]
+pub struct LinkOrdinalRawDylib {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::lib_framework_apple)]
+pub struct LibFrameworkApple;
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::empty_renaming_target)]
+pub struct EmptyRenamingTarget<'a> {
+    pub lib_name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::renaming_no_link)]
+pub struct RenamingNoLink<'a> {
+    pub lib_name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_renamings)]
+pub struct MultipleRenamings<'a> {
+    pub lib_name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::no_link_mod_override)]
+pub struct NoLinkModOverride {
+    #[primary_span]
+    pub span: Option<Span>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::unsupported_abi_i686)]
+pub struct UnsupportedAbiI686 {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::unsupported_abi)]
+pub struct UnsupportedAbi {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::fail_create_file_encoder)]
+pub struct FailCreateFileEncoder {
+    pub err: Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::fail_seek_file)]
+pub struct FailSeekFile {
+    pub err: Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::fail_write_file)]
+pub struct FailWriteFile {
+    pub err: Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::crate_not_panic_runtime)]
+pub struct CrateNotPanicRuntime {
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::no_panic_strategy)]
+pub struct NoPanicStrategy {
+    pub crate_name: Symbol,
+    pub strategy: PanicStrategy,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::profiler_builtins_needs_core)]
+pub struct ProfilerBuiltinsNeedsCore;
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::not_profiler_runtime)]
+pub struct NotProfilerRuntime {
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::no_multiple_global_alloc)]
+pub struct NoMultipleGlobalAlloc {
+    #[primary_span]
+    #[label]
+    pub span2: Span,
+    #[label(metadata::prev_global_alloc)]
+    pub span1: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::conflicting_global_alloc)]
+pub struct ConflictingGlobalAlloc {
+    pub crate_name: Symbol,
+    pub other_crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::global_alloc_required)]
+pub struct GlobalAllocRequired;
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::no_transitive_needs_dep)]
+pub struct NoTransitiveNeedsDep<'a> {
+    pub crate_name: Symbol,
+    pub needs_crate_name: &'a str,
+    pub deps_crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::failed_write_error)]
+pub struct FailedWriteError {
+    pub filename: PathBuf,
+    pub err: Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::failed_create_tempdir)]
+pub struct FailedCreateTempdir {
+    pub err: Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::failed_create_file)]
+pub struct FailedCreateFile<'a> {
+    pub filename: &'a Path,
+    pub err: Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::failed_create_encoded_metadata)]
+pub struct FailedCreateEncodedMetadata {
+    pub err: Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::non_ascii_name)]
+pub struct NonAsciiName {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::extern_location_not_exist)]
+pub struct ExternLocationNotExist<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub location: &'a Path,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::extern_location_not_file)]
+pub struct ExternLocationNotFile<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub location: &'a Path,
+}
+
+pub(crate) struct MultipleCandidates {
+    pub span: Span,
+    pub flavor: CrateFlavor,
+    pub crate_name: Symbol,
+    pub candidates: Vec<PathBuf>,
+}
+
+impl SessionDiagnostic<'_> for MultipleCandidates {
+    fn into_diagnostic(
+        self,
+        sess: &'_ rustc_session::parse::ParseSess,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = sess.struct_err(rustc_errors::fluent::metadata::multiple_candidates);
+        diag.set_arg("crate_name", self.crate_name);
+        diag.set_arg("flavor", self.flavor);
+        diag.code(error_code!(E0465));
+        diag.set_span(self.span);
+        for (i, candidate) in self.candidates.iter().enumerate() {
+            diag.span_note(self.span, &format!("candidate #{}: {}", i + 1, candidate.display()));
+        }
+        diag
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_matching_crates, code = "E0464")]
+#[note]
+pub struct MultipleMatchingCrates {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub candidates: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::symbol_conflicts_current, code = "E0519")]
+pub struct SymbolConflictsCurrent {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::symbol_conflicts_others, code = "E0523")]
+pub struct SymbolConflictsOthers {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::stable_crate_id_collision)]
+pub struct StableCrateIdCollision {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name0: Symbol,
+    pub crate_name1: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::dl_error)]
+pub struct DlError {
+    #[primary_span]
+    pub span: Span,
+    pub err: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::newer_crate_version, code = "E0460")]
+#[note]
+#[note(metadata::found_crate_versions)]
+pub struct NewerCrateVersion {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub add_info: String,
+    pub found_crates: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::no_crate_with_triple, code = "E0461")]
+#[note(metadata::found_crate_versions)]
+pub struct NoCrateWithTriple<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub locator_triple: &'a str,
+    pub add_info: String,
+    pub found_crates: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::found_staticlib, code = "E0462")]
+#[note(metadata::found_crate_versions)]
+#[help]
+pub struct FoundStaticlib {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub add_info: String,
+    pub found_crates: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::incompatible_rustc, code = "E0514")]
+#[note(metadata::found_crate_versions)]
+#[help]
+pub struct IncompatibleRustc {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub add_info: String,
+    pub found_crates: String,
+    pub rustc_version: String,
+}
+
+pub struct InvalidMetadataFiles {
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub add_info: String,
+    pub crate_rejections: Vec<String>,
+}
+
+impl SessionDiagnostic<'_> for InvalidMetadataFiles {
+    fn into_diagnostic(
+        self,
+        sess: &'_ rustc_session::parse::ParseSess,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = sess.struct_err(rustc_errors::fluent::metadata::invalid_meta_files);
+        diag.set_arg("crate_name", self.crate_name);
+        diag.set_arg("add_info", self.add_info);
+        diag.code(error_code!(E0786));
+        diag.set_span(self.span);
+        for crate_rejection in self.crate_rejections {
+            diag.note(crate_rejection);
+        }
+        diag
+    }
+}
+
+pub struct CannotFindCrate {
+    pub span: Span,
+    pub crate_name: Symbol,
+    pub add_info: String,
+    pub missing_core: bool,
+    pub current_crate: String,
+    pub is_nightly_build: bool,
+    pub profiler_runtime: Symbol,
+    pub locator_triple: TargetTriple,
+}
+
+impl SessionDiagnostic<'_> for CannotFindCrate {
+    fn into_diagnostic(
+        self,
+        sess: &'_ rustc_session::parse::ParseSess,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = sess.struct_err(rustc_errors::fluent::metadata::cannot_find_crate);
+        diag.set_arg("crate_name", self.crate_name);
+        diag.set_arg("add_info", self.add_info);
+        diag.set_arg("locator_triple", self.locator_triple.triple());
+        diag.code(error_code!(E0463));
+        diag.set_span(self.span);
+        if (self.crate_name == sym::std || self.crate_name == sym::core)
+            && self.locator_triple != TargetTriple::from_triple(config::host_triple())
+        {
+            if self.missing_core {
+                diag.note(rustc_errors::fluent::metadata::target_not_installed);
+            } else {
+                diag.note(rustc_errors::fluent::metadata::target_no_std_support);
+            }
+            // NOTE: this suggests using rustup, even though the user may not have it installed.
+            // That's because they could choose to install it; or this may give them a hint which
+            // target they need to install from their distro.
+            if self.missing_core {
+                diag.help(rustc_errors::fluent::metadata::consider_downloading_target);
+            }
+            // Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway.
+            // NOTE: this is a dummy span if `extern crate std` was injected by the compiler.
+            // If it's not a dummy, that means someone added `extern crate std` explicitly and
+            // `#![no_std]` won't help.
+            if !self.missing_core && self.span.is_dummy() {
+                diag.note(rustc_errors::fluent::metadata::std_required);
+            }
+            if self.is_nightly_build {
+                diag.help(rustc_errors::fluent::metadata::consider_building_std);
+            }
+        } else if self.crate_name == self.profiler_runtime {
+            diag.note(rustc_errors::fluent::metadata::compiler_missing_profiler);
+        } else if self.crate_name.as_str().starts_with("rustc_") {
+            diag.help(rustc_errors::fluent::metadata::install_missing_components);
+        }
+        diag.span_label(self.span, rustc_errors::fluent::metadata::cant_find_crate);
+        diag
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::no_dylib_plugin, code = "E0457")]
+pub struct NoDylibPlugin {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::crate_location_unknown_type)]
+pub struct CrateLocationUnknownType<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub path: &'a Path,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::lib_filename_form)]
+pub struct LibFilenameForm<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub dll_prefix: &'a str,
+    pub dll_suffix: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::multiple_import_name_type)]
+pub struct MultipleImportNameType {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::import_name_type_form)]
+pub struct ImportNameTypeForm {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::import_name_type_x86)]
+pub struct ImportNameTypeX86 {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::unknown_import_name_type)]
+pub struct UnknownImportNameType<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub import_name_type: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(metadata::import_name_type_raw)]
+pub struct ImportNameTypeRaw {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index e6072901aaa..f360a586476 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -1,3 +1,6 @@
+use crate::errors::{
+    FailedCreateEncodedMetadata, FailedCreateFile, FailedCreateTempdir, FailedWriteError,
+};
 use crate::{encode_metadata, EncodedMetadata};
 
 use rustc_data_structures::temp_dir::MaybeTempDir;
@@ -23,8 +26,8 @@ pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) ->
     let out_filename = tmpdir.as_ref().join(METADATA_FILENAME);
     let result = fs::write(&out_filename, metadata);
 
-    if let Err(e) = result {
-        sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
+    if let Err(err) = result {
+        sess.emit_fatal(FailedWriteError { filename: out_filename, err });
     }
 
     out_filename
@@ -65,7 +68,7 @@ pub fn encode_and_write_metadata(
     let metadata_tmpdir = TempFileBuilder::new()
         .prefix("rmeta")
         .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
-        .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
+        .unwrap_or_else(|err| tcx.sess.emit_fatal(FailedCreateTempdir { err }));
     let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
     let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME);
 
@@ -73,12 +76,8 @@ pub fn encode_and_write_metadata(
     // This simplifies the creation of the output `out_filename` when requested.
     match metadata_kind {
         MetadataKind::None => {
-            std::fs::File::create(&metadata_filename).unwrap_or_else(|e| {
-                tcx.sess.fatal(&format!(
-                    "failed to create the file {}: {}",
-                    metadata_filename.display(),
-                    e
-                ))
+            std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {
+                tcx.sess.emit_fatal(FailedCreateFile { filename: &metadata_filename, err });
             });
         }
         MetadataKind::Uncompressed | MetadataKind::Compressed => {
@@ -93,8 +92,8 @@ pub fn encode_and_write_metadata(
     // this file always exists.
     let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
     let (metadata_filename, metadata_tmpdir) = if need_metadata_file {
-        if let Err(e) = non_durable_rename(&metadata_filename, &out_filename) {
-            tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
+        if let Err(err) = non_durable_rename(&metadata_filename, &out_filename) {
+            tcx.sess.emit_fatal(FailedWriteError { filename: out_filename, err });
         }
         if tcx.sess.opts.json_artifact_notifications {
             tcx.sess
@@ -109,8 +108,8 @@ pub fn encode_and_write_metadata(
 
     // Load metadata back to memory: codegen may need to include it in object files.
     let metadata =
-        EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|e| {
-            tcx.sess.fatal(&format!("failed to create encoded metadata from file: {}", e))
+        EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| {
+            tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err });
         });
 
     let need_metadata_module = metadata_kind == MetadataKind::Compressed;
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 337d3cca2ae..8e0291fc3ad 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -16,6 +16,8 @@
 #![feature(never_type)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 extern crate proc_macro;
 
@@ -37,6 +39,7 @@ mod native_libs;
 mod rmeta;
 
 pub mod creader;
+pub mod errors;
 pub mod fs;
 pub mod locator;
 
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 5b7d0c8581a..35f9ef92a1c 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -213,6 +213,13 @@
 //! metadata::locator or metadata::creader for all the juicy details!
 
 use crate::creader::Library;
+use crate::errors::{
+    CannotFindCrate, CrateLocationUnknownType, DlError, ExternLocationNotExist,
+    ExternLocationNotFile, FoundStaticlib, IncompatibleRustc, InvalidMetadataFiles,
+    LibFilenameForm, MultipleCandidates, MultipleMatchingCrates, NewerCrateVersion,
+    NoCrateWithTriple, NoDylibPlugin, NonAsciiName, StableCrateIdCollision, SymbolConflictsCurrent,
+    SymbolConflictsOthers,
+};
 use crate::rmeta::{rustc_version, MetadataBlob, METADATA_HEADER};
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -220,18 +227,19 @@ use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::owning_ref::OwningRef;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::MetadataRef;
-use rustc_errors::{struct_span_err, FatalError};
+use rustc_errors::{DiagnosticArgValue, FatalError, IntoDiagnosticArg};
 use rustc_session::config::{self, CrateType};
 use rustc_session::cstore::{CrateSource, MetadataLoader};
 use rustc_session::filesearch::FileSearch;
 use rustc_session::search_paths::PathKind;
 use rustc_session::utils::CanonicalizedPath;
 use rustc_session::Session;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::Symbol;
 use rustc_span::Span;
 use rustc_target::spec::{Target, TargetTriple};
 
 use snap::read::FrameDecoder;
+use std::borrow::Cow;
 use std::fmt::Write as _;
 use std::io::{Read, Result as IoResult, Write};
 use std::path::{Path, PathBuf};
@@ -287,6 +295,16 @@ impl fmt::Display for CrateFlavor {
     }
 }
 
+impl IntoDiagnosticArg for CrateFlavor {
+    fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+        match self {
+            CrateFlavor::Rlib => DiagnosticArgValue::Str(Cow::Borrowed("rlib")),
+            CrateFlavor::Rmeta => DiagnosticArgValue::Str(Cow::Borrowed("rmeta")),
+            CrateFlavor::Dylib => DiagnosticArgValue::Str(Cow::Borrowed("dylib")),
+        }
+    }
+}
+
 impl<'a> CrateLocator<'a> {
     pub(crate) fn new(
         sess: &'a Session,
@@ -937,41 +955,20 @@ impl fmt::Display for MetadataError<'_> {
 
 impl CrateError {
     pub(crate) fn report(self, sess: &Session, span: Span, missing_core: bool) {
-        let mut diag = match self {
-            CrateError::NonAsciiName(crate_name) => sess.struct_span_err(
-                span,
-                &format!("cannot load a crate with a non-ascii name `{}`", crate_name),
-            ),
-            CrateError::ExternLocationNotExist(crate_name, loc) => sess.struct_span_err(
-                span,
-                &format!("extern location for {} does not exist: {}", crate_name, loc.display()),
-            ),
-            CrateError::ExternLocationNotFile(crate_name, loc) => sess.struct_span_err(
-                span,
-                &format!("extern location for {} is not a file: {}", crate_name, loc.display()),
-            ),
+        match self {
+            CrateError::NonAsciiName(crate_name) => {
+                sess.emit_err(NonAsciiName { span, crate_name });
+            }
+            CrateError::ExternLocationNotExist(crate_name, loc) => {
+                sess.emit_err(ExternLocationNotExist { span, crate_name, location: &loc });
+            }
+            CrateError::ExternLocationNotFile(crate_name, loc) => {
+                sess.emit_err(ExternLocationNotFile { span, crate_name, location: &loc });
+            }
             CrateError::MultipleCandidates(crate_name, flavor, candidates) => {
-                let mut err = struct_span_err!(
-                    sess,
-                    span,
-                    E0465,
-                    "multiple {} candidates for `{}` found",
-                    flavor,
-                    crate_name,
-                );
-                for (i, candidate) in candidates.iter().enumerate() {
-                    err.span_note(span, &format!("candidate #{}: {}", i + 1, candidate.display()));
-                }
-                err
+                sess.emit_err(MultipleCandidates { span, flavor: flavor, crate_name, candidates });
             }
             CrateError::MultipleMatchingCrates(crate_name, libraries) => {
-                let mut err = struct_span_err!(
-                    sess,
-                    span,
-                    E0464,
-                    "multiple matching crates for `{}`",
-                    crate_name
-                );
                 let mut libraries: Vec<_> = libraries.into_values().collect();
                 // Make ordering of candidates deterministic.
                 // This has to `clone()` to work around lifetime restrictions with `sort_by_key()`.
@@ -999,223 +996,142 @@ impl CrateError {
                         s
                     })
                     .collect::<String>();
-                err.note(&format!("candidates:{}", candidates));
-                err
+                sess.emit_err(MultipleMatchingCrates { span, crate_name, candidates });
+            }
+            CrateError::SymbolConflictsCurrent(root_name) => {
+                sess.emit_err(SymbolConflictsCurrent { span, crate_name: root_name });
+            }
+            CrateError::SymbolConflictsOthers(root_name) => {
+                sess.emit_err(SymbolConflictsOthers { span, crate_name: root_name });
             }
-            CrateError::SymbolConflictsCurrent(root_name) => struct_span_err!(
-                sess,
-                span,
-                E0519,
-                "the current crate is indistinguishable from one of its dependencies: it has the \
-                 same crate-name `{}` and was compiled with the same `-C metadata` arguments. \
-                 This will result in symbol conflicts between the two.",
-                root_name,
-            ),
-            CrateError::SymbolConflictsOthers(root_name) => struct_span_err!(
-                sess,
-                span,
-                E0523,
-                "found two different crates with name `{}` that are not distinguished by differing \
-                 `-C metadata`. This will result in symbol conflicts between the two.",
-                root_name,
-            ),
             CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
-                let msg = format!(
-                    "found crates (`{}` and `{}`) with colliding StableCrateId values.",
-                    crate_name0, crate_name1
-                );
-                sess.struct_span_err(span, &msg)
+                sess.emit_err(StableCrateIdCollision {
+                    span,
+                    crate_name0: crate_name0,
+                    crate_name1: crate_name1,
+                });
+            }
+            CrateError::DlOpen(s) | CrateError::DlSym(s) => {
+                sess.emit_err(DlError { span, err: s });
             }
-            CrateError::DlOpen(s) | CrateError::DlSym(s) => sess.struct_span_err(span, &s),
             CrateError::LocatorCombined(locator) => {
                 let crate_name = locator.crate_name;
-                let add = match &locator.root {
+                let add_info = match &locator.root {
                     None => String::new(),
                     Some(r) => format!(" which `{}` depends on", r.name),
                 };
-                let mut msg = "the following crate versions were found:".to_string();
-                let mut err = if !locator.crate_rejections.via_hash.is_empty() {
-                    let mut err = struct_span_err!(
-                        sess,
-                        span,
-                        E0460,
-                        "found possibly newer version of crate `{}`{}",
-                        crate_name,
-                        add,
-                    );
-                    err.note("perhaps that crate needs to be recompiled?");
+                // FIXME: There are no tests for CrateLocationUnknownType or LibFilenameForm
+                if !locator.crate_rejections.via_filename.is_empty() {
+                    let mismatches = locator.crate_rejections.via_filename.iter();
+                    for CrateMismatch { path, .. } in mismatches {
+                        sess.emit_err(CrateLocationUnknownType { span, path: &path });
+                        sess.emit_err(LibFilenameForm {
+                            span,
+                            dll_prefix: &locator.dll_prefix,
+                            dll_suffix: &locator.dll_suffix,
+                        });
+                    }
+                }
+                let mut found_crates = String::new();
+                if !locator.crate_rejections.via_hash.is_empty() {
                     let mismatches = locator.crate_rejections.via_hash.iter();
                     for CrateMismatch { path, .. } in mismatches {
-                        msg.push_str(&format!("\ncrate `{}`: {}", crate_name, path.display()));
+                        found_crates.push_str(&format!(
+                            "\ncrate `{}`: {}",
+                            crate_name,
+                            path.display()
+                        ));
                     }
                     if let Some(r) = locator.root {
                         for path in r.source.paths() {
-                            msg.push_str(&format!("\ncrate `{}`: {}", r.name, path.display()));
+                            found_crates.push_str(&format!(
+                                "\ncrate `{}`: {}",
+                                r.name,
+                                path.display()
+                            ));
                         }
                     }
-                    err.note(&msg);
-                    err
-                } else if !locator.crate_rejections.via_triple.is_empty() {
-                    let mut err = struct_span_err!(
-                        sess,
+                    sess.emit_err(NewerCrateVersion {
                         span,
-                        E0461,
-                        "couldn't find crate `{}` with expected target triple {}{}",
-                        crate_name,
-                        locator.triple,
-                        add,
-                    );
+                        crate_name: crate_name,
+                        add_info,
+                        found_crates,
+                    });
+                } else if !locator.crate_rejections.via_triple.is_empty() {
                     let mismatches = locator.crate_rejections.via_triple.iter();
                     for CrateMismatch { path, got } in mismatches {
-                        msg.push_str(&format!(
+                        found_crates.push_str(&format!(
                             "\ncrate `{}`, target triple {}: {}",
                             crate_name,
                             got,
                             path.display(),
                         ));
                     }
-                    err.note(&msg);
-                    err
-                } else if !locator.crate_rejections.via_kind.is_empty() {
-                    let mut err = struct_span_err!(
-                        sess,
+                    sess.emit_err(NoCrateWithTriple {
                         span,
-                        E0462,
-                        "found staticlib `{}` instead of rlib or dylib{}",
-                        crate_name,
-                        add,
-                    );
-                    err.help("please recompile that crate using --crate-type lib");
+                        crate_name: crate_name,
+                        locator_triple: locator.triple.triple(),
+                        add_info,
+                        found_crates,
+                    });
+                } else if !locator.crate_rejections.via_kind.is_empty() {
                     let mismatches = locator.crate_rejections.via_kind.iter();
                     for CrateMismatch { path, .. } in mismatches {
-                        msg.push_str(&format!("\ncrate `{}`: {}", crate_name, path.display()));
+                        found_crates.push_str(&format!(
+                            "\ncrate `{}`: {}",
+                            crate_name,
+                            path.display()
+                        ));
                     }
-                    err.note(&msg);
-                    err
+                    sess.emit_err(FoundStaticlib { span, crate_name, add_info, found_crates });
                 } else if !locator.crate_rejections.via_version.is_empty() {
-                    let mut err = struct_span_err!(
-                        sess,
-                        span,
-                        E0514,
-                        "found crate `{}` compiled by an incompatible version of rustc{}",
-                        crate_name,
-                        add,
-                    );
-                    err.help(&format!(
-                        "please recompile that crate using this compiler ({}) \
-                         (consider running `cargo clean` first)",
-                        rustc_version(),
-                    ));
                     let mismatches = locator.crate_rejections.via_version.iter();
                     for CrateMismatch { path, got } in mismatches {
-                        msg.push_str(&format!(
+                        found_crates.push_str(&format!(
                             "\ncrate `{}` compiled by {}: {}",
                             crate_name,
                             got,
                             path.display(),
                         ));
                     }
-                    err.note(&msg);
-                    err
-                } else if !locator.crate_rejections.via_invalid.is_empty() {
-                    let mut err = struct_span_err!(
-                        sess,
+                    sess.emit_err(IncompatibleRustc {
                         span,
-                        E0786,
-                        "found invalid metadata files for crate `{}`{}",
                         crate_name,
-                        add,
-                    );
+                        add_info,
+                        found_crates,
+                        rustc_version: rustc_version(),
+                    });
+                } else if !locator.crate_rejections.via_invalid.is_empty() {
+                    let mut crate_rejections = Vec::new();
                     for CrateMismatch { path: _, got } in locator.crate_rejections.via_invalid {
-                        err.note(&got);
+                        crate_rejections.push(got);
                     }
-                    err
+                    sess.emit_err(InvalidMetadataFiles {
+                        span,
+                        crate_name,
+                        add_info,
+                        crate_rejections,
+                    });
                 } else {
-                    let mut err = struct_span_err!(
-                        sess,
+                    sess.emit_err(CannotFindCrate {
                         span,
-                        E0463,
-                        "can't find crate for `{}`{}",
                         crate_name,
-                        add,
-                    );
-
-                    if (crate_name == sym::std || crate_name == sym::core)
-                        && locator.triple != TargetTriple::from_triple(config::host_triple())
-                    {
-                        if missing_core {
-                            err.note(&format!(
-                                "the `{}` target may not be installed",
-                                locator.triple
-                            ));
-                        } else {
-                            err.note(&format!(
-                                "the `{}` target may not support the standard library",
-                                locator.triple
-                            ));
-                        }
-                        // NOTE: this suggests using rustup, even though the user may not have it installed.
-                        // That's because they could choose to install it; or this may give them a hint which
-                        // target they need to install from their distro.
-                        if missing_core {
-                            err.help(&format!(
-                                "consider downloading the target with `rustup target add {}`",
-                                locator.triple
-                            ));
-                        }
-                        // Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway.
-                        // NOTE: this is a dummy span if `extern crate std` was injected by the compiler.
-                        // If it's not a dummy, that means someone added `extern crate std` explicitly and `#![no_std]` won't help.
-                        if !missing_core && span.is_dummy() {
-                            let current_crate =
-                                sess.opts.crate_name.as_deref().unwrap_or("<unknown>");
-                            err.note(&format!(
-                                "`std` is required by `{}` because it does not declare `#![no_std]`",
-                                current_crate
-                            ));
-                        }
-                        if sess.is_nightly_build() {
-                            err.help("consider building the standard library from source with `cargo build -Zbuild-std`");
-                        }
-                    } else if crate_name
-                        == Symbol::intern(&sess.opts.unstable_opts.profiler_runtime)
-                    {
-                        err.note("the compiler may have been built without the profiler runtime");
-                    } else if crate_name.as_str().starts_with("rustc_") {
-                        err.help(
-                            "maybe you need to install the missing components with: \
-                             `rustup component add rust-src rustc-dev llvm-tools-preview`",
-                        );
-                    }
-                    err.span_label(span, "can't find crate");
-                    err
-                };
-
-                if !locator.crate_rejections.via_filename.is_empty() {
-                    let mismatches = locator.crate_rejections.via_filename.iter();
-                    for CrateMismatch { path, .. } in mismatches {
-                        err.note(&format!(
-                            "extern location for {} is of an unknown type: {}",
-                            crate_name,
-                            path.display(),
-                        ))
-                        .help(&format!(
-                            "file name should be lib*.rlib or {}*.{}",
-                            locator.dll_prefix, locator.dll_suffix
-                        ));
-                    }
+                        add_info,
+                        missing_core,
+                        current_crate: sess
+                            .opts
+                            .crate_name
+                            .clone()
+                            .unwrap_or("<unknown>".to_string()),
+                        is_nightly_build: sess.is_nightly_build(),
+                        profiler_runtime: Symbol::intern(&sess.opts.unstable_opts.profiler_runtime),
+                        locator_triple: locator.triple,
+                    });
                 }
-                err
             }
-            CrateError::NonDylibPlugin(crate_name) => struct_span_err!(
-                sess,
-                span,
-                E0457,
-                "plugin `{}` only found in rlib format, but must be available in dylib format",
-                crate_name,
-            ),
-        };
-
-        diag.emit();
+            CrateError::NonDylibPlugin(crate_name) => {
+                sess.emit_err(NoDylibPlugin { span, crate_name });
+            }
+        }
     }
 }
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 8bafe203748..87b5e750f1c 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -1,7 +1,6 @@
 use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
@@ -12,6 +11,18 @@ use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_target::spec::abi::Abi;
 
+use crate::errors::{
+    AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, EmptyRenamingTarget,
+    FrameworkOnlyWindows, ImportNameTypeForm, ImportNameTypeRaw, ImportNameTypeX86,
+    IncompatibleWasmLink, InvalidLinkModifier, LibFrameworkApple, LinkCfgForm,
+    LinkCfgSinglePredicate, LinkFrameworkApple, LinkKindForm, LinkModifiersForm, LinkNameForm,
+    LinkOrdinalRawDylib, LinkRequiresName, MultipleCfgs, MultipleImportNameType,
+    MultipleKindsInLink, MultipleLinkModifiers, MultipleModifiers, MultipleNamesInLink,
+    MultipleRenamings, MultipleWasmImport, NoLinkModOverride, RawDylibNoNul, RenamingNoLink,
+    UnexpectedLinkArg, UnknownImportNameType, UnknownLinkKind, UnknownLinkModifier, UnsupportedAbi,
+    UnsupportedAbiI686, WasmImportForm, WholeArchiveNeedsStatic,
+};
+
 pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
     let mut collector = Collector { tcx, libs: Vec::new() };
     for id in tcx.hir().items() {
@@ -66,32 +77,26 @@ impl<'tcx> Collector<'tcx> {
                 match item.name_or_empty() {
                     sym::name => {
                         if name.is_some() {
-                            let msg = "multiple `name` arguments in a single `#[link]` attribute";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(MultipleNamesInLink { span: item.span() });
                             continue;
                         }
                         let Some(link_name) = item.value_str() else {
-                            let msg = "link name must be of the form `name = \"string\"`";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(LinkNameForm { span: item.span() });
                             continue;
                         };
                         let span = item.name_value_literal_span().unwrap();
                         if link_name.is_empty() {
-                            struct_span_err!(sess, span, E0454, "link name must not be empty")
-                                .span_label(span, "empty link name")
-                                .emit();
+                            sess.emit_err(EmptyLinkName { span });
                         }
                         name = Some((link_name, span));
                     }
                     sym::kind => {
                         if kind.is_some() {
-                            let msg = "multiple `kind` arguments in a single `#[link]` attribute";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(MultipleKindsInLink { span: item.span() });
                             continue;
                         }
                         let Some(link_kind) = item.value_str() else {
-                            let msg = "link kind must be of the form `kind = \"string\"`";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(LinkKindForm { span: item.span() });
                             continue;
                         };
 
@@ -101,25 +106,13 @@ impl<'tcx> Collector<'tcx> {
                             "dylib" => NativeLibKind::Dylib { as_needed: None },
                             "framework" => {
                                 if !sess.target.is_like_osx {
-                                    struct_span_err!(
-                                        sess,
-                                        span,
-                                        E0455,
-                                        "link kind `framework` is only supported on Apple targets"
-                                    )
-                                    .emit();
+                                    sess.emit_err(LinkFrameworkApple { span });
                                 }
                                 NativeLibKind::Framework { as_needed: None }
                             }
                             "raw-dylib" => {
                                 if !sess.target.is_like_windows {
-                                    struct_span_err!(
-                                        sess,
-                                        span,
-                                        E0455,
-                                        "link kind `raw-dylib` is only supported on Windows targets"
-                                    )
-                                    .emit();
+                                    sess.emit_err(FrameworkOnlyWindows { span });
                                 } else if !features.raw_dylib {
                                     feature_err(
                                         &sess.parse_sess,
@@ -132,13 +125,7 @@ impl<'tcx> Collector<'tcx> {
                                 NativeLibKind::RawDylib
                             }
                             kind => {
-                                let msg = format!(
-                                    "unknown link kind `{kind}`, expected one of: \
-                                     static, dylib, framework, raw-dylib"
-                                );
-                                struct_span_err!(sess, span, E0458, "{}", msg)
-                                    .span_label(span, "unknown link kind")
-                                    .emit();
+                                sess.emit_err(UnknownLinkKind { span, kind });
                                 continue;
                             }
                         };
@@ -146,32 +133,26 @@ impl<'tcx> Collector<'tcx> {
                     }
                     sym::modifiers => {
                         if modifiers.is_some() {
-                            let msg =
-                                "multiple `modifiers` arguments in a single `#[link]` attribute";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(MultipleLinkModifiers { span: item.span() });
                             continue;
                         }
                         let Some(link_modifiers) = item.value_str() else {
-                            let msg = "link modifiers must be of the form `modifiers = \"string\"`";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(LinkModifiersForm { span: item.span() });
                             continue;
                         };
                         modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
                     }
                     sym::cfg => {
                         if cfg.is_some() {
-                            let msg = "multiple `cfg` arguments in a single `#[link]` attribute";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(MultipleCfgs { span: item.span() });
                             continue;
                         }
                         let Some(link_cfg) = item.meta_item_list() else {
-                            let msg = "link cfg must be of the form `cfg(/* predicate */)`";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(LinkCfgForm { span: item.span() });
                             continue;
                         };
                         let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
-                            let msg = "link cfg must have a single predicate argument";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(LinkCfgSinglePredicate { span: item.span() });
                             continue;
                         };
                         if !features.link_cfg {
@@ -187,33 +168,26 @@ impl<'tcx> Collector<'tcx> {
                     }
                     sym::wasm_import_module => {
                         if wasm_import_module.is_some() {
-                            let msg = "multiple `wasm_import_module` arguments \
-                                       in a single `#[link]` attribute";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(MultipleWasmImport { span: item.span() });
                             continue;
                         }
                         let Some(link_wasm_import_module) = item.value_str() else {
-                            let msg = "wasm import module must be of the form \
-                                       `wasm_import_module = \"string\"`";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(WasmImportForm { span: item.span() });
                             continue;
                         };
                         wasm_import_module = Some((link_wasm_import_module, item.span()));
                     }
                     sym::import_name_type => {
                         if import_name_type.is_some() {
-                            let msg = "multiple `import_name_type` arguments in a single `#[link]` attribute";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(MultipleImportNameType { span: item.span() });
                             continue;
                         }
                         let Some(link_import_name_type) = item.value_str() else {
-                            let msg = "import name type must be of the form `import_name_type = \"string\"`";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(ImportNameTypeForm { span: item.span() });
                             continue;
                         };
                         if self.tcx.sess.target.arch != "x86" {
-                            let msg = "import name type is only supported on x86";
-                            sess.span_err(item.span(), msg);
+                            sess.emit_err(ImportNameTypeX86 { span: item.span() });
                             continue;
                         }
 
@@ -222,11 +196,10 @@ impl<'tcx> Collector<'tcx> {
                             "noprefix" => PeImportNameType::NoPrefix,
                             "undecorated" => PeImportNameType::Undecorated,
                             import_name_type => {
-                                let msg = format!(
-                                    "unknown import name type `{import_name_type}`, expected one of: \
-                                     decorated, noprefix, undecorated"
-                                );
-                                sess.span_err(item.span(), msg);
+                                sess.emit_err(UnknownImportNameType {
+                                    span: item.span(),
+                                    import_name_type,
+                                });
                                 continue;
                             }
                         };
@@ -243,9 +216,7 @@ impl<'tcx> Collector<'tcx> {
                         import_name_type = Some((link_import_name_type, item.span()));
                     }
                     _ => {
-                        let msg = "unexpected `#[link]` argument, expected one of: \
-                                   name, kind, modifiers, cfg, wasm_import_module, import_name_type";
-                        sess.span_err(item.span(), msg);
+                        sess.emit_err(UnexpectedLinkArg { span: item.span() });
                     }
                 }
             }
@@ -257,11 +228,7 @@ impl<'tcx> Collector<'tcx> {
                     let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
                         Some(m) => (m, modifier.starts_with('+')),
                         None => {
-                            sess.span_err(
-                                span,
-                                "invalid linking modifier syntax, expected '+' or '-' prefix \
-                                before one of: bundle, verbatim, whole-archive, as-needed",
-                            );
+                            sess.emit_err(InvalidLinkModifier { span });
                             continue;
                         }
                     };
@@ -279,10 +246,7 @@ impl<'tcx> Collector<'tcx> {
                     }
                     let assign_modifier = |dst: &mut Option<bool>| {
                         if dst.is_some() {
-                            let msg = format!(
-                                "multiple `{modifier}` modifiers in a single `modifiers` argument"
-                            );
-                            sess.span_err(span, &msg);
+                            sess.emit_err(MultipleModifiers { span, modifier });
                         } else {
                             *dst = Some(value);
                         }
@@ -292,11 +256,7 @@ impl<'tcx> Collector<'tcx> {
                             assign_modifier(bundle)
                         }
                         ("bundle", _) => {
-                            sess.span_err(
-                                span,
-                                "linking modifier `bundle` is only compatible with \
-                                 `static` linking kind",
-                            );
+                            sess.emit_err(BundleNeedsStatic { span });
                         }
 
                         ("verbatim", _) => {
@@ -308,11 +268,7 @@ impl<'tcx> Collector<'tcx> {
                             assign_modifier(whole_archive)
                         }
                         ("whole-archive", _) => {
-                            sess.span_err(
-                                span,
-                                "linking modifier `whole-archive` is only compatible with \
-                                 `static` linking kind",
-                            );
+                            sess.emit_err(WholeArchiveNeedsStatic { span });
                         }
 
                         ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
@@ -321,21 +277,11 @@ impl<'tcx> Collector<'tcx> {
                             assign_modifier(as_needed)
                         }
                         ("as-needed", _) => {
-                            sess.span_err(
-                                span,
-                                "linking modifier `as-needed` is only compatible with \
-                                 `dylib` and `framework` linking kinds",
-                            );
+                            sess.emit_err(AsNeededCompatibility { span });
                         }
 
                         _ => {
-                            sess.span_err(
-                                span,
-                                format!(
-                                    "unknown linking modifier `{modifier}`, expected one of: \
-                                     bundle, verbatim, whole-archive, as-needed"
-                                ),
-                            );
+                            sess.emit_err(UnknownLinkModifier { span, modifier });
                         }
                     }
                 }
@@ -343,36 +289,23 @@ impl<'tcx> Collector<'tcx> {
 
             if let Some((_, span)) = wasm_import_module {
                 if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
-                    let msg = "`wasm_import_module` is incompatible with \
-                               other arguments in `#[link]` attributes";
-                    sess.span_err(span, msg);
+                    sess.emit_err(IncompatibleWasmLink { span });
                 }
             } else if name.is_none() {
-                struct_span_err!(
-                    sess,
-                    m.span,
-                    E0459,
-                    "`#[link]` attribute requires a `name = \"string\"` argument"
-                )
-                .span_label(m.span, "missing `name` argument")
-                .emit();
+                sess.emit_err(LinkRequiresName { span: m.span });
             }
 
             // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
             if let Some((_, span)) = import_name_type {
                 if kind != Some(NativeLibKind::RawDylib) {
-                    let msg = "import name type can only be used with link kind `raw-dylib`";
-                    sess.span_err(span, msg);
+                    sess.emit_err(ImportNameTypeRaw { span });
                 }
             }
 
             let dll_imports = match kind {
                 Some(NativeLibKind::RawDylib) => {
                     if let Some((name, span)) = name && name.as_str().contains('\0') {
-                        sess.span_err(
-                            span,
-                            "link name must not contain NUL characters if link kind is `raw-dylib`",
-                        );
+                        sess.emit_err(RawDylibNoNul { span });
                     }
                     foreign_mod_items
                         .iter()
@@ -401,10 +334,7 @@ impl<'tcx> Collector<'tcx> {
                                 .iter()
                                 .find(|a| a.has_name(sym::link_ordinal))
                                 .unwrap();
-                            sess.span_err(
-                                link_ordinal_attr.span,
-                                "`#[link_ordinal]` is only supported if link kind is `raw-dylib`",
-                            );
+                            sess.emit_err(LinkOrdinalRawDylib { span: link_ordinal_attr.span });
                         }
                     }
 
@@ -430,7 +360,7 @@ impl<'tcx> Collector<'tcx> {
         for lib in &self.tcx.sess.opts.libs {
             if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
                 // Cannot check this when parsing options because the target is not yet available.
-                self.tcx.sess.err("library kind `framework` is only supported on Apple targets");
+                self.tcx.sess.emit_err(LibFrameworkApple);
             }
             if let Some(ref new_name) = lib.new_name {
                 let any_duplicate = self
@@ -439,23 +369,11 @@ impl<'tcx> Collector<'tcx> {
                     .filter_map(|lib| lib.name.as_ref())
                     .any(|n| n.as_str() == lib.name);
                 if new_name.is_empty() {
-                    self.tcx.sess.err(format!(
-                        "an empty renaming target was specified for library `{}`",
-                        lib.name
-                    ));
+                    self.tcx.sess.emit_err(EmptyRenamingTarget { lib_name: &lib.name });
                 } else if !any_duplicate {
-                    self.tcx.sess.err(format!(
-                        "renaming of the library `{}` was specified, \
-                                                however this crate contains no `#[link(...)]` \
-                                                attributes referencing this library",
-                        lib.name
-                    ));
+                    self.tcx.sess.emit_err(RenamingNoLink { lib_name: &lib.name });
                 } else if !renames.insert(&lib.name) {
-                    self.tcx.sess.err(format!(
-                        "multiple renamings were \
-                                                specified for library `{}`",
-                        lib.name
-                    ));
+                    self.tcx.sess.emit_err(MultipleRenamings { lib_name: &lib.name });
                 }
             }
         }
@@ -480,10 +398,13 @@ impl<'tcx> Collector<'tcx> {
                             // involved or not, library reordering and kind overriding without
                             // explicit `:rename` in particular.
                             if lib.has_modifiers() || passed_lib.has_modifiers() {
-                                let msg = "overriding linking modifiers from command line is not supported";
                                 match lib.foreign_module {
-                                    Some(def_id) => self.tcx.sess.span_err(self.tcx.def_span(def_id), msg),
-                                    None => self.tcx.sess.err(msg),
+                                    Some(def_id) => self.tcx.sess.emit_err(NoLinkModOverride {
+                                        span: Some(self.tcx.def_span(def_id)),
+                                    }),
+                                    None => {
+                                        self.tcx.sess.emit_err(NoLinkModOverride { span: None })
+                                    }
                                 };
                             }
                             if passed_lib.kind != NativeLibKind::Unspecified {
@@ -562,20 +483,14 @@ impl<'tcx> Collector<'tcx> {
                     DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
                 }
                 _ => {
-                    self.tcx.sess.span_fatal(
-                        item.span,
-                        r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#,
-                    );
+                    self.tcx.sess.emit_fatal(UnsupportedAbiI686 { span: item.span });
                 }
             }
         } else {
             match abi {
                 Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
                 _ => {
-                    self.tcx.sess.span_fatal(
-                        item.span,
-                        r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#,
-                    );
+                    self.tcx.sess.emit_fatal(UnsupportedAbi { span: item.span });
                 }
             }
         };
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 4be4d4b7872..8dc5ed2db7e 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1,3 +1,4 @@
+use crate::errors::{FailCreateFileEncoder, FailSeekFile, FailWriteFile};
 use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
 use crate::rmeta::table::TableBuilder;
 use crate::rmeta::*;
@@ -2195,7 +2196,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
 
 fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
     let mut encoder = opaque::FileEncoder::new(path)
-        .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to create file encoder: {}", err)));
+        .unwrap_or_else(|err| tcx.sess.emit_fatal(FailCreateFileEncoder { err }));
     encoder.emit_raw_bytes(METADATA_HEADER);
 
     // Will be filled with the root position after encoding everything.
@@ -2240,10 +2241,10 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
     // Encode the root position.
     let header = METADATA_HEADER.len();
     file.seek(std::io::SeekFrom::Start(header as u64))
-        .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to seek the file: {}", err)));
+        .unwrap_or_else(|err| tcx.sess.emit_fatal(FailSeekFile { err }));
     let pos = root.position.get();
     file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8])
-        .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to write to the file: {}", err)));
+        .unwrap_or_else(|err| tcx.sess.emit_fatal(FailWriteFile { err }));
 
     // Return to the position where we are before writing the root position.
     file.seek(std::io::SeekFrom::Start(pos_before_seek)).unwrap();