about summary refs log tree commit diff
path: root/compiler/rustc_incremental/src
diff options
context:
space:
mode:
authorDavid Wood <david.wood@huawei.com>2022-09-26 15:42:12 +0100
committerDavid Wood <david.wood@huawei.com>2023-01-30 17:11:35 +0000
commit2ff46641a92c27a32db3e0dc94ae86295e6c3277 (patch)
treea195b20cc8254c906535717f3f37a6fd074cedbe /compiler/rustc_incremental/src
parent2575b1abc97e1352b307163ac7de2142aded22a5 (diff)
downloadrust-2ff46641a92c27a32db3e0dc94ae86295e6c3277.tar.gz
rust-2ff46641a92c27a32db3e0dc94ae86295e6c3277.zip
incremental: migrate diagnostics
Migrate the `rustc_incremental` crate's diagnostics to translatable
diagnostic structs.

Signed-off-by: David Wood <david.wood@huawei.com>
Diffstat (limited to 'compiler/rustc_incremental/src')
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs40
-rw-r--r--compiler/rustc_incremental/src/assert_module_sources.rs50
-rw-r--r--compiler/rustc_incremental/src/errors.rs364
-rw-r--r--compiler/rustc_incremental/src/lib.rs4
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs63
-rw-r--r--compiler/rustc_incremental/src/persist/file_format.rs21
-rw-r--r--compiler/rustc_incremental/src/persist/fs.rs111
-rw-r--r--compiler/rustc_incremental/src/persist/load.rs54
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs24
-rw-r--r--compiler/rustc_incremental/src/persist/work_product.rs18
10 files changed, 497 insertions, 252 deletions
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index 6703d53f380..22bd12f2e63 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -33,6 +33,7 @@
 //! fn baz() { foo(); }
 //! ```
 
+use crate::errors;
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING};
@@ -133,12 +134,10 @@ impl<'tcx> IfThisChanged<'tcx> {
                     Some(n) => {
                         match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
                             Ok(n) => n,
-                            Err(()) => {
-                                self.tcx.sess.span_fatal(
-                                    attr.span,
-                                    &format!("unrecognized DepNode variant {:?}", n),
-                                );
-                            }
+                            Err(()) => self.tcx.sess.emit_fatal(errors::UnrecognizedDepNode {
+                                span: attr.span,
+                                name: n,
+                            }),
                         }
                     }
                 };
@@ -149,16 +148,14 @@ impl<'tcx> IfThisChanged<'tcx> {
                     Some(n) => {
                         match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
                             Ok(n) => n,
-                            Err(()) => {
-                                self.tcx.sess.span_fatal(
-                                    attr.span,
-                                    &format!("unrecognized DepNode variant {:?}", n),
-                                );
-                            }
+                            Err(()) => self.tcx.sess.emit_fatal(errors::UnrecognizedDepNode {
+                                span: attr.span,
+                                name: n,
+                            }),
                         }
                     }
                     None => {
-                        self.tcx.sess.span_fatal(attr.span, "missing DepNode variant");
+                        self.tcx.sess.emit_fatal(errors::MissingDepNode { span: attr.span });
                     }
                 };
                 self.then_this_would_need.push((
@@ -204,7 +201,7 @@ fn check_paths<'tcx>(tcx: TyCtxt<'tcx>, if_this_changed: &Sources, then_this_wou
     // Return early here so as not to construct the query, which is not cheap.
     if if_this_changed.is_empty() {
         for &(target_span, _, _, _) in then_this_would_need {
-            tcx.sess.span_err(target_span, "no `#[rustc_if_this_changed]` annotation detected");
+            tcx.sess.emit_err(errors::MissingIfThisChanged { span: target_span });
         }
         return;
     }
@@ -213,16 +210,13 @@ fn check_paths<'tcx>(tcx: TyCtxt<'tcx>, if_this_changed: &Sources, then_this_wou
             let dependents = query.transitive_predecessors(source_dep_node);
             for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need {
                 if !dependents.contains(&target_dep_node) {
-                    tcx.sess.span_err(
-                        target_span,
-                        &format!(
-                            "no path from `{}` to `{}`",
-                            tcx.def_path_str(source_def_id),
-                            target_pass
-                        ),
-                    );
+                    tcx.sess.emit_err(errors::NoPath {
+                        span: target_span,
+                        source: tcx.def_path_str(source_def_id),
+                        target: *target_pass,
+                    });
                 } else {
-                    tcx.sess.span_err(target_span, "OK");
+                    tcx.sess.emit_err(errors::Ok { span: target_span });
                 }
             }
         }
diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs
index 89d419bc8e9..2968a0e1203 100644
--- a/compiler/rustc_incremental/src/assert_module_sources.rs
+++ b/compiler/rustc_incremental/src/assert_module_sources.rs
@@ -22,6 +22,7 @@
 //! allows for doing a more fine-grained check to see if pre- or post-lto data
 //! was re-used.
 
+use crate::errors;
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::LOCAL_CRATE;
@@ -66,10 +67,9 @@ impl<'tcx> AssertModuleSource<'tcx> {
                 sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
                 sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
                 other => {
-                    self.tcx.sess.span_fatal(
-                        attr.span,
-                        &format!("unknown cgu-reuse-kind `{}` specified", other),
-                    );
+                    self.tcx
+                        .sess
+                        .emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other });
                 }
             }
         } else {
@@ -77,10 +77,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
         };
 
         if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
