about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-08-29 06:34:45 +0200
committerGitHub <noreply@github.com>2022-08-29 06:34:45 +0200
commita3c965f5feb5dd1b50ef8c0d1c54d624fd10e3a7 (patch)
treeaf5c08409102c7554761e816daa5b6b65e174425
parent26c86c69931b44732b1554033ba54700f0930060 (diff)
parente1765a9c56d8d9b235bc5b8fda2a0f1d4e92ff49 (diff)
downloadrust-a3c965f5feb5dd1b50ef8c0d1c54d624fd10e3a7.tar.gz
rust-a3c965f5feb5dd1b50ef8c0d1c54d624fd10e3a7.zip
Rollup merge of #100843 - IntQuant:issue-100717-infer, r=compiler-errors
Migrate part of rustc_infer to session diagnostic
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_error_messages/locales/en-US/infer.ftl112
-rw-r--r--compiler/rustc_error_messages/src/lib.rs1
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/errors.rs254
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs28
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs327
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs114
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs22
-rw-r--r--compiler/rustc_infer/src/lib.rs1
10 files changed, 649 insertions, 212 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 100e9095703..7c3879fdd98 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3631,6 +3631,7 @@ dependencies = [
  "rustc_macros",
  "rustc_middle",
  "rustc_serialize",
+ "rustc_session",
  "rustc_span",
  "rustc_target",
  "smallvec",
diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
new file mode 100644
index 00000000000..60086cd6e47
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -0,0 +1,112 @@
+infer_opaque_hidden_type =
+    opaque type's hidden type cannot be another opaque type from the same scope
+    .label = one of the two opaque types used here has to be outside its defining scope
+    .opaque_type = opaque type whose hidden type is being assigned
+    .hidden_type = opaque type being used as hidden type
+
+infer_type_annotations_needed = {$source_kind ->
+    [closure] type annotations needed for the closure `{$source_name}`
+    [normal] type annotations needed for `{$source_name}`
+    *[other] type annotations needed
+}
+    .label = type must be known at this point
+
+infer_label_bad = {$bad_kind ->
+    *[other] cannot infer type
+    [more_info] cannot infer {$prefix_kind ->
+        *[type] type for {$prefix}
+        [const_with_param] the value of const parameter
+        [const] the value of the constant
+    } `{$name}`{$has_parent ->
+        [true] {" "}declared on the {$parent_prefix} `{$parent_name}`
+        *[false] {""}
+    }
+}
+
+infer_source_kind_subdiag_let = {$kind ->
+    [with_pattern] consider giving `{$name}` an explicit type
+    [closure] consider giving this closure parameter an explicit type
+    *[other] consider giving this pattern a type
+}{$x_kind ->
+    [has_name] , where the {$prefix_kind ->
+        *[type] type for {$prefix}
+        [const_with_param] the value of const parameter
+        [const] the value of the constant
+    } `{$arg_name}` is specified
+    [underscore] , where the placeholders `_` are specified
+    *[empty] {""}
+}
+
+infer_source_kind_subdiag_generic_label =
+    cannot infer {$is_type ->
+    [true] type
+    *[false] the value
+    } of the {$is_type ->
+    [true] type
+    *[false] const
+    } {$parent_exists ->
+    [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}`
+    *[false] parameter {$param_name}
+    }
+
+infer_source_kind_subdiag_generic_suggestion =
+    consider specifying the generic {$arg_count ->
+    [one] argument
+    *[other] arguments
+    }
+
+infer_source_kind_fully_qualified =
+    try using a fully qualified path to specify the expected types
+
+infer_source_kind_closure_return =
+    try giving this closure an explicit return type
+
+# generator_kind  may need to be translated
+infer_need_type_info_in_generator =
+    type inside {$generator_kind ->
+    [async_block] `async` block
+    [async_closure] `async` closure
+    [async_fn] `async fn` body
+    *[generator] generator
+    } must be known in this context
+
+
+infer_subtype = ...so that the {$requirement ->
+    [method_compat] method type is compatible with trait
+    [type_compat] associated type is compatible with trait
+    [const_compat] const is compatible with trait
+    [expr_assignable] expression is assignable
+    [if_else_different] `if` and `else` have incompatible types
+    [no_else] `if` missing an `else` returns `()`
+    [fn_main_correct_type] `main` function has the correct type
+    [fn_start_correct_type] #[start]` function has the correct type
+    [intristic_correct_type] intrinsic has the correct type
+    [method_correct_type] method receiver has the correct type
+    *[other] types are compatible
+}
+infer_subtype_2 = ...so that {$requirement ->
+    [method_compat] method type is compatible with trait
+    [type_compat] associated type is compatible with trait
+    [const_compat] const is compatible with trait
+    [expr_assignable] expression is assignable
+    [if_else_different] `if` and `else` have incompatible types
+    [no_else] `if` missing an `else` returns `()`
+    [fn_main_correct_type] `main` function has the correct type
+    [fn_start_correct_type] #[start]` function has the correct type
+    [intristic_correct_type] intrinsic has the correct type
+    [method_correct_type] method receiver has the correct type
+    *[other] types are compatible
+}
+
+infer_reborrow = ...so that reference does not outlive borrowed content
+infer_reborrow_upvar = ...so that closure can access `{$name}`
+infer_relate_object_bound = ...so that it can be closed over into an object
+infer_data_borrowed = ...so that the type `{$name}` is not borrowed for too long
+infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at
+infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues ->
+[true] ...
+*[false] {""}
+}
+infer_relate_param_bound_2 = ...that is required by this bound
+infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied
+infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index b18d1f553e4..b17668dc0ae 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -41,6 +41,7 @@ fluent_messages! {
     driver => "../locales/en-US/driver.ftl",
     expand => "../locales/en-US/expand.ftl",
     interface => "../locales/en-US/interface.ftl",
+    infer => "../locales/en-US/infer.ftl",
     lint => "../locales/en-US/lint.ftl",
     parser => "../locales/en-US/parser.ftl",
     passes => "../locales/en-US/passes.ftl",
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 02ac83a5e8b..aced787d671 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
+rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs
new file mode 100644
index 00000000000..938f8aa77a5
--- /dev/null
+++ b/compiler/rustc_infer/src/errors.rs
@@ -0,0 +1,254 @@
+use rustc_errors::{fluent, AddSubdiagnostic, DiagnosticMessage, DiagnosticStyledString};
+use rustc_hir::FnRetTy;
+use rustc_macros::SessionDiagnostic;
+use rustc_span::{BytePos, Span};
+
+use crate::infer::error_reporting::{
+    need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind},
+    ObligationCauseAsDiagArg,
+};
+
+#[derive(SessionDiagnostic)]
+#[diag(infer::opaque_hidden_type)]
+pub struct OpaqueHiddenTypeDiag {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[note(infer::opaque_type)]
+    pub opaque_type: Span,
+    #[note(infer::hidden_type)]
+    pub hidden_type: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(infer::type_annotations_needed, code = "E0282")]
+pub struct AnnotationRequired<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+}
+
+// Copy of `AnnotationRequired` for E0283
+#[derive(SessionDiagnostic)]
+#[diag(infer::type_annotations_needed, code = "E0283")]
+pub struct AmbigousImpl<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+}
+
+// Copy of `AnnotationRequired` for E0284
+#[derive(SessionDiagnostic)]
+#[diag(infer::type_annotations_needed, code = "E0284")]
+pub struct AmbigousReturn<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(infer::need_type_info_in_generator, code = "E0698")]
+pub struct NeedTypeInfoInGenerator<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub generator_kind: GeneratorKindAsDiagArg,
+    #[subdiagnostic]
+    pub bad_label: InferenceBadError<'a>,
+}
+
+// Used when a better one isn't available
+#[derive(SessionSubdiagnostic)]
+#[label(infer::label_bad)]
+pub struct InferenceBadError<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub bad_kind: &'static str,
+    pub prefix_kind: UnderspecifiedArgKind,
+    pub has_parent: bool,
+    pub prefix: &'a str,
+    pub parent_prefix: &'a str,
+    pub parent_name: String,
+    pub name: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum SourceKindSubdiag<'a> {
+    #[suggestion_verbose(
+        infer::source_kind_subdiag_let,
+        code = ": {type_name}",
+        applicability = "has-placeholders"
+    )]
+    LetLike {
+        #[primary_span]
+        span: Span,
+        name: String,
+        type_name: String,
+        kind: &'static str,
+        x_kind: &'static str,
+        prefix_kind: UnderspecifiedArgKind,
+        prefix: &'a str,
+        arg_name: String,
+    },
+    #[label(infer::source_kind_subdiag_generic_label)]
+    GenericLabel {
+        #[primary_span]
+        span: Span,
+        is_type: bool,
+        param_name: String,
+        parent_exists: bool,
+        parent_prefix: String,
+        parent_name: String,
+    },
+    #[suggestion_verbose(
+        infer::source_kind_subdiag_generic_suggestion,
+        code = "::<{args}>",
+        applicability = "has-placeholders"
+    )]
+    GenericSuggestion {
+        #[primary_span]
+        span: Span,
+        arg_count: usize,
+        args: String,
+    },
+}
+
+// Has to be implemented manually because multipart suggestions are not supported by the derive macro.
+// Would be a part of `SourceKindSubdiag` otherwise.
+pub enum SourceKindMultiSuggestion<'a> {
+    FullyQualified {
+        span: Span,
+        def_path: String,
+        adjustment: &'a str,
+        successor: (&'a str, BytePos),
+    },
+    ClosureReturn {
+        ty_info: String,
+        data: &'a FnRetTy<'a>,
+        should_wrap_expr: Option<Span>,
+    },
+}
+
+impl AddSubdiagnostic for SourceKindMultiSuggestion<'_> {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        match self {
+            Self::FullyQualified { span, def_path, adjustment, successor } => {
+                let suggestion = vec![
+                    (span.shrink_to_lo(), format!("{def_path}({adjustment}")),
+                    (span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
+                ];
+                diag.multipart_suggestion_verbose(
+                    fluent::infer::source_kind_fully_qualified,
+                    suggestion,
+                    rustc_errors::Applicability::HasPlaceholders,
+                );
+            }
+            Self::ClosureReturn { ty_info, data, should_wrap_expr } => {
+                let (arrow, post) = match data {
+                    FnRetTy::DefaultReturn(_) => ("-> ", " "),
+                    _ => ("", ""),
+                };
+                let suggestion = match should_wrap_expr {
+                    Some(end_span) => vec![
+                        (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post)),
+                        (end_span, " }".to_string()),
+                    ],
+                    None => vec![(data.span(), format!("{}{}{}", arrow, ty_info, post))],
+                };
+                diag.multipart_suggestion_verbose(
+                    fluent::infer::source_kind_closure_return,
+                    suggestion,
+                    rustc_errors::Applicability::HasPlaceholders,
+                );
+            }
+        }
+    }
+}
+
+pub enum RegionOriginNote<'a> {
+    Plain {
+        span: Span,
+        msg: DiagnosticMessage,
+    },
+    WithName {
+        span: Span,
+        msg: DiagnosticMessage,
+        name: &'a str,
+        continues: bool,
+    },
+    WithRequirement {
+        span: Span,
+        requirement: ObligationCauseAsDiagArg<'a>,
+        expected_found: Option<(DiagnosticStyledString, DiagnosticStyledString)>,
+    },
+}
+
+impl AddSubdiagnostic for RegionOriginNote<'_> {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        let mut label_or_note = |span, msg: DiagnosticMessage| {
+            let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
+            let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
+            let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span);
+            if span_is_primary && sub_count == 0 && expanded_sub_count == 0 {
+                diag.span_label(span, msg);
+            } else if span_is_primary && expanded_sub_count == 0 {
+                diag.note(msg);
+            } else {
+                diag.span_note(span, msg);
+            }
+        };
+        match self {
+            RegionOriginNote::Plain { span, msg } => {
+                label_or_note(span, msg);
+            }
+            RegionOriginNote::WithName { span, msg, name, continues } => {
+                label_or_note(span, msg);
+                diag.set_arg("name", name);
+                diag.set_arg("continues", continues);
+            }
+            RegionOriginNote::WithRequirement {
+                span,
+                requirement,
+                expected_found: Some((expected, found)),
+            } => {
+                label_or_note(span, fluent::infer::subtype);
+                diag.set_arg("requirement", requirement);
+
+                diag.note_expected_found(&"", expected, &"", found);
+            }
+            RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => {
+                // FIXME: this really should be handled at some earlier stage. Our
+                // handling of region checking when type errors are present is
+                // *terrible*.
+                label_or_note(span, fluent::infer::subtype_2);
+                diag.set_arg("requirement", requirement);
+            }
+        };
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 16cffb45f0f..465508e1205 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -58,7 +58,7 @@ use crate::traits::{
 };
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed};
+use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -78,7 +78,7 @@ use std::{cmp, fmt, iter};
 
 mod note;
 
-mod need_type_info;
+pub(crate) mod need_type_info;
 pub use need_type_info::TypeAnnotationNeeded;
 
 pub mod nice_region_error;
@@ -2886,6 +2886,30 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
     }
 }
 
+/// Newtype to allow implementing IntoDiagnosticArg
+pub struct ObligationCauseAsDiagArg<'tcx>(pub ObligationCause<'tcx>);
+
+impl IntoDiagnosticArg for ObligationCauseAsDiagArg<'_> {
+    fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+        use crate::traits::ObligationCauseCode::*;
+        let kind = match self.0.code() {
+            CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => "method_compat",
+            CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => "type_compat",
+            CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => "const_compat",
+            ExprAssignable => "expr_assignable",
+            IfExpression { .. } => "if_else_different",
+            IfExpressionWithNoElse => "no_else",
+            MainFunctionType => "fn_main_correct_type",
+            StartFunctionType => "fn_start_correct_type",
+            IntrinsicType => "intristic_correct_type",
+            MethodReceiver => "method_correct_type",
+            _ => "other",
+        }
+        .into();
+        rustc_errors::DiagnosticArgValue::Str(kind)
+    }
+}
+
 /// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks
 /// extra information about each type, but we only care about the category.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 561d1354edd..e990fe7ecb5 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -1,6 +1,10 @@