-            self.tcx.sess.span_fatal(
-                attr.span,
-                "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified",
-            );
+            self.tcx.sess.emit_fatal(errors::MissingQueryDepGraph { span: attr.span });
         }
 
         if !self.check_config(attr) {
@@ -92,13 +89,11 @@ impl<'tcx> AssertModuleSource<'tcx> {
         let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string();
 
         if !user_path.starts_with(&crate_name) {
-            let msg = format!(
-                "Found malformed codegen unit name `{}`. \
-                Codegen units names must always start with the name of the \
-                crate (`{}` in this case).",
-                user_path, crate_name
-            );
-            self.tcx.sess.span_fatal(attr.span, &msg);
+            self.tcx.sess.emit_fatal(errors::MalformedCguName {
+                span: attr.span,
+                user_path,
+                crate_name,
+            });
         }
 
         // Split of the "special suffix" if there is one.
@@ -125,15 +120,12 @@ impl<'tcx> AssertModuleSource<'tcx> {
             let mut cgu_names: Vec<&str> =
                 self.available_cgus.iter().map(|cgu| cgu.as_str()).collect();
             cgu_names.sort();
-            self.tcx.sess.span_err(
-                attr.span,
-                &format!(
-                    "no module named `{}` (mangled: {}). Available modules: {}",
-                    user_path,
-                    cgu_name,
-                    cgu_names.join(", ")
-                ),
-            );
+            self.tcx.sess.emit_err(errors::NoModuleNamed {
+                span: attr.span,
+                user_path,
+                cgu_name,
+                cgu_names: cgu_names.join(", "),
+            });
         }
 
         self.tcx.sess.cgu_reuse_tracker.set_expectation(
@@ -151,15 +143,15 @@ impl<'tcx> AssertModuleSource<'tcx> {
                 if let Some(value) = item.value_str() {
                     return value;
                 } else {
-                    self.tcx.sess.span_fatal(
-                        item.span(),
-                        &format!("associated value expected for `{}`", name),
-                    );
+                    self.tcx.sess.emit_fatal(errors::FieldAssociatedValueExpected {
+                        span: item.span(),
+                        name,
+                    });
                 }
             }
         }
 
-        self.tcx.sess.span_fatal(attr.span, &format!("no field `{}`", name));
+        self.tcx.sess.emit_fatal(errors::NoField { span: attr.span, name });
     }
 
     /// Scan for a `cfg="foo"` attribute and check whether we have a
diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs
new file mode 100644
index 00000000000..deb87678365
--- /dev/null
+++ b/compiler/rustc_incremental/src/errors.rs
@@ -0,0 +1,364 @@
+use rustc_macros::Diagnostic;
+use rustc_span::{symbol::Ident, Span, Symbol};
+use std::path::{Path, PathBuf};
+
+#[derive(Diagnostic)]
+#[diag(incremental_unrecognized_depnode)]
+pub struct UnrecognizedDepNode {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_missing_depnode)]
+pub struct MissingDepNode {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_missing_if_this_changed)]
+pub struct MissingIfThisChanged {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_ok)]
+pub struct Ok {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_no_path)]
+pub struct NoPath {
+    #[primary_span]
+    pub span: Span,
+    pub target: Symbol,
+    pub source: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_unknown_reuse_kind)]
+pub struct UnknownReuseKind {
+    #[primary_span]
+    pub span: Span,
+    pub kind: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_missing_query_depgraph)]
+pub struct MissingQueryDepGraph {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_malformed_cgu_name)]
+pub struct MalformedCguName {
+    #[primary_span]
+    pub span: Span,
+    pub user_path: String,
+    pub crate_name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_no_module_named)]
+pub struct NoModuleNamed<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub user_path: &'a str,
+    pub cgu_name: Symbol,
+    pub cgu_names: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_field_associated_value_expected)]
+pub struct FieldAssociatedValueExpected {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_no_field)]
+pub struct NoField {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_assertion_auto)]
+pub struct AssertionAuto<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub name: &'a str,
+    pub e: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_undefined_clean_dirty_assertions_item)]
+pub struct UndefinedCleanDirtyItem {
+    #[primary_span]
+    pub span: Span,
+    pub kind: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_undefined_clean_dirty_assertions)]
+pub struct UndefinedCleanDirty {
+    #[primary_span]
+    pub span: Span,
+    pub kind: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_repeated_depnode_label)]
+pub struct RepeatedDepNodeLabel<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub label: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_unrecognized_depnode_label)]
+pub struct UnrecognizedDepNodeLabel<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub label: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_not_dirty)]
+pub struct NotDirty<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub dep_node_str: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_not_clean)]
+pub struct NotClean<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub dep_node_str: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_not_loaded)]
+pub struct NotLoaded<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub dep_node_str: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_unknown_item)]
+pub struct UnknownItem {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_no_cfg)]
+pub struct NoCfg {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_associated_value_expected_for)]
+pub struct AssociatedValueExpectedFor {
+    #[primary_span]
+    pub span: Span,
+    pub ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_associated_value_expected)]
+pub struct AssociatedValueExpected {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_unchecked_clean)]
+pub struct UncheckedClean {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_delete_old)]
+pub struct DeleteOld<'a> {
+    pub name: &'a str,
+    pub path: PathBuf,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_create_new)]
+pub struct CreateNew<'a> {
+    pub name: &'a str,
+    pub path: PathBuf,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_write_new)]
+pub struct WriteNew<'a> {
+    pub name: &'a str,
+    pub path: PathBuf,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_canonicalize_path)]
+pub struct CanonicalizePath {
+    pub path: PathBuf,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_create_incr_comp_dir)]
+pub struct CreateIncrCompDir<'a> {
+    pub tag: &'a str,
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_create_lock)]
+pub struct CreateLock<'a> {
+    pub lock_err: std::io::Error,
+    pub session_dir: &'a Path,
+    #[note(incremental_lock_unsupported)]
+    pub is_unsupported_lock: Option<()>,
+    #[help(incremental_cargo_help_1)]
+    #[help(incremental_cargo_help_2)]
+    pub is_cargo: Option<()>,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_delete_lock)]
+pub struct DeleteLock<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_hard_link_failed)]
+pub struct HardLinkFailed<'a> {
+    pub path: &'a Path,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_delete_partial)]
+pub struct DeletePartial<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_delete_full)]
+pub struct DeleteFull<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_finalize)]
+pub struct Finalize<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_invalid_gc_failed)]
+pub struct InvalidGcFailed<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_finalized_gc_failed)]
+pub struct FinalizedGcFailed<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_session_gc_failed)]
+pub struct SessionGcFailed<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_assert_not_loaded)]
+pub struct AssertNotLoaded;
+
+#[derive(Diagnostic)]
+#[diag(incremental_assert_loaded)]
+pub struct AssertLoaded;
+
+#[derive(Diagnostic)]
+#[diag(incremental_delete_incompatible)]
+pub struct DeleteIncompatible {
+    pub path: PathBuf,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_load_dep_graph)]
+pub struct LoadDepGraph {
+    pub path: PathBuf,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_decode_incr_cache)]
+pub struct DecodeIncrCache {
+    pub err: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_write_dep_graph)]
+pub struct WriteDepGraph<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_move_dep_graph)]
+pub struct MoveDepGraph<'a> {
+    pub from: &'a Path,
+    pub to: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_create_dep_graph)]
+pub struct CreateDepGraph<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_copy_workproduct_to_cache)]
+pub struct CopyWorkProductToCache<'a> {
+    pub from: &'a Path,
+    pub to: &'a Path,
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(incremental_delete_workproduct)]
+pub struct DeleteWorkProduct<'a> {
+    pub path: &'a Path,
+    pub err: std::io::Error,
+}
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs
index 83dd9a67e61..3c58cfa38f2 100644
--- a/compiler/rustc_incremental/src/lib.rs
+++ b/compiler/rustc_incremental/src/lib.rs
@@ -2,8 +2,11 @@
 
 #![deny(missing_docs)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(never_type)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_middle;
@@ -12,6 +15,7 @@ extern crate tracing;
 
 mod assert_dep_graph;
 pub mod assert_module_sources;
+mod errors;
 mod persist;
 
 use assert_dep_graph::assert_dep_graph;
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index ed7b272b13d..c6e63998c79 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -19,6 +19,7 @@
 //! Errors are reported if we are in the suitable configuration but
 //! the required condition is not met.
 
+use crate::errors;
 use rustc_ast::{self as ast, Attribute, NestedMetaItem};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::LocalDefId;
@@ -196,11 +197,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
         let loaded_from_disk = self.loaded_from_disk(attr);
         for e in except.iter() {
             if !auto.remove(e) {
-                let msg = format!(
-                    "`except` specified DepNodes that can not be affected for \"{}\": \"{}\"",
-                    name, e
-                );
-                self.tcx.sess.span_fatal(attr.span, &msg);
+                self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e });
             }
         }
         Assertion { clean: auto, dirty: except, loaded_from_disk }
@@ -282,14 +279,10 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
                     // An implementation, eg `impl<A> Trait for Foo { .. }`
                     HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
 
-                    _ => self.tcx.sess.span_fatal(
-                        attr.span,
-                        &format!(
-                            "clean/dirty auto-assertions not yet defined \
-                             for Node::Item.node={:?}",
-                            item.kind
-                        ),
-                    ),
+                    _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirtyItem {
+                        span: attr.span,
+                        kind: format!("{:?}", item.kind),
+                    }),
                 }
             }
             HirNode::TraitItem(item) => match item.kind {
@@ -302,10 +295,10 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
                 ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL),
                 ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
             },