+use crate::errors::{
+    AmbigousImpl, AmbigousReturn, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator,
+    SourceKindMultiSuggestion, SourceKindSubdiag,
+};
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
@@ -14,6 +18,7 @@ use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
 use rustc_middle::ty::{self, DefIdTree, InferConst};
 use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
+use rustc_session::SessionDiagnostic;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{BytePos, Span};
 use std::borrow::Cow;
@@ -60,38 +65,49 @@ pub struct InferenceDiagnosticsParentData {
     name: String,
 }
 
+#[derive(Clone)]
 pub enum UnderspecifiedArgKind {
     Type { prefix: Cow<'static, str> },
     Const { is_parameter: bool },
 }
 
 impl InferenceDiagnosticsData {
-    /// Generate a label for a generic argument which can't be inferred. When not
-    /// much is known about the argument, `use_diag` may be used to describe the
-    /// labeled value.
-    fn cannot_infer_msg(&self) -> String {
-        if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
-            return "cannot infer type".to_string();
-        }
-
-        let suffix = match &self.parent {
-            Some(parent) => parent.suffix_string(),
-            None => String::new(),
-        };
-
-        // For example: "cannot infer type for type parameter `T`"
-        format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
+    fn can_add_more_info(&self) -> bool {
+        !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }))
     }
 