-            _ => self.tcx.sess.span_fatal(
-                attr.span,
-                &format!("clean/dirty auto-assertions not yet defined for {:?}", node),
-            ),
+            _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirty {
+                span: attr.span,
+                kind: format!("{:?}", node),
+            }),
         };
         let labels =
             Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string())));
@@ -318,16 +311,15 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
             let label = label.trim();
             if DepNode::has_label_string(label) {
                 if out.contains(label) {
-                    self.tcx.sess.span_fatal(
-                        item.span(),
-                        &format!("dep-node label `{}` is repeated", label),
-                    );
+                    self.tcx
+                        .sess
+                        .emit_fatal(errors::RepeatedDepNodeLabel { span: item.span(), label });
                 }
                 out.insert(label.to_string());
             } else {
                 self.tcx
                     .sess
-                    .span_fatal(item.span(), &format!("dep-node label `{}` not recognized", label));
+                    .emit_fatal(errors::UnrecognizedDepNodeLabel { span: item.span(), label });
             }
         }
         out
@@ -348,7 +340,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
             let dep_node_str = self.dep_node_str(&dep_node);
             self.tcx
                 .sess
-                .span_err(item_span, &format!("`{}` should be dirty but is not", dep_node_str));
+                .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str });
         }
     }
 
@@ -359,7 +351,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
             let dep_node_str = self.dep_node_str(&dep_node);
             self.tcx
                 .sess
-                .span_err(item_span, &format!("`{}` should be clean but is not", dep_node_str));
+                .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str });
         }
     }
 
@@ -368,10 +360,9 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
 
         if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
             let dep_node_str = self.dep_node_str(&dep_node);
-            self.tcx.sess.span_err(
-                item_span,
-                &format!("`{}` should have been loaded from disk but it was not", dep_node_str),
-            );
+            self.tcx
+                .sess
+                .emit_err(errors::NotLoaded { span: item_span, dep_node_str: &dep_node_str });
         }
     }
 
@@ -412,12 +403,12 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool {
             debug!("check_config: searching for cfg {:?}", value);
             cfg = Some(config.contains(&(value, None)));
         } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) {
-            tcx.sess.span_err(attr.span, &format!("unknown item `{}`", item.name_or_empty()));
+            tcx.sess.emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() });
         }
     }
 
     match cfg {
-        None => tcx.sess.span_fatal(attr.span, "no cfg attribute"),
+        None => tcx.sess.emit_fatal(errors::NoCfg { span: attr.span }),
         Some(c) => c,
     }
 }
@@ -426,13 +417,11 @@ fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> Symbol {
     if let Some(value) = item.value_str() {
         value
     } else {
-        let msg = if let Some(ident) = item.ident() {
-            format!("associated value expected for `{}`", ident)
+        if let Some(ident) = item.ident() {
+            tcx.sess.emit_fatal(errors::AssociatedValueExpectedFor { span: item.span(), ident });
         } else {
-            "expected an associated value".to_string()
-        };
-
-        tcx.sess.span_fatal(item.span(), &msg);
+            tcx.sess.emit_fatal(errors::AssociatedValueExpected { span: item.span() });
+        }
     }
 }
 
@@ -456,7 +445,7 @@ impl<'tcx> FindAllAttrs<'tcx> {
     fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) {
         for attr in &self.found_attrs {
             if !checked_attrs.contains(&attr.id) {
-                self.tcx.sess.span_err(attr.span, "found unchecked `#[rustc_clean]` attribute");
+                self.tcx.sess.emit_err(errors::UncheckedClean { span: attr.span });
                 checked_attrs.insert(attr.id);
             }
         }
diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs
index 2dbd4b6bce8..dc981c6179e 100644
--- a/compiler/rustc_incremental/src/persist/file_format.rs
+++ b/compiler/rustc_incremental/src/persist/file_format.rs
@@ -9,15 +9,15 @@
 //! compiler versions don't change frequently for the typical user, being
 //! conservative here practically has no downside.
 
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-use std::path::{Path, PathBuf};
-
+use crate::errors;
 use rustc_data_structures::memmap::Mmap;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
 use rustc_serialize::Encoder;
 use rustc_session::Session;
+use std::env;
+use std::fs;
+use std::io::{self, Read};
+use std::path::{Path, PathBuf};
 
 /// The first few bytes of files generated by incremental compilation.
 const FILE_MAGIC: &[u8] = b"RSIC";
@@ -60,12 +60,7 @@ where
         }
         Err(err) if err.kind() == io::ErrorKind::NotFound => (),
         Err(err) => {
-            sess.err(&format!(
-                "unable to delete old {} at `{}`: {}",
-                name,
-                path_buf.display(),
-                err
-            ));
+            sess.emit_err(errors::DeleteOld { name, path: path_buf, err });
             return;
         }
     }
@@ -73,7 +68,7 @@ where
     let mut encoder = match FileEncoder::new(&path_buf) {
         Ok(encoder) => encoder,
         Err(err) => {
-            sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err));
+            sess.emit_err(errors::CreateNew { name, path: path_buf, err });
             return;
         }
     };
@@ -90,7 +85,7 @@ where
             debug!("save: data written to disk successfully");
         }
         Err(err) => {
-            sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err));
+            sess.emit_err(errors::WriteNew { name, path: path_buf, err });
         }
     }
 }
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 1fd2b9b0d7b..73d7e3becab 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -103,6 +103,7 @@
 //! unsupported file system and emit a warning in that case. This is not yet
 //! implemented.
 