-    fn where_x_is_specified(&self, in_type: Ty<'_>) -> String {
+    fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
         if in_type.is_ty_infer() {
-            String::new()
+            "empty"
         } else if self.name == "_" {
             // FIXME: Consider specializing this message if there is a single `_`
             // in the type.
-            ", where the placeholders `_` are specified".to_string()
+            "underscore"
         } else {
-            format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name)
+            "has_name"
+        }
+    }
+
+    /// Generate a label for a generic argument which can't be inferred. When not
+    /// much is known about the argument, `use_diag` may be used to describe the
+    /// labeled value.
+    fn make_bad_error(&self, span: Span) -> InferenceBadError<'_> {
+        let has_parent = self.parent.is_some();
+        let bad_kind = if self.can_add_more_info() { "more_info" } else { "other" };
+        let (parent_prefix, parent_name) = self
+            .parent
+            .as_ref()
+            .map(|parent| (parent.prefix, parent.name.clone()))
+            .unwrap_or_default();
+        InferenceBadError {
+            span,
+            bad_kind,
+            prefix_kind: self.kind.clone(),
+            prefix: self.kind.try_get_prefix().unwrap_or_default(),
+            name: self.name.clone(),
+            has_parent,
+            parent_prefix,
+            parent_name,
         }
     }
 }
@@ -113,18 +129,24 @@ impl InferenceDiagnosticsParentData {
     fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
         Self::for_parent_def_id(tcx, tcx.parent(def_id))
     }
+}
 
-    fn suffix_string(&self) -> String {
-        format!(" declared on the {} `{}`", self.prefix, self.name)
+impl IntoDiagnosticArg for UnderspecifiedArgKind {
+    fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+        let kind = match self {
+            Self::Type { .. } => "type",
+            Self::Const { is_parameter: true } => "const_with_param",
+            Self::Const { is_parameter: false } => "const",
+        };
+        rustc_errors::DiagnosticArgValue::Str(kind.into())
     }
 }
 
 impl UnderspecifiedArgKind {
-    fn prefix_string(&self) -> Cow<'static, str> {
+    fn try_get_prefix(&self) -> Option<&str> {
         match self {
-            Self::Type { prefix } => format!("type for {}", prefix).into(),
-            Self::Const { is_parameter: true } => "the value of const parameter".into(),
-            Self::Const { is_parameter: false } => "the value of the constant".into(),
+            Self::Type { prefix } => Some(prefix.as_ref()),
+            Self::Const { .. } => None,
         }
     }
 }
@@ -303,11 +325,44 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         arg_data: InferenceDiagnosticsData,
         error_code: TypeAnnotationNeeded,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let error_code = error_code.into();