+use crate::errors;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::{base_n, flock};
@@ -225,12 +226,7 @@ pub fn prepare_session_directory(
     let crate_dir = match crate_dir.canonicalize() {
         Ok(v) => v,
         Err(err) => {
-            let reported = sess.err(&format!(
-                "incremental compilation: error canonicalizing path `{}`: {}",
-                crate_dir.display(),
-                err
-            ));
-            return Err(reported);
+            return Err(sess.emit_err(errors::CanonicalizePath { path: crate_dir, err }));
         }
     };
 
@@ -273,14 +269,7 @@ pub fn prepare_session_directory(
             debug!("successfully copied data from: {}", source_directory.display());
 
             if !allows_links {
-                sess.warn(&format!(
-                    "Hard linking files in the incremental \
-                                        compilation cache failed. Copying files \
-                                        instead. Consider moving the cache \
-                                        directory to a file system which supports \
-                                        hard linking in session dir `{}`",
-                    session_dir.display()
-                ));
+                sess.emit_warning(errors::HardLinkFailed { path: &session_dir });
             }
 
             sess.init_incr_comp_session(session_dir, directory_lock, true);
@@ -295,12 +284,7 @@ pub fn prepare_session_directory(
             // Try to remove the session directory we just allocated. We don't
             // know if there's any garbage in it from the failed copy action.
             if let Err(err) = safe_remove_dir_all(&session_dir) {
-                sess.warn(&format!(
-                    "Failed to delete partly initialized \
-                                    session dir `{}`: {}",
-                    session_dir.display(),
-                    err
-                ));
+                sess.emit_warning(errors::DeletePartial { path: &session_dir, err });
             }
 
             delete_session_dir_lock_file(sess, &lock_file_path);
@@ -332,12 +316,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Svh) {
         );
 
         if let Err(err) = safe_remove_dir_all(&*incr_comp_session_dir) {
-            sess.warn(&format!(
-                "Error deleting incremental compilation \
-                                session directory `{}`: {}",
-                incr_comp_session_dir.display(),
-                err
-            ));
+            sess.emit_warning(errors::DeleteFull { path: &incr_comp_session_dir, err });
         }
 
         let lock_file_path = lock_file_path(&*incr_comp_session_dir);
@@ -380,12 +359,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Svh) {
         }
         Err(e) => {
             // Warn about the error. However, no need to abort compilation now.
-            sess.warn(&format!(
-                "Error finalizing incremental compilation \
-                               session directory `{}`: {}",
-                incr_comp_session_dir.display(),
-                e
-            ));
+            sess.emit_warning(errors::Finalize { path: &incr_comp_session_dir, err: e });
 
             debug!("finalize_session_directory() - error, marking as invalid");
             // Drop the file lock, so we can garage collect
@@ -488,16 +462,7 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorGua
             debug!("{} directory created successfully", dir_tag);
             Ok(())
         }
-        Err(err) => {
-            let reported = sess.err(&format!(
-                "Could not create incremental compilation {} \
-                               directory `{}`: {}",
-                dir_tag,
-                path.display(),
-                err
-            ));
-            Err(reported)
-        }
+        Err(err) => Err(sess.emit_err(errors::CreateIncrCompDir { tag: dir_tag, path, err })),
     }
 }
 
@@ -518,46 +483,20 @@ fn lock_directory(
         // the lock should be exclusive
         Ok(lock) => Ok((lock, lock_file_path)),
         Err(lock_err) => {
-            let mut err = sess.struct_err(&format!(
-                "incremental compilation: could not create \
-                 session directory lock file: {}",
-                lock_err
-            ));
-            if flock::Lock::error_unsupported(&lock_err) {
-                err.note(&format!(
-                    "the filesystem for the incremental path at {} \
-                     does not appear to support locking, consider changing the \
-                     incremental path to a filesystem that supports locking \
-                     or disable incremental compilation",
-                    session_dir.display()
-                ));
-                if std::env::var_os("CARGO").is_some() {
-                    err.help(
-                        "incremental compilation can be disabled by setting the \
-                         environment variable CARGO_INCREMENTAL=0 (see \
-                         https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)",
-                    );
-                    err.help(
-                        "the entire build directory can be changed to a different \
-                        filesystem by setting the environment variable CARGO_TARGET_DIR \
-                        to a different path (see \
-                        https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)",
-                    );
-                }
-            }
-            Err(err.emit())
+            let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err).then_some(());
+            Err(sess.emit_err(errors::CreateLock {
+                lock_err,
+                session_dir,
+                is_unsupported_lock,
+                is_cargo: std::env::var_os("CARGO").map(|_| ()),
+            }))
         }
     }
 }
 
 fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) {
     if let Err(err) = safe_remove_file(&lock_file_path) {
-        sess.warn(&format!(
-            "Error deleting lock file for incremental \
-                            compilation session directory `{}`: {}",
-            lock_file_path.display(),
-            err
-        ));
+        sess.emit_warning(errors::DeleteLock { path: lock_file_path, err });
     }
 }
 