-        let mut err =
-            self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code);
-        err.span_label(span, arg_data.cannot_infer_msg());
-        err
+        let source_kind = "other";
+        let source_name = "";
+        let failure_span = None;
+        let infer_subdiags = Vec::new();
+        let multi_suggestions = Vec::new();
+        let bad_label = Some(arg_data.make_bad_error(span));
+        match error_code {
+            TypeAnnotationNeeded::E0282 => AnnotationRequired {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0283 => AmbigousImpl {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0284 => AmbigousReturn {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+        }
     }
 
     pub fn emit_inference_failure_err(
@@ -340,48 +395,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             return self.bad_inference_failure_err(failure_span, arg_data, error_code)
         };
 
-        let error_code = error_code.into();
-        let mut err = self.tcx.sess.struct_span_err_with_code(
-            span,
-            &format!("type annotations needed{}", kind.ty_msg(self)),
-            error_code,
-        );
-
-        if should_label_span && !failure_span.overlaps(span) {
-            err.span_label(failure_span, "type must be known at this point");
-        }
+        let (source_kind, name) = kind.ty_localized_msg(self);
+        let failure_span = if should_label_span && !failure_span.overlaps(span) {
+            Some(failure_span)
+        } else {
+            None
+        };
 
+        let mut infer_subdiags = Vec::new();
+        let mut multi_suggestions = Vec::new();
         match kind {
             InferSourceKind::LetBinding { insert_span, pattern_name, ty } => {
-                let suggestion_msg = if let Some(name) = pattern_name {
-                    format!(
-                        "consider giving `{}` an explicit type{}",
-                        name,
-                        arg_data.where_x_is_specified(ty)
-                    )
-                } else {
-                    format!(
-                        "consider giving this pattern a type{}",
-                        arg_data.where_x_is_specified(ty)
-                    )
-                };
-                err.span_suggestion_verbose(
-                    insert_span,
-                    &suggestion_msg,
-                    format!(": {}", ty_to_string(self, ty)),
-                    Applicability::HasPlaceholders,
-                );
+                infer_subdiags.push(SourceKindSubdiag::LetLike {
+                    span: insert_span,
+                    name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
+                    x_kind: arg_data.where_x_is_kind(ty),
+                    prefix_kind: arg_data.kind.clone(),
+                    prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
+                    arg_name: arg_data.name,
+                    kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
+                    type_name: ty_to_string(self, ty),
+                });
             }
             InferSourceKind::ClosureArg { insert_span, ty } => {
-                err.span_suggestion_verbose(
-                    insert_span,
-                    &format!(
-                        "consider giving this closure parameter an explicit type{}",
-                        arg_data.where_x_is_specified(ty)
-                    ),
-                    format!(": {}", ty_to_string(self, ty)),
-                    Applicability::HasPlaceholders,
-                );
+                infer_subdiags.push(SourceKindSubdiag::LetLike {
+                    span: insert_span,
+                    name: String::new(),
+                    x_kind: arg_data.where_x_is_kind(ty),
+                    prefix_kind: arg_data.kind.clone(),
+                    prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
+                    arg_name: arg_data.name,
+                    kind: "closure",
+                    type_name: ty_to_string(self, ty),
+                });
             }
             InferSourceKind::GenericArg {
                 insert_span,
@@ -393,19 +439,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 let generics = self.tcx.generics_of(generics_def_id);
                 let is_type = matches!(arg.unpack(), GenericArgKind::Type(_));
 
-                let cannot_infer_msg = format!(
-                    "cannot infer {} of the {} parameter `{}`{}",
-                    if is_type { "type" } else { "the value" },
-                    if is_type { "type" } else { "const" },
-                    generics.params[argument_index].name,
-                    // We use the `generics_def_id` here, as even when suggesting `None::<T>`,
-                    // the type parameter `T` was still declared on the enum, not on the
-                    // variant.
+                let (parent_exists, parent_prefix, parent_name) =
                     InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id)
-                        .map_or(String::new(), |parent| parent.suffix_string()),
-                );
+                        .map_or((false, String::new(), String::new()), |parent| {
+                            (true, parent.prefix.to_string(), parent.name)
+                        });
 
-                err.span_label(span, cannot_infer_msg);
+                infer_subdiags.push(SourceKindSubdiag::GenericLabel {
+                    span,
+                    is_type,
+                    param_name: generics.params[argument_index].name.to_string(),
+                    parent_exists,
+                    parent_prefix,
+                    parent_name,
+                });
 
                 let args = fmt_printer(self, Namespace::TypeNS)
                     .comma_sep(generic_args.iter().copied().map(|arg| {
@@ -435,15 +482,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     .unwrap()
                     .into_buffer();
 
-                err.span_suggestion_verbose(
-                    insert_span,
-                    &format!(
-                        "consider specifying the generic argument{}",
-                        pluralize!(generic_args.len()),
-                    ),
-                    format!("::<{}>", args),
-                    Applicability::HasPlaceholders,
-                );
+                infer_subdiags.push(SourceKindSubdiag::GenericSuggestion {
+                    span: insert_span,
+                    arg_count: generic_args.len(),
+                    args,
+                });
             }
             InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => {
                 let printer = fmt_printer(self, Namespace::ValueNS);
@@ -468,37 +511,54 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     _ => "",
                 };
 
-                let suggestion = vec![
-                    (receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")),
-                    (receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
-                ];
-                err.multipart_suggestion_verbose(
-                    "try using a fully qualified path to specify the expected types",
-                    suggestion,
-                    Applicability::HasPlaceholders,
-                );
+                multi_suggestions.push(SourceKindMultiSuggestion::FullyQualified {
+                    span: receiver.span,
+                    def_path,
+                    adjustment,
+                    successor,
+                });
             }
             InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
-                let ret = ty_to_string(self, ty);
-                let (arrow, post) = match data {
-                    FnRetTy::DefaultReturn(_) => ("-> ", " "),
-                    _ => ("", ""),
-                };
-                let suggestion = match should_wrap_expr {
-                    Some(end_span) => vec![
-                        (data.span(), format!("{}{}{}{{ ", arrow, ret, post)),
-                        (end_span, " }".to_string()),
-                    ],
-                    None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))],
-                };
-                err.multipart_suggestion_verbose(
-                    "try giving this closure an explicit return type",
-                    suggestion,
-                    Applicability::HasPlaceholders,
-                );
+                let ty_info = ty_to_string(self, ty);
+                multi_suggestions.push(SourceKindMultiSuggestion::ClosureReturn {
+                    ty_info,
+                    data,
+                    should_wrap_expr,
+                });
+            }
+        }
+        match error_code {
+            TypeAnnotationNeeded::E0282 => AnnotationRequired {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0283 => AmbigousImpl {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
+            }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
+            TypeAnnotationNeeded::E0284 => AmbigousReturn {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
             }
+            .into_diagnostic(&self.tcx.sess.parse_sess),
         }
-        err
     }
 
     pub fn need_type_info_err_in_generator(
@@ -510,15 +570,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         let ty = self.resolve_vars_if_possible(ty);
         let data = self.extract_inference_diagnostics_data(ty.into(), None);
 
-        let mut err = struct_span_err!(
-            self.tcx.sess,
+        NeedTypeInfoInGenerator {
+            bad_label: data.make_bad_error(span),
             span,
-            E0698,
-            "type inside {} must be known in this context",
-            kind,
-        );
-        err.span_label(span, data.cannot_infer_msg());
-        err
+            generator_kind: GeneratorKindAsDiagArg(kind),
+        }
+        .into_diagnostic(&self.tcx.sess.parse_sess)
+    }
+}
+
+pub struct GeneratorKindAsDiagArg(pub hir::GeneratorKind);
+
+impl IntoDiagnosticArg for GeneratorKindAsDiagArg {
+    fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+        let kind = match self.0 {
+            hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "async_block",
+            hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "async_closure",
+            hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "async_fn",
+            hir::GeneratorKind::Gen => "generator",
+        };
+        rustc_errors::DiagnosticArgValue::Str(kind.into())
     }
 }
 
@@ -579,22 +650,22 @@ impl<'tcx> InferSource<'tcx> {
 }
 
 impl<'tcx> InferSourceKind<'tcx> {
-    fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String {
+    fn ty_localized_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> (&'static str, String) {
         match *self {
             InferSourceKind::LetBinding { ty, .. }
             | InferSourceKind::ClosureArg { ty, .. }
             | InferSourceKind::ClosureReturn { ty, .. } => {
                 if ty.is_closure() {
-                    format!(" for the closure `{}`", closure_as_fn_str(infcx, ty))
+                    ("closure", closure_as_fn_str(infcx, ty))
                 } else if !ty.is_ty_infer() {
-                    format!(" for `{}`", ty_to_string(infcx, ty))
+                    ("normal", ty_to_string(infcx, ty))
                 } else {
-                    String::new()
+                    ("other", String::new())
                 }
             }
             // FIXME: We should be able to add some additional info here.
             InferSourceKind::GenericArg { .. }
-            | InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(),
+            | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new()),
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 8c465b08760..cffdf56bb6d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -1,96 +1,78 @@
-use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt};
+use crate::errors::RegionOriginNote;
+use crate::infer::error_reporting::note_and_explain_region;
 use crate::infer::{self, InferCtxt, SubregionOrigin};
-use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{
+    fluent, struct_span_err, AddSubdiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{self, Region};
 
+use super::ObligationCauseAsDiagArg;
+
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
-        let mut label_or_note = |span, msg: &str| {
-            let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count();
-            let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count();
-            let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span);
-            if span_is_primary && sub_count == 0 && expanded_sub_count == 0 {
-                err.span_label(span, msg);
-            } else if span_is_primary && expanded_sub_count == 0 {
-                err.note(msg);
-            } else {
-                err.span_note(span, msg);
-            }
-        };
         match *origin {
-            infer::Subtype(ref trace) => {
-                if let Some((expected, found)) = self.values_str(trace.values) {
-                    label_or_note(
-                        trace.cause.span,
-                        &format!("...so that the {}", trace.cause.as_requirement_str()),
-                    );
-
-                    err.note_expected_found(&"", expected, &"", found);
-                } else {
-                    // FIXME: this really should be handled at some earlier stage. Our
-                    // handling of region checking when type errors are present is
-                    // *terrible*.
-
-                    label_or_note(
-                        trace.cause.span,
-                        &format!("...so that {}", trace.cause.as_requirement_str()),
-                    );
-                }
-            }
-            infer::Reborrow(span) => {
-                label_or_note(span, "...so that reference does not outlive borrowed content");
+            infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+                span: trace.cause.span,
+                requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
+                expected_found: self.values_str(trace.values),
             }
+            .add_to_diagnostic(err),
+            infer::Reborrow(span) => RegionOriginNote::Plain { span, msg: fluent::infer::reborrow }
+                .add_to_diagnostic(err),
             infer::ReborrowUpvar(span, ref upvar_id) => {
                 let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
-                label_or_note(span, &format!("...so that closure can access `{}`", var_name));
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::infer::reborrow,
+                    name: &var_name.to_string(),
+                    continues: false,
+                }
+                .add_to_diagnostic(err);
             }
             infer::RelateObjectBound(span) => {
-                label_or_note(span, "...so that it can be closed over into an object");
+                RegionOriginNote::Plain { span, msg: fluent::infer::relate_object_bound }
+                    .add_to_diagnostic(err);
             }
             infer::DataBorrowed(ty, span) => {
-                label_or_note(
+                RegionOriginNote::WithName {
                     span,
-                    &format!(
-                        "...so that the type `{}` is not borrowed for too long",
-                        self.ty_to_string(ty)
-                    ),
-                );
+                    msg: fluent::infer::data_borrowed,
+                    name: &self.ty_to_string(ty),
+                    continues: false,
+                }
+                .add_to_diagnostic(err);
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
-                label_or_note(
+                RegionOriginNote::WithName {
                     span,
-                    &format!(
-                        "...so that the reference type `{}` does not outlive the data it points at",
-                        self.ty_to_string(ty)
-                    ),
-                );
+                    msg: fluent::infer::reference_outlives_referent,
+                    name: &self.ty_to_string(ty),
+                    continues: false,
+                }
+                .add_to_diagnostic(err);
             }
-            infer::RelateParamBound(span, t, opt_span) => {
-                label_or_note(
+            infer::RelateParamBound(span, ty, opt_span) => {
+                RegionOriginNote::WithName {
                     span,
-                    &format!(
-                        "...so that the type `{}` will meet its required lifetime bounds{}",
-                        self.ty_to_string(t),
-                        if opt_span.is_some() { "..." } else { "" },
-                    ),
-                );
+                    msg: fluent::infer::relate_param_bound,
+                    name: &self.ty_to_string(ty),
+                    continues: opt_span.is_some(),
+                }
+                .add_to_diagnostic(err);
                 if let Some(span) = opt_span {
-                    err.span_note(span, "...that is required by this bound");
+                    RegionOriginNote::Plain { span, msg: fluent::infer::relate_param_bound_2 }
+                        .add_to_diagnostic(err);
                 }
             }
             infer::RelateRegionParamBound(span) => {
-                label_or_note(
-                    span,
-                    "...so that the declared lifetime parameter bounds are satisfied",
-                );
+                RegionOriginNote::Plain { span, msg: fluent::infer::relate_region_param_bound }
+                    .add_to_diagnostic(err);
             }
             infer::CompareImplItemObligation { span, .. } => {
-                label_or_note(
-                    span,
-                    "...so that the definition in impl matches the definition from the trait",
-                );
+                RegionOriginNote::Plain { span, msg: fluent::infer::compare_impl_item_obligation }
+                    .add_to_diagnostic(err);
             }
             infer::CheckAssociatedTypeBounds { ref parent, .. } => {
                 self.note_region_origin(err, &parent);
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index e579afbf389..233a5004a39 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -1,3 +1,4 @@
+use crate::errors::OpaqueHiddenTypeDiag;
 use crate::infer::{DefiningAnchor, InferCtxt, InferOk};
 use crate::traits;
 use hir::def_id::{DefId, LocalDefId};
@@ -153,22 +154,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     if let Some(OpaqueTyOrigin::TyAlias) =
                         did2.as_local().and_then(|did2| self.opaque_type_origin(did2, cause.span))
                     {
-                        self.tcx
-                                .sess
-                                .struct_span_err(
-                                    cause.span,
-                                    "opaque type's hidden type cannot be another opaque type from the same scope",
-                                )
-                                .span_label(cause.span, "one of the two opaque types used here has to be outside its defining scope")
-                                .span_note(
-                                    self.tcx.def_span(def_id),
-                                    "opaque type whose hidden type is being assigned",
-                                )
-                                .span_note(
-                                    self.tcx.def_span(did2),
-                                    "opaque type being used as hidden type",
-                                )
-                                .emit();
+                        self.tcx.sess.emit_err(OpaqueHiddenTypeDiag {
+                            span: cause.span,
+                            hidden_type: self.tcx.def_span(did2),
+                            opaque_type: self.tcx.def_span(def_id),
+                        });
                     }
                 }
                 Some(self.register_hidden_type(
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 8f02a6cc4a1..602a9ab13f3 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -34,5 +34,6 @@ extern crate tracing;
 #[macro_use]
 extern crate rustc_middle;
 
+mod errors;
 pub mod infer;
 pub mod traits;