@@ -774,12 +713,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
         if !lock_file_to_session_dir.values().any(|dir| *dir == directory_name) {
             let path = crate_directory.join(directory_name);
             if let Err(err) = safe_remove_dir_all(&path) {
-                sess.warn(&format!(
-                    "Failed to garbage collect invalid incremental \
-                                    compilation session directory `{}`: {}",
-                    path.display(),
-                    err
-                ));
+                sess.emit_warning(errors::InvalidGcFailed { path: &path, err });
             }
         }
     }
@@ -885,12 +819,7 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
         debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
 
         if let Err(err) = safe_remove_dir_all(&path) {
-            sess.warn(&format!(
-                "Failed to garbage collect finalized incremental \
-                                compilation session directory `{}`: {}",
-                path.display(),
-                err
-            ));
+            sess.emit_warning(errors::FinalizedGcFailed { path: &path, err });
         } else {
             delete_session_dir_lock_file(sess, &lock_file_path(&path));
         }
@@ -907,11 +836,7 @@ fn delete_old(sess: &Session, path: &Path) {
     debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
 
     if let Err(err) = safe_remove_dir_all(&path) {
-        sess.warn(&format!(
-            "Failed to garbage collect incremental compilation session directory `{}`: {}",
-            path.display(),
-            err
-        ));
+        sess.emit_warning(errors::SessionGcFailed { path: &path, err });
     } else {
         delete_session_dir_lock_file(sess, &lock_file_path(&path));
     }
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 1c5fd916902..d5097065dda 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -1,5 +1,6 @@
 //! Code to save/load the dep-graph from files.
 
+use crate::errors;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::memmap::Mmap;
 use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId};
@@ -8,7 +9,7 @@ use rustc_serialize::opaque::MemDecoder;
 use rustc_serialize::Decodable;
 use rustc_session::config::IncrementalStateAssertion;
 use rustc_session::Session;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 
 use super::data::*;
 use super::file_format;
@@ -27,11 +28,10 @@ pub enum LoadResult<T> {
     },
     /// The file either didn't exist or was produced by an incompatible compiler version.
     DataOutOfDate,
-    /// An error occurred.
-    Error {
-        #[allow(missing_docs)]
-        message: String,
-    },
+    /// Loading the dep graph failed.
+    LoadDepGraph(PathBuf, std::io::Error),
+    /// Decoding loaded incremental cache failed.
+    DecodeIncrCache(Box<dyn std::any::Any + Send>),
 }
 
 impl<T: Default> LoadResult<T> {
@@ -40,36 +40,31 @@ impl<T: Default> LoadResult<T> {
         // Check for errors when using `-Zassert-incremental-state`
         match (sess.opts.assert_incr_state, &self) {
             (Some(IncrementalStateAssertion::NotLoaded), LoadResult::Ok { .. }) => {
-                sess.fatal(
-                    "We asserted that the incremental cache should not be loaded, \
-                         but it was loaded.",
-                );
+                sess.emit_fatal(errors::AssertNotLoaded);
             }
             (
                 Some(IncrementalStateAssertion::Loaded),
-                LoadResult::Error { .. } | LoadResult::DataOutOfDate,
+                LoadResult::LoadDepGraph(..)
+                | LoadResult::DecodeIncrCache(..)
+                | LoadResult::DataOutOfDate,
             ) => {
-                sess.fatal(
-                    "We asserted that an existing incremental cache directory should \
-                         be successfully loaded, but it was not.",
-                );
+                sess.emit_fatal(errors::AssertLoaded);
             }
             _ => {}
         };
 
         match self {
-            LoadResult::Error { message } => {
-                sess.warn(&message);
+            LoadResult::LoadDepGraph(path, err) => {
+                sess.emit_warning(errors::LoadDepGraph { path, err });
+                Default::default()
+            }
+            LoadResult::DecodeIncrCache(err) => {
+                sess.emit_warning(errors::DecodeIncrCache { err: format!("{err:?}") });
                 Default::default()
             }
             LoadResult::DataOutOfDate => {
                 if let Err(err) = delete_all_session_dir_contents(sess) {
-                    sess.err(&format!(
-                        "Failed to delete invalidated or incompatible \
-                         incremental compilation session directory contents `{}`: {}.",
-                        dep_graph_path(sess).display(),
-                        err
-                    ));
+                    sess.emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err });
                 }
                 Default::default()
             }
@@ -90,9 +85,7 @@ fn load_data(
             // compiler version. Neither is an error.
             LoadResult::DataOutOfDate
         }
-        Err(err) => LoadResult::Error {
-            message: format!("could not load dep-graph from `{}`: {}", path.display(), err),
-        },
+        Err(err) => LoadResult::LoadDepGraph(path.to_path_buf(), err),
     }
 }
 
@@ -114,9 +107,9 @@ impl<T> MaybeAsync<LoadResult<T>> {
     pub fn open(self) -> LoadResult<T> {
         match self {
             MaybeAsync::Sync(result) => result,
-            MaybeAsync::Async(handle) => handle.join().unwrap_or_else(|e| LoadResult::Error {
-                message: format!("could not decode incremental cache: {:?}", e),
-            }),
+            MaybeAsync::Async(handle) => {
+                handle.join().unwrap_or_else(|e| LoadResult::DecodeIncrCache(e))
+            }
         }
     }
 }
@@ -185,7 +178,8 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
 
         match load_data(report_incremental_info, &path, nightly_build) {
             LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
-            LoadResult::Error { message } => LoadResult::Error { message },
+            LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
+            LoadResult::DecodeIncrCache(err) => LoadResult::DecodeIncrCache(err),
             LoadResult::Ok { data: (bytes, start_pos) } => {
                 let mut decoder = MemDecoder::new(&bytes, start_pos);
                 let prev_commandline_args_hash = u64::decode(&mut decoder);
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index 6e9dcdd981e..27be56eac6f 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -1,3 +1,4 @@
+use crate::errors;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::join;
 use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProduct, WorkProductId};
@@ -59,19 +60,14 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
             move || {
                 sess.time("incr_comp_persist_dep_graph", || {
                     if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
-                        sess.err(&format!(
-                            "failed to write dependency graph to `{}`: {}",
-                            staging_dep_graph_path.display(),
-                            err
-                        ));
+                        sess.emit_err(errors::WriteDepGraph { path: &staging_dep_graph_path, err });
                     }
                     if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) {
-                        sess.err(&format!(
-                            "failed to move dependency graph from `{}` to `{}`: {}",
-                            staging_dep_graph_path.display(),
-                            dep_graph_path.display(),
-                            err
-                        ));
+                        sess.emit_err(errors::MoveDepGraph {
+                            from: &staging_dep_graph_path,
+                            to: &dep_graph_path,
+                            err,
+                        });
                     }
                 });
             },
@@ -163,11 +159,7 @@ pub fn build_dep_graph(
     let mut encoder = match FileEncoder::new(&path_buf) {
         Ok(encoder) => encoder,
         Err(err) => {
-            sess.err(&format!(
-                "failed to create dependency graph at `{}`: {}",
-                path_buf.display(),
-                err
-            ));
+            sess.emit_err(errors::CreateDepGraph { path: &path_buf, err });
             return None;
         }
     };
diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs
index 2f1853c441e..dc98fbeb0d1 100644
--- a/compiler/rustc_incremental/src/persist/work_product.rs
+++ b/compiler/rustc_incremental/src/persist/work_product.rs
@@ -2,6 +2,7 @@
 //!
 //! [work products]: WorkProduct
 
+use crate::errors;
 use crate::persist::fs::*;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_fs_util::link_or_copy;
@@ -28,12 +29,11 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
                 let _ = saved_files.insert(ext.to_string(), file_name);
             }
             Err(err) => {
-                sess.warn(&format!(
-                    "error copying object file `{}` to incremental directory as `{}`: {}",
-                    path.display(),
-                    path_in_incr_dir.display(),
-                    err
-                ));
+                sess.emit_warning(errors::CopyWorkProductToCache {
+                    from: &path,
+                    to: &path_in_incr_dir,
+                    err,
+                });
             }
         }
     }
@@ -49,11 +49,7 @@ pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
     for (_, path) in &work_product.saved_files {
         let path = in_incr_comp_dir_sess(sess, path);
         if let Err(err) = std_fs::remove_file(&path) {
-            sess.warn(&format!(
-                "file-system error deleting outdated file `{}`: {}",
-                path.display(),
-                err
-            ));
+            sess.emit_warning(errors::DeleteWorkProduct { path: &path, err });
         }
     }
 }