about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_messages/locales/en-US/middle.ftl9
-rw-r--r--compiler/rustc_error_messages/locales/en-US/passes.ftl559
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs50
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs21
-rw-r--r--compiler/rustc_passes/src/check_attr.rs151
-rw-r--r--compiler/rustc_passes/src/check_const.rs13
-rw-r--r--compiler/rustc_passes/src/dead.rs16
-rw-r--r--compiler/rustc_passes/src/debugger_visualizer.rs15
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs37
-rw-r--r--compiler/rustc_passes/src/entry.rs145
-rw-r--r--compiler/rustc_passes/src/errors.rs785
-rw-r--r--compiler/rustc_passes/src/lang_items.rs207
-rw-r--r--compiler/rustc_passes/src/layout_test.rs71
-rw-r--r--compiler/rustc_passes/src/lib.rs2
-rw-r--r--compiler/rustc_passes/src/lib_features.rs34
-rw-r--r--compiler/rustc_passes/src/loops.rs161
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs84
-rw-r--r--compiler/rustc_passes/src/stability.rs130
-rw-r--r--compiler/rustc_passes/src/weak_lang_items.rs29
-rw-r--r--compiler/rustc_session/src/parse.rs13
-rw-r--r--compiler/rustc_session/src/session.rs11
-rw-r--r--src/test/ui/associated-types/issue-85103.rs2
-rw-r--r--src/test/ui/associated-types/issue-85103.stderr2
24 files changed, 1793 insertions, 756 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/middle.ftl b/compiler/rustc_error_messages/locales/en-US/middle.ftl
index ca3c91ce24a..b9e4499d47f 100644
--- a/compiler/rustc_error_messages/locales/en-US/middle.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/middle.ftl
@@ -18,3 +18,12 @@ middle_limit_invalid =
 
 middle_const_eval_non_int =
     constant evaluation of enum discriminant resulted in non-integer
+
+middle_unknown_layout =
+    the type `{$ty}` has an unknown layout
+
+middle_values_too_big =
+    values of the type `{$ty}` are too big for the current architecture
+
+middle_cannot_be_normalized =
+    unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized
diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl
index 995ad4fe258..1f1c9c29d66 100644
--- a/compiler/rustc_error_messages/locales/en-US/passes.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl
@@ -10,88 +10,119 @@ passes_outer_crate_level_attr =
 passes_inner_crate_level_attr =
     crate-level attribute should be in the root module
 
-passes_ignored_attr_with_macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs
+passes_ignored_attr_with_macro =
+    `#[{$sym}]` is ignored on struct fields, match arms and macro defs
     .warn = {-passes_previously_accepted}
     .note = {-passes_see_issue(issue: "80564")}
 
-passes_ignored_attr = `#[{$sym}]` is ignored on struct fields and match arms
+passes_ignored_attr =
+    `#[{$sym}]` is ignored on struct fields and match arms
     .warn = {-passes_previously_accepted}
     .note = {-passes_see_issue(issue: "80564")}
 
-passes_inline_ignored_function_prototype = `#[inline]` is ignored on function prototypes
+passes_inline_ignored_function_prototype =
+    `#[inline]` is ignored on function prototypes
 
-passes_inline_ignored_constants = `#[inline]` is ignored on constants
+passes_inline_ignored_constants =
+    `#[inline]` is ignored on constants
     .warn = {-passes_previously_accepted}
     .note = {-passes_see_issue(issue: "65833")}
 
-passes_inline_not_fn_or_closure = attribute should be applied to function or closure
+passes_inline_not_fn_or_closure =
+    attribute should be applied to function or closure
     .label = not a function or closure
 
-passes_no_coverage_ignored_function_prototype = `#[no_coverage]` is ignored on function prototypes
+passes_no_coverage_ignored_function_prototype =
+    `#[no_coverage]` is ignored on function prototypes
 
 passes_no_coverage_propagate =
     `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
 
-passes_no_coverage_fn_defn = `#[no_coverage]` may only be applied to function definitions
+passes_no_coverage_fn_defn =
+    `#[no_coverage]` may only be applied to function definitions
 
-passes_no_coverage_not_coverable = `#[no_coverage]` must be applied to coverable code
+passes_no_coverage_not_coverable =
+    `#[no_coverage]` must be applied to coverable code
     .label = not coverable code
 
-passes_should_be_applied_to_fn = attribute should be applied to a function definition
+passes_should_be_applied_to_fn =
+    attribute should be applied to a function definition
     .label = not a function definition
 
-passes_naked_tracked_caller = cannot use `#[track_caller]` with `#[naked]`
+passes_naked_tracked_caller =
+    cannot use `#[track_caller]` with `#[naked]`
 
-passes_should_be_applied_to_struct_enum = attribute should be applied to a struct or enum
+passes_should_be_applied_to_struct_enum =
+    attribute should be applied to a struct or enum
     .label = not a struct or enum
 
-passes_should_be_applied_to_trait = attribute should be applied to a trait
+passes_should_be_applied_to_trait =
+    attribute should be applied to a trait
     .label = not a trait
 
-passes_target_feature_on_statement = {passes_should_be_applied_to_fn}
+passes_target_feature_on_statement =
+    {passes_should_be_applied_to_fn}
     .warn = {-passes_previously_accepted}
     .label = {passes_should_be_applied_to_fn.label}
 
-passes_should_be_applied_to_static = attribute should be applied to a static
+passes_should_be_applied_to_static =
+    attribute should be applied to a static
     .label = not a static
 
-passes_doc_expect_str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
+passes_doc_expect_str =
+    doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
 
-passes_doc_alias_empty = {$attr_str} attribute cannot have empty value
+passes_doc_alias_empty =
+    {$attr_str} attribute cannot have empty value
 
-passes_doc_alias_bad_char = {$char_} character isn't allowed in {$attr_str}
+passes_doc_alias_bad_char =
+    {$char_} character isn't allowed in {$attr_str}
 
-passes_doc_alias_start_end = {$attr_str} cannot start or end with ' '
+passes_doc_alias_start_end =
+    {$attr_str} cannot start or end with ' '
 
-passes_doc_alias_bad_location = {$attr_str} isn't allowed on {$location}
+passes_doc_alias_bad_location =
+    {$attr_str} isn't allowed on {$location}
 
-passes_doc_alias_not_an_alias = {$attr_str} is the same as the item's name
+passes_doc_alias_not_an_alias =
+    {$attr_str} is the same as the item's name
 
 passes_doc_alias_duplicated = doc alias is duplicated
     .label = first defined here
 
-passes_doc_alias_not_string_literal = `#[doc(alias("a"))]` expects string literals
+passes_doc_alias_not_string_literal =
+    `#[doc(alias("a"))]` expects string literals
 
 passes_doc_alias_malformed =
     doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
 
-passes_doc_keyword_empty_mod = `#[doc(keyword = "...")]` should be used on empty modules
+passes_doc_keyword_empty_mod =
+    `#[doc(keyword = "...")]` should be used on empty modules
 
-passes_doc_keyword_not_mod = `#[doc(keyword = "...")]` should be used on modules
+passes_doc_keyword_not_mod =
+    `#[doc(keyword = "...")]` should be used on modules
 
-passes_doc_keyword_invalid_ident = `{$doc_keyword}` is not a valid identifier
+passes_doc_keyword_invalid_ident =
+    `{$doc_keyword}` is not a valid identifier
 
 passes_doc_fake_variadic_not_valid =
     `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity
 
-passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks
+passes_doc_keyword_only_impl =
+    `#[doc(keyword = "...")]` should be used on impl blocks
 
-passes_doc_inline_conflict_first = this attribute...
-passes_doc_inline_conflict_second = ...conflicts with this attribute
-passes_doc_inline_conflict = conflicting doc inlining attributes
+passes_doc_inline_conflict_first =
+    this attribute...
+
+passes_doc_inline_conflict_second =
+    {"."}..conflicts with this attribute
+
+passes_doc_inline_conflict =
+    conflicting doc inlining attributes
     .help = remove one of the conflicting attributes
 
-passes_doc_inline_only_use = this attribute can only be applied to a `use` item
+passes_doc_inline_only_use =
+    this attribute can only be applied to a `use` item
     .label = only applicable on `use` items
     .not_a_use_item_label = not a `use` item
     .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
@@ -99,30 +130,39 @@ passes_doc_inline_only_use = this attribute can only be applied to a `use` item
 passes_doc_attr_not_crate_level =
     `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
 
-passes_attr_crate_level = this attribute can only be applied at the crate level
+passes_attr_crate_level =
+    this attribute can only be applied at the crate level
     .suggestion = to apply to the crate, use an inner attribute
     .help = to apply to the crate, use an inner attribute
     .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
 
-passes_doc_test_unknown = unknown `doc(test)` attribute `{$path}`
+passes_doc_test_unknown =
+    unknown `doc(test)` attribute `{$path}`
 
-passes_doc_test_takes_list = `#[doc(test(...)]` takes a list of attributes
+passes_doc_test_takes_list =
+    `#[doc(test(...)]` takes a list of attributes
 
-passes_doc_primitive = `doc(primitive)` should never have been stable
+passes_doc_primitive =
+    `doc(primitive)` should never have been stable
 
-passes_doc_test_unknown_any = unknown `doc` attribute `{$path}`
+passes_doc_test_unknown_any =
+    unknown `doc` attribute `{$path}`
 
-passes_doc_test_unknown_spotlight = unknown `doc` attribute `{$path}`
+passes_doc_test_unknown_spotlight =
+    unknown `doc` attribute `{$path}`
     .note = `doc(spotlight)` was renamed to `doc(notable_trait)`
     .suggestion = use `notable_trait` instead
     .no_op_note = `doc(spotlight)` is now a no-op
 
-passes_doc_test_unknown_include = unknown `doc` attribute `{$path}`
+passes_doc_test_unknown_include =
+    unknown `doc` attribute `{$path}`
     .suggestion = use `doc = include_str!` instead
 
-passes_doc_invalid = invalid `doc` attribute
+passes_doc_invalid =
+    invalid `doc` attribute
 
-passes_pass_by_value = `pass_by_value` attribute should be applied to a struct, enum or type alias
+passes_pass_by_value =
+    `pass_by_value` attribute should be applied to a struct, enum or type alias
     .label = is not a struct, enum or type alias
 
 passes_allow_incoherent_impl =
@@ -137,42 +177,54 @@ passes_must_use_async =
     `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
     .label = this attribute does nothing, the `Future`s returned by async functions are already `must_use`
 
-passes_must_use_no_effect = `#[must_use]` has no effect when applied to {$article} {$target}
+passes_must_use_no_effect =
+    `#[must_use]` has no effect when applied to {$article} {$target}
 
-passes_must_not_suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait
+passes_must_not_suspend =
+    `must_not_suspend` attribute should be applied to a struct, enum, or trait
     .label = is not a struct, enum, or trait
 
-passes_cold = {passes_should_be_applied_to_fn}
+passes_cold =
+    {passes_should_be_applied_to_fn}
     .warn = {-passes_previously_accepted}
     .label = {passes_should_be_applied_to_fn.label}
 
-passes_link = attribute should be applied to an `extern` block with non-Rust ABI
+passes_link =
+    attribute should be applied to an `extern` block with non-Rust ABI
     .warn = {-passes_previously_accepted}
     .label = not an `extern` block
 
-passes_link_name = attribute should be applied to a foreign function or static
+passes_link_name =
+    attribute should be applied to a foreign function or static
     .warn = {-passes_previously_accepted}
     .label = not a foreign function or static
     .help = try `#[link(name = "{$value}")]` instead
 
-passes_no_link = attribute should be applied to an `extern crate` item
+passes_no_link =
+    attribute should be applied to an `extern crate` item
     .label = not an `extern crate` item
 
-passes_export_name = attribute should be applied to a free function, impl method or static
+passes_export_name =
+    attribute should be applied to a free function, impl method or static
     .label = not a free function, impl method or static
 
-passes_rustc_layout_scalar_valid_range_not_struct = attribute should be applied to a struct
+passes_rustc_layout_scalar_valid_range_not_struct =
+    attribute should be applied to a struct
     .label = not a struct
 
-passes_rustc_layout_scalar_valid_range_arg = expected exactly one integer literal argument
+passes_rustc_layout_scalar_valid_range_arg =
+    expected exactly one integer literal argument
 
-passes_rustc_legacy_const_generics_only = #[rustc_legacy_const_generics] functions must only have const generics
+passes_rustc_legacy_const_generics_only =
+    #[rustc_legacy_const_generics] functions must only have const generics
     .label = non-const generic parameter
 
-passes_rustc_legacy_const_generics_index = #[rustc_legacy_const_generics] must have one index for each generic parameter
+passes_rustc_legacy_const_generics_index =
+    #[rustc_legacy_const_generics] must have one index for each generic parameter
     .label = generic parameters
 
-passes_rustc_legacy_const_generics_index_exceed = index exceeds number of arguments
+passes_rustc_legacy_const_generics_index_exceed =
+    index exceeds number of arguments
     .label = there {$arg_count ->
         [one] is
         *[other] are
@@ -181,93 +233,438 @@ passes_rustc_legacy_const_generics_index_exceed = index exceeds number of argume
         *[other] arguments
     }
 
-passes_rustc_legacy_const_generics_index_negative = arguments should be non-negative integers
+passes_rustc_legacy_const_generics_index_negative =
+    arguments should be non-negative integers
 
-passes_rustc_dirty_clean = attribute requires -Z query-dep-graph to be enabled
+passes_rustc_dirty_clean =
+    attribute requires -Z query-dep-graph to be enabled
 
-passes_link_section = attribute should be applied to a function or static
+passes_link_section =
+    attribute should be applied to a function or static
     .warn = {-passes_previously_accepted}
     .label = not a function or static
 
-passes_no_mangle_foreign = `#[no_mangle]` has no effect on a foreign {$foreign_item_kind}
+passes_no_mangle_foreign =
+    `#[no_mangle]` has no effect on a foreign {$foreign_item_kind}
     .warn = {-passes_previously_accepted}
     .label = foreign {$foreign_item_kind}
     .note = symbol names in extern blocks are not mangled
     .suggestion = remove this attribute
 
-passes_no_mangle = attribute should be applied to a free function, impl method or static
+passes_no_mangle =
+    attribute should be applied to a free function, impl method or static
     .warn = {-passes_previously_accepted}
     .label = not a free function, impl method or static
 
-passes_repr_ident = meta item in `repr` must be an identifier
+passes_repr_ident =
+    meta item in `repr` must be an identifier
 
-passes_repr_conflicting = conflicting representation hints
+passes_repr_conflicting =
+    conflicting representation hints
 
-passes_used_static = attribute must be applied to a `static` variable
+passes_used_static =
+    attribute must be applied to a `static` variable
 
-passes_used_compiler_linker = `used(compiler)` and `used(linker)` can't be used together
+passes_used_compiler_linker =
+    `used(compiler)` and `used(linker)` can't be used together
 
-passes_allow_internal_unstable = attribute should be applied to a macro
+passes_allow_internal_unstable =
+    attribute should be applied to a macro
     .label = not a macro
 
-passes_debug_visualizer_placement = attribute should be applied to a module
+passes_debug_visualizer_placement =
+    attribute should be applied to a module
 
-passes_debug_visualizer_invalid = invalid argument
+passes_debug_visualizer_invalid =
+    invalid argument
     .note_1 = expected: `natvis_file = "..."`
     .note_2 = OR
     .note_3 = expected: `gdb_script_file = "..."`
 
-passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn`
+passes_debug_visualizer_unreadable =
+    couldn't read {$file}: {$error}
+
+passes_rustc_allow_const_fn_unstable =
+    attribute should be applied to `const fn`
     .label = not a `const fn`
 
-passes_rustc_std_internal_symbol = attribute should be applied to functions or statics
+passes_rustc_std_internal_symbol =
+    attribute should be applied to functions or statics
     .label = not a function or static
 
-passes_const_trait = attribute should be applied to a trait
+passes_const_trait =
+    attribute should be applied to a trait
 
-passes_stability_promotable = attribute cannot be applied to an expression
+passes_stability_promotable =
+    attribute cannot be applied to an expression
 
-passes_deprecated = attribute is ignored here
+passes_deprecated =
+    attribute is ignored here
 
-passes_macro_use = `#[{$name}]` only has an effect on `extern crate` and modules
+passes_macro_use =
+    `#[{$name}]` only has an effect on `extern crate` and modules
 
-passes_macro_export = `#[macro_export]` only has an effect on macro definitions
+passes_macro_export =
+    `#[macro_export]` only has an effect on macro definitions
 
-passes_plugin_registrar = `#[plugin_registrar]` only has an effect on functions
+passes_plugin_registrar =
+    `#[plugin_registrar]` only has an effect on functions
 
-passes_unused_empty_lints_note = attribute `{$name}` with an empty list has no effect
+passes_unused_empty_lints_note =
+    attribute `{$name}` with an empty list has no effect
 
-passes_unused_no_lints_note = attribute `{$name}` without any lints has no effect
+passes_unused_no_lints_note =
+    attribute `{$name}` without any lints has no effect
 
 passes_unused_default_method_body_const_note =
     `default_method_body_is_const` has been replaced with `#[const_trait]` on traits
 
-passes_unused = unused attribute
+passes_unused =
+    unused attribute
     .suggestion = remove this attribute
 
-passes_non_exported_macro_invalid_attrs = attribute should be applied to function or closure
+passes_non_exported_macro_invalid_attrs =
+    attribute should be applied to function or closure
     .label = not a function or closure
 
-passes_unused_duplicate = unused attribute
+passes_unused_duplicate =
+    unused attribute
     .suggestion = remove this attribute
     .note = attribute also specified here
     .warn = {-passes_previously_accepted}
 
-passes_unused_multiple = multiple `{$name}` attributes
+passes_unused_multiple =
+    multiple `{$name}` attributes
     .suggestion = remove this attribute
     .note = attribute also specified here
 
-passes_rustc_lint_opt_ty = `#[rustc_lint_opt_ty]` should be applied to a struct
+passes_rustc_lint_opt_ty =
+    `#[rustc_lint_opt_ty]` should be applied to a struct
     .label = not a struct
 
-passes_rustc_lint_opt_deny_field_access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field
+passes_rustc_lint_opt_deny_field_access =
+    `#[rustc_lint_opt_deny_field_access]` should be applied to a field
     .label = not a field
 
-passes_link_ordinal = attribute should be applied to a foreign function or static
+passes_link_ordinal =
+    attribute should be applied to a foreign function or static
     .label = not a foreign function or static
 
-passes_collapse_debuginfo = `collapse_debuginfo` attribute should be applied to macro definitions
+passes_collapse_debuginfo =
+    `collapse_debuginfo` attribute should be applied to macro definitions
     .label = not a macro definition
 
-passes_deprecated_annotation_has_no_effect = this `#[deprecated]` annotation has no effect
+passes_deprecated_annotation_has_no_effect =
+    this `#[deprecated]` annotation has no effect
     .suggestion = remove the unnecessary deprecation attribute
+
+passes_unknown_external_lang_item =
+    unknown external lang item: `{$lang_item}`
+
+passes_missing_panic_handler =
+    `#[panic_handler]` function required, but not found
+
+passes_alloc_func_required =
+    `#[alloc_error_handler]` function required, but not found
+
+passes_missing_alloc_error_handler =
+    use `#![feature(default_alloc_error_handler)]` for a default error handler
+
+passes_missing_lang_item =
+    language item required, but not found: `{$name}`
+    .note = this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library
+    .help = you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config`
+
+passes_lang_item_on_incorrect_target =
+    `{$name}` language item must be applied to a {$expected_target}
+    .label = attribute should be applied to a {$expected_target}, not a {$actual_target}
+
+passes_unknown_lang_item =
+    definition of an unknown language item: `{$name}`
+    .label = definition of unknown language item `{$name}`
+
+passes_invalid_attr_at_crate_level =
+    `{$name}` attribute cannot be used at crate level
+    .suggestion = perhaps you meant to use an outer attribute
+
+passes_duplicate_diagnostic_item =
+    duplicate diagnostic item found: `{$name}`.
+
+passes_duplicate_diagnostic_item_in_crate =
+    duplicate diagnostic item in crate `{$crate_name}`: `{$name}`.
+
+passes_diagnostic_item_first_defined =
+    the diagnostic item is first defined here
+    .note = the diagnostic item is first defined in crate `{$orig_crate_name}`.
+
+passes_abi =
+    abi: {$abi}
+
+passes_align =
+    align: {$align}
+
+passes_size =
+    size: {$size}
+
+passes_homogeneous_aggregate =
+    homogeneous_aggregate: {$homogeneous_aggregate}
+
+passes_layout_of =
+    layout_of({$normalized_ty}) = {$ty_layout}
+
+passes_unrecognized_field =
+    unrecognized field name `{$name}`
+
+passes_layout =
+    layout error: {$layout_error}
+
+passes_feature_stable_twice =
+    feature `{$feature}` is declared stable since {$since}, but was previously declared stable since {$prev_since}
+
+passes_feature_previously_declared =
+    feature `{$feature}` is declared {$declared}, but was previously declared {$prev_declared}
+
+passes_expr_not_allowed_in_context =
+    {$expr} is not allowed in a `{$context}`
+
+passes_const_impl_const_trait =
+    const `impl`s must be for traits marked with `#[const_trait]`
+    .note = this trait must be annotated with `#[const_trait]`
+
+passes_break_non_loop =
+    `break` with value from a `{$kind}` loop
+    .label = can only break with a value inside `loop` or breakable block
+    .label2 = you can't `break` with a value in a `{$kind}` loop
+    .suggestion = use `break` on its own without a value inside this `{$kind}` loop
+    .break_expr_suggestion = alternatively, you might have meant to use the available loop label
+
+passes_continue_labeled_block =
+    `continue` pointing to a labeled block
+    .label = labeled blocks cannot be `continue`'d
+    .block_label = labeled block the `continue` points to
+
+passes_break_inside_closure =
+    `{$name}` inside of a closure
+    .label = cannot `{$name}` inside of a closure
+    .closure_label = enclosing closure
+
+passes_break_inside_async_block =
+    `{$name}` inside of an `async` block
+    .label = cannot `{$name}` inside of an `async` block
+    .async_block_label = enclosing `async` block
+
+passes_outside_loop =
+    `{$name}` outside of a loop
+    .label = cannot `{$name}` outside of a loop
+
+passes_unlabeled_in_labeled_block =
+    unlabeled `{$cf_type}` inside of a labeled block
+    .label = `{$cf_type}` statements that would diverge to or through a labeled block need to bear a label
+
+passes_unlabeled_cf_in_while_condition =
+    `break` or `continue` with no label in the condition of a `while` loop
+    .label = unlabeled `{$cf_type}` in the condition of a `while` loop
+
+passes_cannot_inline_naked_function =
+    naked functions cannot be inlined
+
+passes_undefined_naked_function_abi =
+    Rust ABI is unsupported in naked functions
+
+passes_no_patterns =
+    patterns not allowed in naked function parameters
+
+passes_params_not_allowed =
+    referencing function parameters is not allowed in naked functions
+    .help = follow the calling convention in asm block to use parameters
+
+passes_naked_functions_asm_block =
+    naked functions must contain a single asm block
+    .label_multiple_asm = multiple asm blocks are unsupported in naked functions
+    .label_non_asm = non-asm is unsupported in naked functions
+
+passes_naked_functions_operands =
+    only `const` and `sym` operands are supported in naked functions
+
+passes_naked_functions_asm_options =
+    asm options unsupported in naked functions: {$unsupported_options}
+
+passes_naked_functions_must_use_noreturn =
+    asm in naked functions must use `noreturn` option
+    .suggestion = consider specifying that the asm block is responsible for returning from the function
+
+passes_attr_only_on_main =
+    `{$attr}` attribute can only be used on `fn main()`
+
+passes_attr_only_on_root_main =
+    `{$attr}` attribute can only be used on root `fn main()`
+
+passes_attr_only_in_functions =
+    `{$attr}` attribute can only be used on functions
+
+passes_multiple_rustc_main =
+    multiple functions with a `#[rustc_main]` attribute
+    .first = first `#[rustc_main]` function
+    .additional = additional `#[rustc_main]` function
+
+passes_multiple_start_functions =
+    multiple `start` functions
+    .label = multiple `start` functions
+    .previous = previous `#[start]` function here
+
+passes_extern_main =
+    the `main` function cannot be declared in an `extern` block
+
+passes_unix_sigpipe_values =
+    valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
+
+passes_no_main_function =
+    `main` function not found in crate `{$crate_name}`
+    .here_is_main = here is a function named `main`
+    .one_or_more_possible_main = you have one or more functions named `main` not defined at the crate level
+    .consider_moving_main = consider moving the `main` function definitions
+    .main_must_be_defined_at_crate = the main function must be defined at the crate level{$has_filename ->
+        [true] {" "}(in `{$filename}`)
+        *[false] {""}
+    }
+    .consider_adding_main_to_file = consider adding a `main` function to `{$filename}`
+    .consider_adding_main_at_crate = consider adding a `main` function at the crate level
+    .teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/
+    .non_function_main = non-function item at `crate::main` is found
+
+passes_duplicate_lang_item =
+    found duplicate lang item `{$lang_item_name}`
+    .first_defined_span = the lang item is first defined here
+    .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)
+    .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`.
+    .first_definition_local = first definition in the local crate (`{$orig_crate_name}`)
+    .second_definition_local = second definition in the local crate (`{$crate_name}`)
+    .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
+    .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
+
+passes_duplicate_lang_item_crate =
+    duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`.
+    .first_defined_span = the lang item is first defined here
+    .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)
+    .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`.
+    .first_definition_local = first definition in the local crate (`{$orig_crate_name}`)
+    .second_definition_local = second definition in the local crate (`{$crate_name}`)
+    .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
+    .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
+
+passes_duplicate_lang_item_crate_depends =
+    duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`.
+    .first_defined_span = the lang item is first defined here
+    .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)
+    .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`.
+    .first_definition_local = first definition in the local crate (`{$orig_crate_name}`)
+    .second_definition_local = second definition in the local crate (`{$crate_name}`)
+    .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
+    .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
+
+passes_incorrect_target =
+    `{$name}` language item must be applied to a {$kind} with {$at_least ->
+        [true] at least {$num}
+        *[false] {$num}
+    } generic {$num ->
+        [one] argument
+        *[other] arguments
+    }
+    .label = this {$kind} has {$actual_num} generic {$actual_num ->
+        [one] argument
+        *[other] arguments
+    }
+
+passes_useless_assignment =
+    useless assignment of {$is_field_assign ->
+        [true] field
+        *[false] variable
+    } of type `{$ty}` to itself
+
+passes_only_has_effect_on =
+    `#[{$attr_name}]` only has an effect on {$target_name ->
+        [function] functions
+        [module] modules
+        [implementation_block] implementation blocks
+        *[unspecified] (unspecified--this is a compiler bug)
+    }
+
+passes_object_lifetime_err =
+    {$repr}
+
+passes_unrecognized_repr_hint =
+    unrecognized representation hint
+    .help = valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+
+passes_attr_application_enum =
+    attribute should be applied to an enum
+    .label = not an enum
+
+passes_attr_application_struct =
+    attribute should be applied to a struct
+    .label = not a struct
+
+passes_attr_application_struct_union =
+    attribute should be applied to a struct or union
+    .label = not a struct or union
+
+passes_attr_application_struct_enum_union =
+    attribute should be applied to a struct, enum, or union
+    .label = not a struct, enum, or union
+
+passes_attr_application_struct_enum_function_union =
+    attribute should be applied to a struct, enum, function, or union
+    .label = not a struct, enum, function, or union
+
+passes_transparent_incompatible =
+    transparent {$target} cannot have other repr hints
+
+passes_deprecated_attribute =
+    deprecated attribute must be paired with either stable or unstable attribute
+
+passes_useless_stability =
+    this stability annotation is useless
+    .label = useless stability annotation
+    .item = the stability attribute annotates this item
+
+passes_invalid_stability =
+    invalid stability version found
+    .label = invalid stability version
+    .item = the stability attribute annotates this item
+
+passes_cannot_stabilize_deprecated =
+    an API can't be stabilized after it is deprecated
+    .label = invalid version
+    .item = the stability attribute annotates this item
+
+passes_invalid_deprecation_version =
+    invalid deprecation version found
+    .label = invalid deprecation version
+    .item = the stability attribute annotates this item
+
+passes_missing_stability_attr =
+    {$descr} has missing stability attribute
+
+passes_missing_const_stab_attr =
+    {$descr} has missing const stability attribute
+
+passes_trait_impl_const_stable =
+    trait implementations cannot be const stable yet
+    .note = see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
+
+passes_feature_only_on_nightly =
+    `#![feature]` may not be used on the {$release_channel} release channel
+
+passes_unknown_feature =
+    unknown feature `{$feature}`
+
+passes_implied_feature_not_exist =
+    feature `{$implied_by}` implying `{$feature}` does not exist
+
+passes_duplicate_feature_err =
+    the feature `{$feature}` has already been declared
+
+passes_missing_const_err =
+    attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const`
+    .help = make the function or method const
+    .label = attribute specified here
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index bbe6435be59..9b41234dcfb 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -255,6 +255,56 @@ impl EmissionGuarantee for () {
     }
 }
 
+/// Marker type which enables implementation of `create_note` and `emit_note` functions for
+/// note-without-error struct diagnostics.
+#[derive(Copy, Clone)]
+pub struct Noted;
+
+impl<'a> DiagnosticBuilder<'a, Noted> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    pub(crate) fn new_note(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
+        let diagnostic = Diagnostic::new_with_code(Level::Note, None, message);
+        Self::new_diagnostic_note(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    pub(crate) fn new_diagnostic_note(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+        debug!("Created new diagnostic");
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(diagnostic),
+            },
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl EmissionGuarantee for Noted {
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+        match db.inner.state {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+                handler.emit_diagnostic(&mut db.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+
+        Noted
+    }
+
+    fn make_diagnostic_builder(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self> {
+        DiagnosticBuilder::new_note(handler, msg)
+    }
+}
+
 impl<'a> DiagnosticBuilder<'a, !> {
     /// Convenience function for internal use, clients should use one of the
     /// `struct_*` methods on [`Handler`].
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 2f6686f8196..c8ccdc539af 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -374,7 +374,7 @@ pub use diagnostic::{
     AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgFromDisplay,
     DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
 };
-pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee};
+pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
 use std::backtrace::Backtrace;
 
 /// A handler deals with errors and other compiler output.
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 5f8729a8ddf..6045c1acdd0 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -191,10 +191,29 @@ pub enum LayoutError<'tcx> {
 
 impl<'a> IntoDiagnostic<'a, !> for LayoutError<'a> {
     fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> {
-        handler.struct_fatal(self.to_string())
+        let mut diag = handler.struct_fatal("");
+
+        match self {
+            LayoutError::Unknown(ty) => {
+                diag.set_arg("ty", ty);
+                diag.set_primary_message(rustc_errors::fluent::middle::unknown_layout);
+            }
+            LayoutError::SizeOverflow(ty) => {
+                diag.set_arg("ty", ty);
+                diag.set_primary_message(rustc_errors::fluent::middle::values_too_big);
+            }
+            LayoutError::NormalizationFailure(ty, e) => {
+                diag.set_arg("ty", ty);
+                diag.set_arg("failure_ty", e.get_type_for_failure());
+                diag.set_primary_message(rustc_errors::fluent::middle::cannot_be_normalized);
+            }
+        }
+        diag
     }
 }
 
+// FIXME: Once the other errors that embed this error have been converted to translateable
+// diagnostics, this Display impl should be removed.
 impl<'tcx> fmt::Display for LayoutError<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 5455d063c13..73fb89bbc38 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -4,10 +4,13 @@
 //! conflicts between multiple such attributes attached to the same
 //! item.
 
-use crate::errors;
+use crate::errors::{
+    self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
+    OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint,
+};
 use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{fluent, Applicability, MultiSpan};
 use rustc_expand::base::resolve_path;
 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
@@ -164,17 +167,17 @@ impl CheckAttrVisitor<'_> {
                 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
                 sym::deprecated => self.check_deprecated(hir_id, attr, span, target),
                 sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
-                sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
+                sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod),
                 sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
                 sym::macro_export => self.check_macro_export(hir_id, attr, target),
                 sym::ignore | sym::should_panic | sym::proc_macro_derive => {
-                    self.check_generic_attr(hir_id, attr, target, &[Target::Fn])
+                    self.check_generic_attr(hir_id, attr, target, Target::Fn)
                 }
                 sym::automatically_derived => {
-                    self.check_generic_attr(hir_id, attr, target, &[Target::Impl])
+                    self.check_generic_attr(hir_id, attr, target, Target::Impl)
                 }
                 sym::no_implicit_prelude => {
-                    self.check_generic_attr(hir_id, attr, target, &[Target::Mod])
+                    self.check_generic_attr(hir_id, attr, target, Target::Mod)
                 }
                 sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
                 _ => {}
@@ -351,31 +354,17 @@ impl CheckAttrVisitor<'_> {
         hir_id: HirId,
         attr: &Attribute,
         target: Target,
-        allowed_targets: &[Target],
+        allowed_target: Target,
     ) {
-        if !allowed_targets.iter().any(|t| t == &target) {
-            let name = attr.name_or_empty();
-            let mut i = allowed_targets.iter();
-            // Pluralize
-            let b = i.next().map_or_else(String::new, |t| t.to_string() + "s");
-            let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| {
-                if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 {
-                    b.push_str(", and ");
-                } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 {
-                    b.push_str(" and ");
-                } else {
-                    b.push_str(", ");
-                }
-                // Pluralize
-                b.push_str(&(allowed_target.to_string() + "s"));
-                b
-            });
-            self.tcx.struct_span_lint_hir(
+        if target != allowed_target {
+            self.tcx.emit_spanned_lint(
                 UNUSED_ATTRIBUTES,
                 hir_id,
                 attr.span,
-                &format!("`#[{name}]` only has an effect on {}", supported_names),
-                |lint| lint,
+                OnlyHasEffectOn {
+                    attr_name: attr.name_or_empty(),
+                    target_name: allowed_target.name().replace(" ", "_"),
+                },
             );
         }
     }
@@ -432,7 +421,7 @@ impl CheckAttrVisitor<'_> {
                     ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
                     ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
                 };
-                tcx.sess.span_err(p.span, &repr);
+                tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr });
             }
         }
     }
@@ -1605,12 +1594,17 @@ impl CheckAttrVisitor<'_> {
                 continue;
             }
 
-            let (article, allowed_targets) = match hint.name_or_empty() {
+            match hint.name_or_empty() {
                 sym::C => {
                     is_c = true;
                     match target {
                         Target::Struct | Target::Union | Target::Enum => continue,
-                        _ => ("a", "struct, enum, or union"),
+                        _ => {
+                            self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
+                                hint_span: hint.span(),
+                                span,
+                            });
+                        }
                     }
                 }
                 sym::align => {
@@ -1626,12 +1620,20 @@ impl CheckAttrVisitor<'_> {
 
                     match target {
                         Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
-                        _ => ("a", "struct, enum, function, or union"),
+                        _ => {
+                            self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion {
+                                hint_span: hint.span(),
+                                span,
+                            });
+                        }
                     }
                 }
                 sym::packed => {
                     if target != Target::Struct && target != Target::Union {
-                        ("a", "struct or union")
+                        self.tcx.sess.emit_err(AttrApplication::StructUnion {
+                            hint_span: hint.span(),
+                            span,
+                        });
                     } else {
                         continue;
                     }
@@ -1639,7 +1641,9 @@ impl CheckAttrVisitor<'_> {
                 sym::simd => {
                     is_simd = true;
                     if target != Target::Struct {
-                        ("a", "struct")
+                        self.tcx
+                            .sess
+                            .emit_err(AttrApplication::Struct { hint_span: hint.span(), span });
                     } else {
                         continue;
                     }
@@ -1648,7 +1652,12 @@ impl CheckAttrVisitor<'_> {
                     is_transparent = true;
                     match target {
                         Target::Struct | Target::Union | Target::Enum => continue,
-                        _ => ("a", "struct, enum, or union"),
+                        _ => {
+                            self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
+                                hint_span: hint.span(),
+                                span,
+                            });
+                        }
                     }
                 }
                 sym::i8
@@ -1665,35 +1674,18 @@ impl CheckAttrVisitor<'_> {
                 | sym::usize => {
                     int_reprs += 1;
                     if target != Target::Enum {
-                        ("an", "enum")
+                        self.tcx
+                            .sess
+                            .emit_err(AttrApplication::Enum { hint_span: hint.span(), span });
                     } else {
                         continue;
                     }
                 }
                 _ => {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        hint.span(),
-                        E0552,
-                        "unrecognized representation hint"
-                    )
-                    .help("valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, \
-                          `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`")
-                    .emit();
-
+                    self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() });
                     continue;
                 }
             };
-
-            struct_span_err!(
-                self.tcx.sess,
-                hint.span(),
-                E0517,
-                "{}",
-                &format!("attribute should be applied to {article} {allowed_targets}")
-            )
-            .span_label(span, &format!("not {article} {allowed_targets}"))
-            .emit();
         }
 
         // Just point at all repr hints if there are any incompatibilities.
@@ -1703,14 +1695,9 @@ impl CheckAttrVisitor<'_> {
         // Error on repr(transparent, <anything else>).
         if is_transparent && hints.len() > 1 {
             let hint_spans: Vec<_> = hint_spans.clone().collect();
-            struct_span_err!(
-                self.tcx.sess,
-                hint_spans,
-                E0692,
-                "transparent {} cannot have other repr hints",
-                target
-            )
-            .emit();
+            self.tcx
+                .sess
+                .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() });
         }
         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
         if (int_reprs > 1)
@@ -1862,14 +1849,12 @@ impl CheckAttrVisitor<'_> {
 
         match std::fs::File::open(&file) {
             Ok(_) => true,
-            Err(err) => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        meta_item.span,
-                        &format!("couldn't read {}: {}", file.display(), err),
-                    )
-                    .emit();
+            Err(error) => {
+                self.tcx.sess.emit_err(DebugVisualizerUnreadable {
+                    span: meta_item.span,
+                    file: &file,
+                    error,
+                });
                 false
             }
         }
@@ -2180,25 +2165,11 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
         if attr.style == AttrStyle::Inner {
             for attr_to_check in ATTRS_TO_CHECK {
                 if attr.has_name(*attr_to_check) {
-                    let mut err = tcx.sess.struct_span_err(
-                        attr.span,
-                        &format!(
-                            "`{}` attribute cannot be used at crate level",
-                            attr_to_check.to_ident_string()
-                        ),
-                    );
-                    // Only emit an error with a suggestion if we can create a
-                    // string out of the attribute span
-                    if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) {
-                        let replacement = src.replace("#!", "#");
-                        err.span_suggestion_verbose(
-                            attr.span,
-                            "perhaps you meant to use an outer attribute",
-                            replacement,
-                            rustc_errors::Applicability::MachineApplicable,
-                        );
-                    }
-                    err.emit();
+                    tcx.sess.emit_err(InvalidAttrAtCrateLevel {
+                        span: attr.span,
+                        snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(),
+                        name: *attr_to_check,
+                    });
                 }
             }
         }
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index e502b9b54e3..aa726d6cd92 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -8,7 +8,6 @@
 //! through, but errors for structured control flow in a `const` should be emitted here.
 
 use rustc_attr as attr;
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -18,6 +17,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::feature_err;
 use rustc_span::{sym, Span, Symbol};
 
+use crate::errors::ExprNotAllowedInContext;
+
 /// An expression that is not *always* legal in a const context.
 #[derive(Clone, Copy)]
 enum NonConstExpr {
@@ -133,18 +134,22 @@ impl<'tcx> CheckConstVisitor<'tcx> {
         let const_kind =
             const_kind.expect("`const_check_violated` may only be called inside a const context");
 
-        let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());
-
         let required_gates = required_gates.unwrap_or(&[]);
         let missing_gates: Vec<_> =
             required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
 
         match missing_gates.as_slice() {
             [] => {
-                struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit();
+                tcx.sess.emit_err(ExprNotAllowedInContext {
+                    span,
+                    expr: expr.name(),
+                    context: const_kind.keyword_name(),
+                });
             }
 
             [missing_primary, ref missing_secondary @ ..] => {
+                let msg =
+                    format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());
                 let mut err = feature_err(&tcx.sess.parse_sess, *missing_primary, span, &msg);
 
                 // If multiple feature gates would be required to enable this expression, include
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 08f704da62c..6a97ad3fe86 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -4,7 +4,7 @@
 
 use itertools::Itertools;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, Applicability, DelayDm, MultiSpan};
+use rustc_errors::{pluralize, Applicability, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -18,6 +18,8 @@ use rustc_session::lint;
 use rustc_span::symbol::{sym, Symbol};
 use std::mem;
 
+use crate::errors::UselessAssignment;
+
 // Any local node that may call something in its body block should be
 // explored. For example, if it's a live Node::Item that is a
 // function, then we should explore its block to check for codes that
@@ -180,19 +182,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 && !assign.span.from_expansion()
         {
                 let is_field_assign = matches!(lhs.kind, hir::ExprKind::Field(..));
-                self.tcx.struct_span_lint_hir(
+                self.tcx.emit_spanned_lint(
                     lint::builtin::DEAD_CODE,
                     assign.hir_id,
                     assign.span,
-                    DelayDm(|| format!(
-                            "useless assignment of {} of type `{}` to itself",
-                            if is_field_assign { "field" } else { "variable" },
-                            self.typeck_results().expr_ty(lhs),
-                        )),
-                    |lint| {
-                        lint
-
-                    },
+                    UselessAssignment { is_field_assign, ty: self.typeck_results().expr_ty(lhs) }
                 )
         }
     }
diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs
index e08683fe23b..253b0a88e48 100644
--- a/compiler/rustc_passes/src/debugger_visualizer.rs
+++ b/compiler/rustc_passes/src/debugger_visualizer.rs
@@ -13,6 +13,8 @@ use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType};
 
 use std::sync::Arc;
 
+use crate::errors::DebugVisualizerUnreadable;
+
 fn check_for_debugger_visualizer<'tcx>(
     tcx: TyCtxt<'tcx>,
     hir_id: HirId,
@@ -54,13 +56,12 @@ fn check_for_debugger_visualizer<'tcx>(
                     debugger_visualizers
                         .insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type));
                 }
-                Err(err) => {
-                    tcx.sess
-                        .struct_span_err(
-                            meta_item.span,
-                            &format!("couldn't read {}: {}", file.display(), err),
-                        )
-                        .emit();
+                Err(error) => {
+                    tcx.sess.emit_err(DebugVisualizerUnreadable {
+                        span: meta_item.span,
+                        file: &file,
+                        error,
+                    });
                 }
             }
         }
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index e428d9293db..3f991cf6572 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -14,7 +14,9 @@ use rustc_hir::diagnostic_items::DiagnosticItems;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{kw::Empty, sym, Symbol};
+
+use crate::errors::{DuplicateDiagnosticItem, DuplicateDiagnosticItemInCrate};
 
 fn observe_item<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -33,25 +35,22 @@ fn collect_item(tcx: TyCtxt<'_>, items: &mut DiagnosticItems, name: Symbol, item
     items.id_to_name.insert(item_def_id, name);
     if let Some(original_def_id) = items.name_to_id.insert(name, item_def_id) {
         if original_def_id != item_def_id {
-            let mut err = match tcx.hir().span_if_local(item_def_id) {
-                Some(span) => tcx
-                    .sess
-                    .struct_span_err(span, &format!("duplicate diagnostic item found: `{name}`.")),
-                None => tcx.sess.struct_err(&format!(
-                    "duplicate diagnostic item in crate `{}`: `{}`.",
-                    tcx.crate_name(item_def_id.krate),
-                    name
-                )),
-            };
-            if let Some(span) = tcx.hir().span_if_local(original_def_id) {
-                err.span_note(span, "the diagnostic item is first defined here");
+            let orig_span = tcx.hir().span_if_local(original_def_id);
+            let orig_crate_name = if orig_span.is_some() {
+                None
             } else {
-                err.note(&format!(
-                    "the diagnostic item is first defined in crate `{}`.",
-                    tcx.crate_name(original_def_id.krate)
-                ));
-            }
-            err.emit();
+                Some(tcx.crate_name(original_def_id.krate))
+            };
+            match tcx.hir().span_if_local(item_def_id) {
+                Some(span) => tcx.sess.emit_err(DuplicateDiagnosticItem { span, name }),
+                None => tcx.sess.emit_err(DuplicateDiagnosticItemInCrate {
+                    span: orig_span,
+                    orig_crate_name: orig_crate_name.unwrap_or(Empty),
+                    have_orig_crate_name: orig_crate_name.map(|_| ()),
+                    crate_name: tcx.crate_name(item_def_id.krate),
+                    name,
+                }),
+            };
         }
     }
 }
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index eec37d19a88..38a259ca884 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -1,5 +1,5 @@
 use rustc_ast::entry::EntryPointType;
-use rustc_errors::struct_span_err;
+use rustc_errors::error_code;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
@@ -8,7 +8,12 @@ use rustc_middle::ty::{DefIdTree, TyCtxt};
 use rustc_session::config::{sigpipe, CrateType, EntryFnType};
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_span::{Span, Symbol};
+
+use crate::errors::{
+    AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain,
+    MultipleStartFunctions, NoMainErr, UnixSigpipeValues,
+};
 
 struct EntryContext<'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -71,14 +76,9 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry
     }
 }
 
-fn err_if_attr_found(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol, details: &str) {
+fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> {
     let attrs = ctxt.tcx.hir().attrs(id.hir_id());
-    if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) {
-        ctxt.tcx
-            .sess
-            .struct_span_err(attr.span, &format!("`{}` attribute {}", sym, details))
-            .emit();
-    }
+    ctxt.tcx.sess.find_by_name(attrs, sym).map(|attr| attr.span)
 }
 
 fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
@@ -86,49 +86,47 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
 
     match entry_point_type(ctxt, id, at_root) {
         EntryPointType::None => {
-            err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
+            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
+                ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
+            }
         }
         _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => {
-            err_if_attr_found(ctxt, id, sym::start, "can only be used on functions");
-            err_if_attr_found(ctxt, id, sym::rustc_main, "can only be used on functions");
+            for attr in [sym::start, sym::rustc_main] {
+                if let Some(span) = attr_span_by_symbol(ctxt, id, attr) {
+                    ctxt.tcx.sess.emit_err(AttrOnlyInFunctions { span, attr });
+                }
+            }
         }
         EntryPointType::MainNamed => (),
         EntryPointType::OtherMain => {
-            err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on root `fn main()`");
+            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
+                ctxt.tcx.sess.emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe });
+            }
             ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id));
         }
         EntryPointType::RustcMainAttr => {
             if ctxt.attr_main_fn.is_none() {
                 ctxt.attr_main_fn = Some((id.def_id.def_id, ctxt.tcx.def_span(id.def_id)));
             } else {
-                struct_span_err!(
-                    ctxt.tcx.sess,
-                    ctxt.tcx.def_span(id.def_id.to_def_id()),
-                    E0137,
-                    "multiple functions with a `#[rustc_main]` attribute"
-                )
-                .span_label(
-                    ctxt.tcx.def_span(id.def_id.to_def_id()),
-                    "additional `#[rustc_main]` function",
-                )
-                .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[rustc_main]` function")
-                .emit();
+                ctxt.tcx.sess.emit_err(MultipleRustcMain {
+                    span: ctxt.tcx.def_span(id.def_id.to_def_id()),
+                    first: ctxt.attr_main_fn.unwrap().1,
+                    additional: ctxt.tcx.def_span(id.def_id.to_def_id()),
+                });
             }
         }
         EntryPointType::Start => {
-            err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
+            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
+                ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
+            }
             if ctxt.start_fn.is_none() {
                 ctxt.start_fn = Some((id.def_id.def_id, ctxt.tcx.def_span(id.def_id)));
             } else {
-                struct_span_err!(
-                    ctxt.tcx.sess,
-                    ctxt.tcx.def_span(id.def_id),
-                    E0138,
-                    "multiple `start` functions"
-                )
-                .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here")
-                .span_label(ctxt.tcx.def_span(id.def_id.to_def_id()), "multiple `start` functions")
-                .emit();
+                ctxt.tcx.sess.emit_err(MultipleStartFunctions {
+                    span: ctxt.tcx.def_span(id.def_id),
+                    labeled: ctxt.tcx.def_span(id.def_id.to_def_id()),
+                    previous: ctxt.start_fn.unwrap().1,
+                });
             }
         }
     }
@@ -144,12 +142,7 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
         if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() {
             // non-local main imports are handled below
             if let Some(def_id) = def_id.as_local() && matches!(tcx.hir().find_by_def_id(def_id), Some(Node::ForeignItem(_))) {
-                tcx.sess
-                    .struct_span_err(
-                        tcx.def_span(def_id),
-                        "the `main` function cannot be declared in an `extern` block",
-                    )
-                    .emit();
+                tcx.sess.emit_err(ExternMain { span: tcx.def_span(def_id) });
                 return None;
             }
 
@@ -182,12 +175,7 @@ fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
                 sigpipe::DEFAULT
             }
             _ => {
-                tcx.sess
-                    .struct_span_err(
-                        attr.span,
-                        "valid values for `#[unix_sigpipe = \"...\"]` are `inherit`, `sig_ign`, or `sig_dfl`",
-                    )
-                    .emit();
+                tcx.sess.emit_err(UnixSigpipeValues { span: attr.span });
                 sigpipe::DEFAULT
             }
         }
@@ -206,52 +194,29 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
     }
 
     // There is no main function.
-    let mut err = struct_span_err!(
-        tcx.sess,
-        DUMMY_SP,
-        E0601,
-        "`main` function not found in crate `{}`",
-        tcx.crate_name(LOCAL_CRATE)
-    );
-    let filename = &tcx.sess.local_crate_source_file;
-    let note = if !visitor.non_main_fns.is_empty() {
-        for &span in &visitor.non_main_fns {
-            err.span_note(span, "here is a function named `main`");
-        }
-        err.note("you have one or more functions named `main` not defined at the crate level");
-        err.help("consider moving the `main` function definitions");
-        // There were some functions named `main` though. Try to give the user a hint.
-        format!(
-            "the main function must be defined at the crate level{}",
-            filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default()
-        )
-    } else if let Some(filename) = filename {
-        format!("consider adding a `main` function to `{}`", filename.display())
-    } else {
-        String::from("consider adding a `main` function at the crate level")
-    };
+    let mut has_filename = true;
+    let filename = tcx.sess.local_crate_source_file.clone().unwrap_or_else(|| {
+        has_filename = false;
+        Default::default()
+    });
+    let main_def_opt = tcx.resolutions(()).main_def;
+    let diagnostic_id = error_code!(E0601);
+    let add_teach_note = tcx.sess.teach(&diagnostic_id);
     // The file may be empty, which leads to the diagnostic machinery not emitting this
     // note. This is a relatively simple way to detect that case and emit a span-less
     // note instead.
-    if tcx.sess.source_map().lookup_line(sp.hi()).is_ok() {
-        err.set_span(sp.shrink_to_hi());
-        err.span_label(sp.shrink_to_hi(), &note);
-    } else {
-        err.note(&note);
-    }
-
-    if let Some(main_def) = tcx.resolutions(()).main_def && main_def.opt_fn_def_id().is_none(){
-        // There is something at `crate::main`, but it is not a function definition.
-        err.span_label(main_def.span, "non-function item at `crate::main` is found");
-    }
-
-    if tcx.sess.teach(&err.get_code().unwrap()) {
-        err.note(
-            "If you don't know the basics of Rust, you can go look to the Rust Book \
-                  to get started: https://doc.rust-lang.org/book/",
-        );
-    }
-    err.emit();
+    let file_empty = !tcx.sess.source_map().lookup_line(sp.hi()).is_ok();
+
+    tcx.sess.emit_err(NoMainErr {
+        sp,
+        crate_name: tcx.crate_name(LOCAL_CRATE),
+        has_filename,
+        filename,
+        file_empty,
+        non_main_fns: visitor.non_main_fns.clone(),
+        main_def_opt,
+        add_teach_note,
+    });
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index cc231af71a2..1cc81a9ab98 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1,6 +1,16 @@
-use rustc_errors::{Applicability, MultiSpan};
+use std::{
+    io::Error,
+    path::{Path, PathBuf},
+};
+
+use rustc_ast::Label;
+use rustc_errors::{error_code, Applicability, ErrorGuaranteed, IntoDiagnostic, MultiSpan};
+use rustc_hir::{self as hir, ExprKind, Target};
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
-use rustc_span::{Span, Symbol};
+use rustc_middle::ty::{MainDefinition, Ty};
+use rustc_span::{Span, Symbol, DUMMY_SP};
+
+use crate::lang_items::Duplicate;
 
 #[derive(LintDiagnostic)]
 #[diag(passes::outer_crate_level_attr)]
@@ -527,6 +537,15 @@ pub struct DebugVisualizerInvalid {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes::debug_visualizer_unreadable)]
+pub struct DebugVisualizerUnreadable<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub file: &'a Path,
+    pub error: Error,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes::rustc_allow_const_fn_unstable)]
 pub struct RustcAllowConstFnUnstable {
     #[primary_span]
@@ -665,3 +684,765 @@ pub struct DeprecatedAnnotationHasNoEffect {
     #[suggestion(applicability = "machine-applicable", code = "")]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(passes::unknown_external_lang_item, code = "E0264")]
+pub struct UnknownExternLangItem {
+    #[primary_span]
+    pub span: Span,
+    pub lang_item: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_panic_handler)]
+pub struct MissingPanicHandler;
+
+#[derive(Diagnostic)]
+#[diag(passes::alloc_func_required)]
+pub struct AllocFuncRequired;
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_alloc_error_handler)]
+pub struct MissingAllocErrorHandler;
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_lang_item)]
+#[note]
+#[help]
+pub struct MissingLangItem {
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::lang_item_on_incorrect_target, code = "E0718")]
+pub struct LangItemOnIncorrectTarget {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub name: Symbol,
+    pub expected_target: Target,
+    pub actual_target: Target,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unknown_lang_item, code = "E0522")]
+pub struct UnknownLangItem {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+pub struct InvalidAttrAtCrateLevel {
+    pub span: Span,
+    pub snippet: Option<String>,
+    pub name: Symbol,
+}
+
+impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
+    fn into_diagnostic(
+        self,
+        handler: &'_ rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag =
+            handler.struct_err(rustc_errors::fluent::passes::invalid_attr_at_crate_level);
+        diag.set_span(self.span);
+        diag.set_arg("name", self.name);
+        // Only emit an error with a suggestion if we can create a string out
+        // of the attribute span
+        if let Some(src) = self.snippet {
+            let replacement = src.replace("#!", "#");
+            diag.span_suggestion_verbose(
+                self.span,
+                rustc_errors::fluent::passes::suggestion,
+                replacement,
+                rustc_errors::Applicability::MachineApplicable,
+            );
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::duplicate_diagnostic_item)]
+pub struct DuplicateDiagnosticItem {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::duplicate_diagnostic_item_in_crate)]
+pub struct DuplicateDiagnosticItemInCrate {
+    #[note(passes::diagnostic_item_first_defined)]
+    pub span: Option<Span>,
+    pub orig_crate_name: Symbol,
+    #[note]
+    pub have_orig_crate_name: Option<()>,
+    pub crate_name: Symbol,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::abi)]
+pub struct Abi {
+    #[primary_span]
+    pub span: Span,
+    pub abi: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::align)]
+pub struct Align {
+    #[primary_span]
+    pub span: Span,
+    pub align: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::size)]
+pub struct Size {
+    #[primary_span]
+    pub span: Span,
+    pub size: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::homogeneous_aggregate)]
+pub struct HomogeneousAggregate {
+    #[primary_span]
+    pub span: Span,
+    pub homogeneous_aggregate: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::layout_of)]
+pub struct LayoutOf {
+    #[primary_span]
+    pub span: Span,
+    pub normalized_ty: String,
+    pub ty_layout: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unrecognized_field)]
+pub struct UnrecognizedField {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::feature_stable_twice, code = "E0711")]
+pub struct FeatureStableTwice {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+    pub since: Symbol,
+    pub prev_since: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::feature_previously_declared, code = "E0711")]
+pub struct FeaturePreviouslyDeclared<'a, 'b> {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+    pub declared: &'a str,
+    pub prev_declared: &'b str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::expr_not_allowed_in_context, code = "E0744")]
+pub struct ExprNotAllowedInContext<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub expr: String,
+    pub context: &'a str,
+}
+
+pub struct BreakNonLoop<'a> {
+    pub span: Span,
+    pub head: Option<Span>,
+    pub kind: &'a str,
+    pub suggestion: String,
+    pub loop_label: Option<Label>,
+    pub break_label: Option<Label>,
+    pub break_expr_kind: &'a ExprKind<'a>,
+    pub break_expr_span: Span,
+}
+
+impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> {
+    fn into_diagnostic(
+        self,
+        handler: &rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = handler.struct_span_err_with_code(
+            self.span,
+            rustc_errors::fluent::passes::break_non_loop,
+            error_code!(E0571),
+        );
+        diag.set_arg("kind", self.kind);
+        diag.span_label(self.span, rustc_errors::fluent::passes::label);
+        if let Some(head) = self.head {
+            diag.span_label(head, rustc_errors::fluent::passes::label2);
+        }
+        diag.span_suggestion(
+            self.span,
+            rustc_errors::fluent::passes::suggestion,
+            self.suggestion,
+            Applicability::MaybeIncorrect,
+        );
+        if let (Some(label), None) = (self.loop_label, self.break_label) {
+            match self.break_expr_kind {
+                ExprKind::Path(hir::QPath::Resolved(
+                    None,
+                    hir::Path { segments: [segment], res: hir::def::Res::Err, .. },
+                )) if label.ident.to_string() == format!("'{}", segment.ident) => {
+                    // This error is redundant, we will have already emitted a
+                    // suggestion to use the label when `segment` wasn't found
+                    // (hence the `Res::Err` check).
+                    diag.delay_as_bug();
+                }
+                _ => {
+                    diag.span_suggestion(
+                        self.break_expr_span,
+                        rustc_errors::fluent::passes::break_expr_suggestion,
+                        label.ident,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::continue_labeled_block, code = "E0696")]
+pub struct ContinueLabeledBlock {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::block_label)]
+    pub block_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::break_inside_closure, code = "E0267")]
+pub struct BreakInsideClosure<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::closure_label)]
+    pub closure_span: Span,
+    pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::break_inside_async_block, code = "E0267")]
+pub struct BreakInsideAsyncBlock<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::async_block_label)]
+    pub closure_span: Span,
+    pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::outside_loop, code = "E0268")]
+pub struct OutsideLoop<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unlabeled_in_labeled_block, code = "E0695")]
+pub struct UnlabeledInLabeledBlock<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub cf_type: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unlabeled_cf_in_while_condition, code = "E0590")]
+pub struct UnlabeledCfInWhileCondition<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub cf_type: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::cannot_inline_naked_function)]
+pub struct CannotInlineNakedFunction {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes::undefined_naked_function_abi)]
+pub struct UndefinedNakedFunctionAbi;
+
+#[derive(Diagnostic)]
+#[diag(passes::no_patterns)]
+pub struct NoPatterns {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::params_not_allowed)]
+#[help]
+pub struct ParamsNotAllowed {
+    #[primary_span]
+    pub span: Span,
+}
+
+pub struct NakedFunctionsAsmBlock {
+    pub span: Span,
+    pub multiple_asms: Vec<Span>,
+    pub non_asms: Vec<Span>,
+}
+
+impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock {
+    fn into_diagnostic(
+        self,
+        handler: &rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = handler.struct_span_err_with_code(
+            self.span,
+            rustc_errors::fluent::passes::naked_functions_asm_block,
+            error_code!(E0787),
+        );
+        for span in self.multiple_asms.iter() {
+            diag.span_label(*span, rustc_errors::fluent::passes::label_multiple_asm);
+        }
+        for span in self.non_asms.iter() {
+            diag.span_label(*span, rustc_errors::fluent::passes::label_non_asm);
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::naked_functions_operands, code = "E0787")]
+pub struct NakedFunctionsOperands {
+    #[primary_span]
+    pub unsupported_operands: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::naked_functions_asm_options, code = "E0787")]
+pub struct NakedFunctionsAsmOptions {
+    #[primary_span]
+    pub span: Span,
+    pub unsupported_options: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::naked_functions_must_use_noreturn, code = "E0787")]
+pub struct NakedFunctionsMustUseNoreturn {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")]
+    pub last_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::attr_only_on_main)]
+pub struct AttrOnlyOnMain {
+    #[primary_span]
+    pub span: Span,
+    pub attr: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::attr_only_on_root_main)]
+pub struct AttrOnlyOnRootMain {
+    #[primary_span]
+    pub span: Span,
+    pub attr: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::attr_only_in_functions)]
+pub struct AttrOnlyInFunctions {
+    #[primary_span]
+    pub span: Span,
+    pub attr: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::multiple_rustc_main, code = "E0137")]
+pub struct MultipleRustcMain {
+    #[primary_span]
+    pub span: Span,
+    #[label(passes::first)]
+    pub first: Span,
+    #[label(passes::additional)]
+    pub additional: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::multiple_start_functions, code = "E0138")]
+pub struct MultipleStartFunctions {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub labeled: Span,
+    #[label(passes::previous)]
+    pub previous: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::extern_main)]
+pub struct ExternMain {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unix_sigpipe_values)]
+pub struct UnixSigpipeValues {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::no_main_function, code = "E0601")]
+pub struct NoMainFunction {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: String,
+}
+
+pub struct NoMainErr {
+    pub sp: Span,
+    pub crate_name: Symbol,
+    pub has_filename: bool,
+    pub filename: PathBuf,
+    pub file_empty: bool,
+    pub non_main_fns: Vec<Span>,
+    pub main_def_opt: Option<MainDefinition>,
+    pub add_teach_note: bool,
+}
+
+impl<'a> IntoDiagnostic<'a> for NoMainErr {
+    fn into_diagnostic(
+        self,
+        handler: &'a rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let mut diag = handler.struct_span_err_with_code(
+            DUMMY_SP,
+            rustc_errors::fluent::passes::no_main_function,
+            error_code!(E0601),
+        );
+        diag.set_arg("crate_name", self.crate_name);
+        diag.set_arg("filename", self.filename);
+        diag.set_arg("has_filename", self.has_filename);
+        let note = if !self.non_main_fns.is_empty() {
+            for &span in &self.non_main_fns {
+                diag.span_note(span, rustc_errors::fluent::passes::here_is_main);
+            }
+            diag.note(rustc_errors::fluent::passes::one_or_more_possible_main);
+            diag.help(rustc_errors::fluent::passes::consider_moving_main);
+            // There were some functions named `main` though. Try to give the user a hint.
+            rustc_errors::fluent::passes::main_must_be_defined_at_crate
+        } else if self.has_filename {
+            rustc_errors::fluent::passes::consider_adding_main_to_file
+        } else {
+            rustc_errors::fluent::passes::consider_adding_main_at_crate
+        };
+        if self.file_empty {
+            diag.note(note);
+        } else {
+            diag.set_span(self.sp.shrink_to_hi());
+            diag.span_label(self.sp.shrink_to_hi(), note);
+        }
+
+        if let Some(main_def) = self.main_def_opt && main_def.opt_fn_def_id().is_none(){
+            // There is something at `crate::main`, but it is not a function definition.
+            diag.span_label(main_def.span, rustc_errors::fluent::passes::non_function_main);
+        }
+
+        if self.add_teach_note {
+            diag.note(rustc_errors::fluent::passes::teach_note);
+        }
+        diag
+    }
+}
+
+pub struct DuplicateLangItem {
+    pub local_span: Option<Span>,
+    pub lang_item_name: Symbol,
+    pub crate_name: Symbol,
+    pub dependency_of: Symbol,
+    pub is_local: bool,
+    pub path: String,
+    pub first_defined_span: Option<Span>,
+    pub orig_crate_name: Symbol,
+    pub orig_dependency_of: Symbol,
+    pub orig_is_local: bool,
+    pub orig_path: String,
+    pub(crate) duplicate: Duplicate,
+}
+
+impl IntoDiagnostic<'_> for DuplicateLangItem {
+    fn into_diagnostic(
+        self,
+        handler: &rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = handler.struct_err_with_code(
+            match self.duplicate {
+                Duplicate::Plain => rustc_errors::fluent::passes::duplicate_lang_item,
+
+                Duplicate::Crate => rustc_errors::fluent::passes::duplicate_lang_item_crate,
+                Duplicate::CrateDepends => {
+                    rustc_errors::fluent::passes::duplicate_lang_item_crate_depends
+                }
+            },
+            error_code!(E0152),
+        );
+        diag.set_arg("lang_item_name", self.lang_item_name);
+        diag.set_arg("crate_name", self.crate_name);
+        diag.set_arg("dependency_of", self.dependency_of);
+        diag.set_arg("path", self.path);
+        diag.set_arg("orig_crate_name", self.orig_crate_name);
+        diag.set_arg("orig_dependency_of", self.orig_dependency_of);
+        diag.set_arg("orig_path", self.orig_path);
+        if let Some(span) = self.local_span {
+            diag.set_span(span);
+        }
+        if let Some(span) = self.first_defined_span {
+            diag.span_note(span, rustc_errors::fluent::passes::first_defined_span);
+        } else {
+            if self.orig_dependency_of.is_empty() {
+                diag.note(rustc_errors::fluent::passes::first_defined_crate);
+            } else {
+                diag.note(rustc_errors::fluent::passes::first_defined_crate_depends);
+            }
+
+            if self.orig_is_local {
+                diag.note(rustc_errors::fluent::passes::first_definition_local);
+            } else {
+                diag.note(rustc_errors::fluent::passes::first_definition_path);
+            }
+
+            if self.is_local {
+                diag.note(rustc_errors::fluent::passes::second_definition_local);
+            } else {
+                diag.note(rustc_errors::fluent::passes::second_definition_path);
+            }
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::incorrect_target, code = "E0718")]
+pub struct IncorrectTarget<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub generics_span: Span,
+    pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn`
+    pub kind: &'static str,
+    pub num: usize,
+    pub actual_num: usize,
+    pub at_least: bool,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes::useless_assignment)]
+pub struct UselessAssignment<'a> {
+    pub is_field_assign: bool,
+    pub ty: Ty<'a>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes::only_has_effect_on)]
+pub struct OnlyHasEffectOn {
+    pub attr_name: Symbol,
+    pub target_name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::object_lifetime_err)]
+pub struct ObjectLifetimeErr {
+    #[primary_span]
+    pub span: Span,
+    pub repr: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unrecognized_repr_hint, code = "E0552")]
+#[help]
+pub struct UnrecognizedReprHint {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+pub enum AttrApplication {
+    #[diag(passes::attr_application_enum, code = "E0517")]
+    Enum {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct, code = "E0517")]
+    Struct {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct_union, code = "E0517")]
+    StructUnion {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct_enum_union, code = "E0517")]
+    StructEnumUnion {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct_enum_function_union, code = "E0517")]
+    StructEnumFunctionUnion {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::transparent_incompatible, code = "E0692")]
+pub struct TransparentIncompatible {
+    #[primary_span]
+    pub hint_spans: Vec<Span>,
+    pub target: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::deprecated_attribute, code = "E0549")]
+pub struct DeprecatedAttribute {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::useless_stability)]
+pub struct UselessStability {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::invalid_stability)]
+pub struct InvalidStability {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::cannot_stabilize_deprecated)]
+pub struct CannotStabilizeDeprecated {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::invalid_deprecation_version)]
+pub struct InvalidDeprecationVersion {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_stability_attr)]
+pub struct MissingStabilityAttr<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub descr: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_const_stab_attr)]
+pub struct MissingConstStabAttr<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub descr: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::trait_impl_const_stable)]
+#[note]
+pub struct TraitImplConstStable {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::feature_only_on_nightly, code = "E0554")]
+pub struct FeatureOnlyOnNightly {
+    #[primary_span]
+    pub span: Span,
+    pub release_channel: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unknown_feature, code = "E0635")]
+pub struct UnknownFeature {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::implied_feature_not_exist)]
+pub struct ImpliedFeatureNotExist {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+    pub implied_by: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::duplicate_feature_err, code = "E0636")]
+pub struct DuplicateFeatureErr {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+}
+#[derive(Diagnostic)]
+#[diag(passes::missing_const_err)]
+pub struct MissingConstErr {
+    #[primary_span]
+    #[help]
+    pub fn_sig_span: Span,
+    #[label]
+    pub const_span: Span,
+}
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 79900a90aed..71b0735192a 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -8,9 +8,11 @@
 //! * Functions called by the compiler itself.
 
 use crate::check_attr::target_from_impl_item;
+use crate::errors::{
+    DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem,
+};
 use crate::weak_lang_items;
 
-use rustc_errors::{pluralize, struct_span_err};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -18,10 +20,16 @@ use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS};
 use rustc_hir::{HirId, LangItem, LanguageItems, Target};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::cstore::ExternCrate;
-use rustc_span::Span;
+use rustc_span::{symbol::kw::Empty, Span};
 
 use rustc_middle::ty::query::Providers;
 
+pub(crate) enum Duplicate {
+    Plain,
+    Crate,
+    CrateDepends,
+}
+
 struct LanguageItemCollector<'tcx> {
     items: LanguageItems,
     tcx: TyCtxt<'tcx>,
@@ -34,42 +42,24 @@ impl<'tcx> LanguageItemCollector<'tcx> {
 
     fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) {
         let attrs = self.tcx.hir().attrs(hir_id);
-        if let Some((value, span)) = extract(&attrs) {
-            match ITEM_REFS.get(&value).cloned() {
+        if let Some((name, span)) = extract(&attrs) {
+            match ITEM_REFS.get(&name).cloned() {
                 // Known lang item with attribute on correct target.
                 Some((item_index, expected_target)) if actual_target == expected_target => {
                     self.collect_item_extended(item_index, hir_id, span);
                 }
                 // Known lang item with attribute on incorrect target.
                 Some((_, expected_target)) => {
-                    struct_span_err!(
-                        self.tcx.sess,
+                    self.tcx.sess.emit_err(LangItemOnIncorrectTarget {
                         span,
-                        E0718,
-                        "`{}` language item must be applied to a {}",
-                        value,
+                        name,
                         expected_target,
-                    )
-                    .span_label(
-                        span,
-                        format!(
-                            "attribute should be applied to a {}, not a {}",
-                            expected_target, actual_target,
-                        ),
-                    )
-                    .emit();
+                        actual_target,
+                    });
                 }
                 // Unknown lang item.
                 _ => {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0522,
-                        "definition of an unknown language item: `{}`",
-                        value
-                    )
-                    .span_label(span, format!("definition of unknown language item `{}`", value))
-                    .emit();
+                    self.tcx.sess.emit_err(UnknownLangItem { span, name });
                 }
             }
         }
@@ -79,74 +69,72 @@ impl<'tcx> LanguageItemCollector<'tcx> {
         // Check for duplicates.
         if let Some(original_def_id) = self.items.items[item_index] {
             if original_def_id != item_def_id {
-                let lang_item = LangItem::from_u32(item_index as u32).unwrap();
-                let name = lang_item.name();
-                let mut err = match self.tcx.hir().span_if_local(item_def_id) {
-                    Some(span) => struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0152,
-                        "found duplicate lang item `{}`",
-                        name
-                    ),
-                    None => match self.tcx.extern_crate(item_def_id) {
-                        Some(ExternCrate { dependency_of, .. }) => {
-                            self.tcx.sess.struct_err(&format!(
-                                "duplicate lang item in crate `{}` (which `{}` depends on): `{}`.",
-                                self.tcx.crate_name(item_def_id.krate),
-                                self.tcx.crate_name(*dependency_of),
-                                name
-                            ))
-                        }
-                        _ => self.tcx.sess.struct_err(&format!(
-                            "duplicate lang item in crate `{}`: `{}`.",
-                            self.tcx.crate_name(item_def_id.krate),
-                            name
-                        )),
-                    },
+                let local_span = self.tcx.hir().span_if_local(item_def_id);
+                let lang_item_name = LangItem::from_u32(item_index as u32).unwrap().name();
+                let crate_name = self.tcx.crate_name(item_def_id.krate);
+                let mut dependency_of = Empty;
+                let is_local = item_def_id.is_local();
+                let path = if is_local {
+                    String::new()
+                } else {
+                    self.tcx
+                        .crate_extern_paths(item_def_id.krate)
+                        .iter()
+                        .map(|p| p.display().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                        .into()
                 };
-                if let Some(span) = self.tcx.hir().span_if_local(original_def_id) {
-                    err.span_note(span, "the lang item is first defined here");
+                let first_defined_span = self.tcx.hir().span_if_local(original_def_id);
+                let mut orig_crate_name = Empty;
+                let mut orig_dependency_of = Empty;
+                let orig_is_local = original_def_id.is_local();
+                let orig_path = if orig_is_local {
+                    String::new()
                 } else {
-                    match self.tcx.extern_crate(original_def_id) {
-                        Some(ExternCrate { dependency_of, .. }) => {
-                            err.note(&format!(
-                                "the lang item is first defined in crate `{}` (which `{}` depends on)",
-                                self.tcx.crate_name(original_def_id.krate),
-                                self.tcx.crate_name(*dependency_of)
-                            ));
-                        }
-                        _ => {
-                            err.note(&format!(
-                                "the lang item is first defined in crate `{}`.",
-                                self.tcx.crate_name(original_def_id.krate)
-                            ));
-                        }
+                    self.tcx
+                        .crate_extern_paths(original_def_id.krate)
+                        .iter()
+                        .map(|p| p.display().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                        .into()
+                };
+                if first_defined_span.is_none() {
+                    orig_crate_name = self.tcx.crate_name(original_def_id.krate);
+                    if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
+                        self.tcx.extern_crate(original_def_id)
+                    {
+                        orig_dependency_of = self.tcx.crate_name(*inner_dependency_of);
                     }
-                    let mut note_def = |which, def_id: DefId| {
-                        let crate_name = self.tcx.crate_name(def_id.krate);
-                        let note = if def_id.is_local() {
-                            format!("{} definition in the local crate (`{}`)", which, crate_name)
-                        } else {
-                            let paths: Vec<_> = self
-                                .tcx
-                                .crate_extern_paths(def_id.krate)
-                                .iter()
-                                .map(|p| p.display().to_string())
-                                .collect();
-                            format!(
-                                "{} definition in `{}` loaded from {}",
-                                which,
-                                crate_name,
-                                paths.join(", ")
-                            )
-                        };
-                        err.note(&note);
-                    };
-                    note_def("first", original_def_id);
-                    note_def("second", item_def_id);
                 }
-                err.emit();
+
+                let duplicate = if local_span.is_some() {
+                    Duplicate::Plain
+                } else {
+                    match self.tcx.extern_crate(item_def_id) {
+                        Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
+                            dependency_of = self.tcx.crate_name(*inner_dependency_of);
+                            Duplicate::CrateDepends
+                        }
+                        _ => Duplicate::Crate,
+                    }
+                };
+
+                self.tcx.sess.emit_err(DuplicateLangItem {
+                    local_span,
+                    lang_item_name,
+                    crate_name,
+                    dependency_of,
+                    is_local,
+                    path,
+                    first_defined_span,
+                    orig_crate_name,
+                    orig_dependency_of,
+                    orig_is_local,
+                    orig_path,
+                    duplicate,
+                });
             }
         }
 
@@ -179,41 +167,30 @@ impl<'tcx> LanguageItemCollector<'tcx> {
                 None => (0, *item_span),
             };
 
+            let mut at_least = false;
             let required = match lang_item.required_generics() {
-                GenericRequirement::Exact(num) if num != actual_num => {
-                    Some((format!("{}", num), pluralize!(num)))
-                }
+                GenericRequirement::Exact(num) if num != actual_num => Some(num),
                 GenericRequirement::Minimum(num) if actual_num < num => {
-                    Some((format!("at least {}", num), pluralize!(num)))
-                }
+                    at_least = true;
+                    Some(num)}
+                ,
                 // If the number matches, or there is no requirement, handle it normally
                 _ => None,
             };
 
-            if let Some((range_str, pluralized)) = required {
+            if let Some(num) = required {
                 // We are issuing E0718 "incorrect target" here, because while the
                 // item kind of the target is correct, the target is still wrong
                 // because of the wrong number of generic arguments.
-                struct_span_err!(
-                    self.tcx.sess,
+                self.tcx.sess.emit_err(IncorrectTarget {
                     span,
-                    E0718,
-                    "`{}` language item must be applied to a {} with {} generic argument{}",
-                    name,
-                    kind.descr(),
-                    range_str,
-                    pluralized,
-                )
-                .span_label(
                     generics_span,
-                    format!(
-                        "this {} has {} generic argument{}",
-                        kind.descr(),
-                        actual_num,
-                        pluralize!(actual_num),
-                    ),
-                )
-                .emit();
+                    name: name.as_str(),
+                    kind: kind.descr(),
+                    num,
+                    actual_num,
+                    at_least,
+                });
 
                 // return early to not collect the lang item
                 return;
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 46c4a702fde..c1085094962 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -3,10 +3,13 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::abi::{HasDataLayout, TargetDataLayout};
 
+use crate::errors::{Abi, Align, HomogeneousAggregate, LayoutOf, Size, UnrecognizedField};
+
 pub fn test_layout(tcx: TyCtxt<'_>) {
     if tcx.features().rustc_attrs {
         // if the `rustc_attrs` feature is not enabled, don't bother testing layout
@@ -35,62 +38,64 @@ fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attri
             for meta_item in meta_items {
                 match meta_item.name_or_empty() {
                     sym::abi => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("abi: {:?}", ty_layout.abi),
-                        );
+                        tcx.sess.emit_err(Abi {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            abi: format!("{:?}", ty_layout.abi),
+                        });
                     }
 
                     sym::align => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("align: {:?}", ty_layout.align),
-                        );
+                        tcx.sess.emit_err(Align {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            align: format!("{:?}", ty_layout.align),
+                        });
                     }
 
                     sym::size => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("size: {:?}", ty_layout.size),
-                        );
+                        tcx.sess.emit_err(Size {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            size: format!("{:?}", ty_layout.size),
+                        });
                     }
 
                     sym::homogeneous_aggregate => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!(
-                                "homogeneous_aggregate: {:?}",
-                                ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
+                        tcx.sess.emit_err(HomogeneousAggregate {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            homogeneous_aggregate: format!(
+                                "{:?}",
+                                ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env })
                             ),
-                        );
+                        });
                     }
 
                     sym::debug => {
-                        let normalized_ty = tcx.normalize_erasing_regions(
-                            param_env.with_reveal_all_normalized(tcx),
-                            ty,
-                        );
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout),
+                        let normalized_ty = format!(
+                            "{:?}",
+                            tcx.normalize_erasing_regions(
+                                param_env.with_reveal_all_normalized(tcx),
+                                ty,
+                            )
                         );
+                        let ty_layout = format!("{:#?}", *ty_layout);
+                        tcx.sess.emit_err(LayoutOf {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            normalized_ty,
+                            ty_layout,
+                        });
                     }
 
                     name => {
-                        tcx.sess.span_err(
-                            meta_item.span(),
-                            &format!("unrecognized field name `{}`", name),
-                        );
+                        tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name });
                     }
                 }
             }
         }
 
         Err(layout_error) => {
-            tcx.sess.span_err(
-                tcx.def_span(item_def_id.to_def_id()),
-                &format!("layout error: {:?}", layout_error),
-            );
+            tcx.sess.emit_fatal(Spanned {
+                node: layout_error,
+                span: tcx.def_span(item_def_id.to_def_id()),
+            });
         }
     }
 }
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 6e621b7eb5e..15f60f626c8 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -5,6 +5,8 @@
 //! This API is completely unstable and subject to change.
 
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 04173c792a9..b5843c0ae48 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -6,7 +6,6 @@
 
 use rustc_ast::{Attribute, MetaItemKind};
 use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER};
-use rustc_errors::struct_span_err;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::lib_features::LibFeatures;
@@ -15,6 +14,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::Symbol;
 use rustc_span::{sym, Span};
 
+use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice};
+
 fn new_lib_features() -> LibFeatures {
     LibFeatures { stable: Default::default(), unstable: Default::default() }
 }
@@ -92,14 +93,12 @@ impl<'tcx> LibFeatureCollector<'tcx> {
             (Some(since), _, false) => {
                 if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) {
                     if *prev_since != since {
-                        self.span_feature_error(
+                        self.tcx.sess.emit_err(FeatureStableTwice {
                             span,
-                            &format!(
-                                "feature `{}` is declared stable since {}, \
-                                 but was previously declared stable since {}",
-                                feature, since, prev_since,
-                            ),
-                        );
+                            feature,
+                            since,
+                            prev_since: *prev_since,
+                        });
                         return;
                     }
                 }
@@ -110,22 +109,17 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                 self.lib_features.unstable.insert(feature, span);
             }
             (Some(_), _, true) | (None, true, _) => {
-                self.span_feature_error(
+                let declared = if since.is_some() { "stable" } else { "unstable" };
+                let prev_declared = if since.is_none() { "stable" } else { "unstable" };
+                self.tcx.sess.emit_err(FeaturePreviouslyDeclared {
                     span,
-                    &format!(
-                        "feature `{}` is declared {}, but was previously declared {}",
-                        feature,
-                        if since.is_some() { "stable" } else { "unstable" },
-                        if since.is_none() { "stable" } else { "unstable" },
-                    ),
-                );
+                    feature,
+                    declared,
+                    prev_declared,
+                });
             }
         }
     }
-
-    fn span_feature_error(&self, span: Span, msg: &str) {
-        struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg).emit();
-    }
 }
 
 impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> {
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index cdda0e388dd..077194ec687 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -1,6 +1,5 @@
 use Context::*;
 
-use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -13,6 +12,11 @@ use rustc_session::Session;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::Span;
 
+use crate::errors::{
+    BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
+    UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
+};
+
 #[derive(Clone, Copy, Debug, PartialEq)]
 enum Context {
     Normal,
@@ -90,7 +94,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     Ok(loop_id) => Some(loop_id),
                     Err(hir::LoopIdError::OutsideLoopScope) => None,
                     Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
-                        self.emit_unlabled_cf_in_while_condition(e.span, "break");
+                        self.sess.emit_err(UnlabeledCfInWhileCondition {
+                            span: e.span,
+                            cf_type: "break",
+                        });
                         None
                     }
                     Err(hir::LoopIdError::UnresolvedLabel) => None,
@@ -116,69 +123,22 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     match loop_kind {
                         None | Some(hir::LoopSource::Loop) => (),
                         Some(kind) => {
-                            let mut err = struct_span_err!(
-                                self.sess,
-                                e.span,
-                                E0571,
-                                "`break` with value from a `{}` loop",
-                                kind.name()
-                            );
-                            err.span_label(
-                                e.span,
-                                "can only break with a value inside `loop` or breakable block",
+                            let suggestion = format!(
+                                "break{}",
+                                break_label
+                                    .label
+                                    .map_or_else(String::new, |l| format!(" {}", l.ident))
                             );
-                            if let Some(head) = head {
-                                err.span_label(
-                                    head,
-                                    &format!(
-                                        "you can't `break` with a value in a `{}` loop",
-                                        kind.name()
-                                    ),
-                                );
-                            }
-                            err.span_suggestion(
-                                e.span,
-                                &format!(
-                                    "use `break` on its own without a value inside this `{}` loop",
-                                    kind.name(),
-                                ),
-                                format!(
-                                    "break{}",
-                                    break_label
-                                        .label
-                                        .map_or_else(String::new, |l| format!(" {}", l.ident))
-                                ),
-                                Applicability::MaybeIncorrect,
-                            );
-                            if let (Some(label), None) = (loop_label, break_label.label) {
-                                match break_expr.kind {
-                                    hir::ExprKind::Path(hir::QPath::Resolved(
-                                        None,
-                                        hir::Path {
-                                            segments: [segment],
-                                            res: hir::def::Res::Err,
-                                            ..
-                                        },
-                                    )) if label.ident.to_string()
-                                        == format!("'{}", segment.ident) =>
-                                    {
-                                        // This error is redundant, we will have already emitted a
-                                        // suggestion to use the label when `segment` wasn't found
-                                        // (hence the `Res::Err` check).
-                                        err.delay_as_bug();
-                                    }
-                                    _ => {
-                                        err.span_suggestion(
-                                            break_expr.span,
-                                            "alternatively, you might have meant to use the \
-                                             available loop label",
-                                            label.ident,
-                                            Applicability::MaybeIncorrect,
-                                        );
-                                    }
-                                }
-                            }
-                            err.emit();
+                            self.sess.emit_err(BreakNonLoop {
+                                span: e.span,
+                                head,
+                                kind: kind.name(),
+                                suggestion,
+                                loop_label,
+                                break_label: break_label.label,
+                                break_expr_kind: &break_expr.kind,
+                                break_expr_span: break_expr.span,
+                            });
                         }
                     }
                 }
@@ -191,19 +151,17 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                 match destination.target_id {
                     Ok(loop_id) => {
                         if let Node::Block(block) = self.hir_map.find(loop_id).unwrap() {
-                            struct_span_err!(
-                                self.sess,
-                                e.span,
-                                E0696,
-                                "`continue` pointing to a labeled block"
-                            )
-                            .span_label(e.span, "labeled blocks cannot be `continue`'d")
-                            .span_label(block.span, "labeled block the `continue` points to")
-                            .emit();
+                            self.sess.emit_err(ContinueLabeledBlock {
+                                span: e.span,
+                                block_span: block.span,
+                            });
                         }
                     }
                     Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
-                        self.emit_unlabled_cf_in_while_condition(e.span, "continue");
+                        self.sess.emit_err(UnlabeledCfInWhileCondition {
+                            span: e.span,
+                            cf_type: "continue",
+                        });
                     }
                     Err(_) => {}
                 }
@@ -226,21 +184,16 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
     }
 
     fn require_break_cx(&self, name: &str, span: Span) {
-        let err_inside_of = |article, ty, closure_span| {
-            struct_span_err!(self.sess, span, E0267, "`{}` inside of {} {}", name, article, ty)
-                .span_label(span, format!("cannot `{}` inside of {} {}", name, article, ty))
-                .span_label(closure_span, &format!("enclosing {}", ty))
-                .emit();
-        };
-
         match self.cx {
             LabeledBlock | Loop(_) => {}
-            Closure(closure_span) => err_inside_of("a", "closure", closure_span),
-            AsyncClosure(closure_span) => err_inside_of("an", "`async` block", closure_span),
+            Closure(closure_span) => {
+                self.sess.emit_err(BreakInsideClosure { span, closure_span, name });
+            }
+            AsyncClosure(closure_span) => {
+                self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name });
+            }
             Normal | AnonConst => {
-                struct_span_err!(self.sess, span, E0268, "`{}` outside of a loop", name)
-                    .span_label(span, format!("cannot `{}` outside of a loop", name))
-                    .emit();
+                self.sess.emit_err(OutsideLoop { span, name });
             }
         }
     }
@@ -251,37 +204,13 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
         label: &Destination,
         cf_type: &str,
     ) -> bool {
-        if !span.is_desugaring(DesugaringKind::QuestionMark) && self.cx == LabeledBlock {
-            if label.label.is_none() {
-                struct_span_err!(
-                    self.sess,
-                    span,
-                    E0695,
-                    "unlabeled `{}` inside of a labeled block",
-                    cf_type
-                )
-                .span_label(
-                    span,
-                    format!(
-                        "`{}` statements that would diverge to or through \
-                                a labeled block need to bear a label",
-                        cf_type
-                    ),
-                )
-                .emit();
-                return true;
-            }
+        if !span.is_desugaring(DesugaringKind::QuestionMark)
+            && self.cx == LabeledBlock
+            && label.label.is_none()
+        {
+            self.sess.emit_err(UnlabeledInLabeledBlock { span, cf_type });
+            return true;
         }
         false
     }
-    fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) {
-        struct_span_err!(
-            self.sess,
-            span,
-            E0590,
-            "`break` or `continue` with no label in the condition of a `while` loop"
-        )
-        .span_label(span, format!("unlabeled `{}` in the condition of a `while` loop", cf_type))
-        .emit();
-    }
 }
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index 2690be66c21..acc54e7e110 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -1,7 +1,6 @@
 //! Checks validity of naked functions.
 
 use rustc_ast::InlineAsmOptions;
-use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
@@ -14,6 +13,12 @@ use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
+use crate::errors::{
+    CannotInlineNakedFunction, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions,
+    NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed,
+    UndefinedNakedFunctionAbi,
+};
+
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers { check_mod_naked_functions, ..*providers };
 }
@@ -56,7 +61,7 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
 fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let attrs = tcx.get_attrs(def_id.to_def_id(), sym::inline);
     for attr in attrs {
-        tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit();
+        tcx.sess.emit_err(CannotInlineNakedFunction { span: attr.span });
     }
 }
 
@@ -65,12 +70,11 @@ fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) {
     if abi == Abi::Rust {
         let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
         let span = tcx.def_span(def_id);
-        tcx.struct_span_lint_hir(
+        tcx.emit_spanned_lint(
             UNDEFINED_NAKED_FUNCTION_ABI,
             hir_id,
             span,
-            "Rust ABI is unsupported in naked functions",
-            |lint| lint,
+            UndefinedNakedFunctionAbi,
         );
     }
 }
@@ -82,12 +86,7 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
             hir::PatKind::Wild
             | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {}
             _ => {
-                tcx.sess
-                    .struct_span_err(
-                        param.pat.span,
-                        "patterns not allowed in naked function parameters",
-                    )
-                    .emit();
+                tcx.sess.emit_err(NoPatterns { span: param.pat.span });
             }
         }
     }
@@ -117,14 +116,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
         )) = expr.kind
         {
             if self.params.contains(var_hir_id) {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        expr.span,
-                        "referencing function parameters is not allowed in naked functions",
-                    )
-                    .help("follow the calling convention in asm block to use parameters")
-                    .emit();
+                self.tcx.sess.emit_err(ParamsNotAllowed { span: expr.span });
                 return;
             }
         }
@@ -139,26 +131,21 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<
     if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] {
         // Ok.
     } else {
-        let mut diag = struct_span_err!(
-            tcx.sess,
-            tcx.def_span(def_id),
-            E0787,
-            "naked functions must contain a single asm block"
-        );
-
         let mut must_show_error = false;
         let mut has_asm = false;
         let mut has_err = false;
+        let mut multiple_asms = vec![];
+        let mut non_asms = vec![];
         for &(kind, span) in &this.items {
             match kind {
                 ItemKind::Asm if has_asm => {
                     must_show_error = true;
-                    diag.span_label(span, "multiple asm blocks are unsupported in naked functions");
+                    multiple_asms.push(span);
                 }
                 ItemKind::Asm => has_asm = true,
                 ItemKind::NonAsm => {
                     must_show_error = true;
-                    diag.span_label(span, "non-asm is unsupported in naked functions");
+                    non_asms.push(span);
                 }
                 ItemKind::Err => has_err = true,
             }
@@ -168,9 +155,11 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<
         // errors, then don't show an additional error. This allows for appending/prepending
         // `compile_error!("...")` statements and reduces error noise.
         if must_show_error || !has_err {
-            diag.emit();
-        } else {
-            diag.cancel();
+            tcx.sess.emit_err(NakedFunctionsAsmBlock {
+                span: tcx.def_span(def_id),
+                multiple_asms,
+                non_asms,
+            });
         }
     }
 }
@@ -251,13 +240,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
             })
             .collect();
         if !unsupported_operands.is_empty() {
-            struct_span_err!(
-                self.tcx.sess,
-                unsupported_operands,
-                E0787,
-                "only `const` and `sym` operands are supported in naked functions",
-            )
-            .emit();
+            self.tcx.sess.emit_err(NakedFunctionsOperands { unsupported_operands });
         }
 
         let unsupported_options: Vec<&'static str> = [
@@ -273,14 +256,10 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
         .collect();
 
         if !unsupported_options.is_empty() {
-            struct_span_err!(
-                self.tcx.sess,
+            self.tcx.sess.emit_err(NakedFunctionsAsmOptions {
                 span,
-                E0787,
-                "asm options unsupported in naked functions: {}",
-                unsupported_options.join(", ")
-            )
-            .emit();
+                unsupported_options: unsupported_options.join(", "),
+            });
         }
 
         if !asm.options.contains(InlineAsmOptions::NORETURN) {
@@ -290,20 +269,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
                 .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1)
                 .shrink_to_hi();
 
-            struct_span_err!(
-                self.tcx.sess,
-                span,
-                E0787,
-                "asm in naked functions must use `noreturn` option"
-            )
-            .span_suggestion(
-                last_span,
-                "consider specifying that the asm block is responsible \
-                for returning from the function",
-                ", options(noreturn)",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+            self.tcx.sess.emit_err(NakedFunctionsMustUseNoreturn { span, last_span });
         }
     }
 }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 34fa80228df..cfd6acd8d7c 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -1,13 +1,18 @@
 //! A pass that annotates every item and method with its stability level,
 //! propagating default levels lexically from parent to children ast nodes.
 
-use crate::errors;
+use crate::errors::{
+    self, CannotStabilizeDeprecated, DeprecatedAttribute, DuplicateFeatureErr,
+    FeatureOnlyOnNightly, ImpliedFeatureNotExist, InvalidDeprecationVersion, InvalidStability,
+    MissingConstErr, MissingConstStabAttr, MissingStabilityAttr, TraitImplConstStable,
+    UnknownFeature, UselessStability,
+};
 use rustc_attr::{
     self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable,
     UnstableReason, VERSION_PLACEHOLDER,
 };
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -20,7 +25,6 @@ use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
 use rustc_middle::ty::{query::Providers, TyCtxt};
 use rustc_session::lint;
 use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
-use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
@@ -179,7 +183,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 if !self.in_trait_impl
                     || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id()))
                 {
-                    missing_const_err(&self.tcx.sess, fn_sig.span, const_span);
+                    self.tcx
+                        .sess
+                        .emit_err(MissingConstErr { fn_sig_span: fn_sig.span, const_span });
                 }
             }
         }
@@ -197,14 +203,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
 
         if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr {
             if stab.is_none() {
-                struct_span_err!(
-                    self.tcx.sess,
-                    *span,
-                    E0549,
-                    "deprecated attribute must be paired with \
-                    either stable or unstable attribute"
-                )
-                .emit();
+                self.tcx.sess.emit_err(DeprecatedAttribute { span: *span });
             }
         }
 
@@ -220,10 +219,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             if kind == AnnotationKind::Prohibited
                 || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
             {
-                self.tcx.sess.struct_span_err(span,"this stability annotation is useless")
-                    .span_label(span, "useless stability annotation")
-                    .span_label(item_sp, "the stability attribute annotates this item")
-                    .emit();
+                self.tcx.sess.emit_err(UselessStability { span, item_sp });
             }
 
             debug!("annotate: found {:?}", stab);
@@ -239,19 +235,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 {
                     match stab_v.parse::<u64>() {
                         Err(_) => {
-                            self.tcx.sess.struct_span_err(span, "invalid stability version found")
-                                .span_label(span, "invalid stability version")
-                                .span_label(item_sp, "the stability attribute annotates this item")
-                                .emit();
+                            self.tcx.sess.emit_err(InvalidStability { span, item_sp });
                             break;
                         }
                         Ok(stab_vp) => match dep_v.parse::<u64>() {
                             Ok(dep_vp) => match dep_vp.cmp(&stab_vp) {
                                 Ordering::Less => {
-                                    self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated")
-                                        .span_label(span, "invalid version")
-                                        .span_label(item_sp, "the stability attribute annotates this item")
-                                        .emit();
+                                    self.tcx
+                                        .sess
+                                        .emit_err(CannotStabilizeDeprecated { span, item_sp });
                                     break;
                                 }
                                 Ordering::Equal => continue,
@@ -259,10 +251,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                             },
                             Err(_) => {
                                 if dep_v != "TBD" {
-                                    self.tcx.sess.struct_span_err(span, "invalid deprecation version found")
-                                        .span_label(span, "invalid deprecation version")
-                                        .span_label(item_sp, "the stability attribute annotates this item")
-                                        .emit();
+                                    self.tcx
+                                        .sess
+                                        .emit_err(InvalidDeprecationVersion { span, item_sp });
                                 }
                                 break;
                             }
@@ -271,7 +262,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 }
             }
 
-            if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab {
+            if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } =
+                stab
+            {
                 self.index.implications.insert(implied_by, feature);
             }
 
@@ -531,7 +524,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
         let stab = self.tcx.stability().local_stability(def_id);
         if !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(def_id) {
             let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
-            self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr));
+            self.tcx.sess.emit_err(MissingStabilityAttr { span, descr });
         }
     }
 
@@ -551,7 +544,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
 
         if is_const && is_stable && missing_const_stability_attribute && is_reachable {
             let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
-            self.tcx.sess.span_err(span, &format!("{descr} has missing const stability attribute"));
+            self.tcx.sess.emit_err(MissingConstStabAttr { span, descr });
         }
     }
 }
@@ -764,11 +757,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                         && *constness == hir::Constness::Const
                         && const_stab.map_or(false, |(stab, _)| stab.is_const_stable())
                     {
-                        self.tcx
-                            .sess
-                            .struct_span_err(item.span, "trait implementations cannot be const stable yet")
-                            .note("see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information")
-                            .emit();
+                        self.tcx.sess.emit_err(TraitImplConstStable { span: item.span });
                     }
                 }
 
@@ -929,7 +918,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
         }
         if !lang_features.insert(feature) {
             // Warn if the user enables a lang feature multiple times.
-            duplicate_feature_err(tcx.sess, span, feature);
+            tcx.sess.emit_err(DuplicateFeatureErr { span, feature });
         }
     }
 
@@ -937,18 +926,14 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     let mut remaining_lib_features = FxIndexMap::default();
     for (feature, span) in declared_lib_features {
         if !tcx.sess.opts.unstable_features.is_nightly_build() {
-            struct_span_err!(
-                tcx.sess,
-                *span,
-                E0554,
-                "`#![feature]` may not be used on the {} release channel",
-                env!("CFG_RELEASE_CHANNEL")
-            )
-            .emit();
+            tcx.sess.emit_err(FeatureOnlyOnNightly {
+                span: *span,
+                release_channel: env!("CFG_RELEASE_CHANNEL"),
+            });
         }
         if remaining_lib_features.contains_key(&feature) {
             // Warn if the user enables a lib feature multiple times.
-            duplicate_feature_err(tcx.sess, *span, *feature);
+            tcx.sess.emit_err(DuplicateFeatureErr { span: *span, feature: *feature });
         }
         remaining_lib_features.insert(feature, *span);
     }
@@ -1049,23 +1034,18 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     }
 
     for (feature, span) in remaining_lib_features {
-        struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
+        tcx.sess.emit_err(UnknownFeature { span, feature: *feature });
     }
 
     for (implied_by, feature) in remaining_implications {
         let local_defined_features = tcx.lib_features(());
-        let span = local_defined_features
+        let span = *local_defined_features
             .stable
             .get(&feature)
             .map(|(_, span)| span)
             .or_else(|| local_defined_features.unstable.get(&feature))
             .expect("feature that implied another does not exist");
-        tcx.sess
-            .struct_span_err(
-                *span,
-                format!("feature `{implied_by}` implying `{feature}` does not exist"),
-            )
-            .emit();
+        tcx.sess.emit_err(ImpliedFeatureNotExist { span, feature, implied_by });
     }
 
     // FIXME(#44232): the `used_features` table no longer exists, so we
@@ -1088,21 +1068,20 @@ fn unnecessary_partially_stable_feature_lint(
              by the feature `{implies}`"
         ),
         |lint| {
-            lint
-        .span_suggestion(
-            span,
-            &format!(
+            lint.span_suggestion(
+                span,
+                &format!(
                 "if you are using features which are still unstable, change to using `{implies}`"
             ),
-            implies,
-            Applicability::MaybeIncorrect,
-        )
-        .span_suggestion(
-            tcx.sess.source_map().span_extend_to_line(span),
-            "if you are using features which are now stable, remove this line",
-            "",
-            Applicability::MaybeIncorrect,
-        )
+                implies,
+                Applicability::MaybeIncorrect,
+            )
+            .span_suggestion(
+                tcx.sess.source_map().span_extend_to_line(span),
+                "if you are using features which are now stable, remove this line",
+                "",
+                Applicability::MaybeIncorrect,
+            )
         },
     );
 }
@@ -1120,20 +1099,3 @@ fn unnecessary_stable_feature_lint(
         lint
     });
 }
-
-fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {
-    struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature)
-        .emit();
-}
-
-fn missing_const_err(session: &Session, fn_sig_span: Span, const_span: Span) {
-    const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \
-         and `#[rustc_const_stable]` require \
-         the function or method to be `const`";
-
-    session
-        .struct_span_err(fn_sig_span, ERROR_MSG)
-        .span_help(fn_sig_span, "make the function or method const")
-        .span_label(const_span, "attribute specified here")
-        .emit();
-}
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index c48b4ecf87a..92024989a75 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -1,13 +1,17 @@
 //! Validity checking for weak lang items
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
 use rustc_hir::lang_items::{self, LangItem};
 use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS;
 use rustc_middle::middle::lang_items::required;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::CrateType;
 
+use crate::errors::{
+    AllocFuncRequired, MissingAllocErrorHandler, MissingLangItem, MissingPanicHandler,
+    UnknownExternLangItem,
+};
+
 /// Checks the crate for usage of weak lang items, returning a vector of all the
 /// language items required by this crate, but not defined yet.
 pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItems) {
@@ -31,14 +35,7 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem
                 }
             } else {
                 let span = tcx.def_span(id.def_id);
-                struct_span_err!(
-                    tcx.sess,
-                    span,
-                    E0264,
-                    "unknown external lang item: `{}`",
-                    lang_item
-                )
-                .emit();
+                tcx.sess.emit_err(UnknownExternLangItem { span, lang_item });
             }
         }
     }
@@ -71,20 +68,14 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
     for (name, &item) in WEAK_ITEMS_REFS.iter() {
         if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() {
             if item == LangItem::PanicImpl {
-                tcx.sess.err("`#[panic_handler]` function required, but not found");
+                tcx.sess.emit_err(MissingPanicHandler);
             } else if item == LangItem::Oom {
                 if !tcx.features().default_alloc_error_handler {
-                    tcx.sess.err("`#[alloc_error_handler]` function required, but not found");
-                    tcx.sess.note_without_error("use `#![feature(default_alloc_error_handler)]` for a default error handler");
+                    tcx.sess.emit_err(AllocFuncRequired);
+                    tcx.sess.emit_note(MissingAllocErrorHandler);
                 }
             } else {
-                tcx
-                    .sess
-                    .diagnostic()
-                    .struct_err(&format!("language item required, but not found: `{}`", name))
-                    .note(&format!("this can occur when a binary crate with `#![no_std]` is compiled for a target where `{}` is defined in the standard library", name))
-                    .help(&format!("you may be able to compile for a target that doesn't need `{}`, specify a target with `--target` or in `.cargo/config`", name))
-                    .emit();
+                tcx.sess.emit_err(MissingLangItem { name: *name });
             }
         }
     }
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 2c3d8d5283b..a199947ebed 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -12,7 +12,7 @@ use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
 use rustc_errors::{
     fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
-    EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, StashKey,
+    EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
@@ -354,6 +354,17 @@ impl ParseSess {
         self.create_warning(warning).emit()
     }
 
+    pub fn create_note<'a>(
+        &'a self,
+        note: impl IntoDiagnostic<'a, Noted>,
+    ) -> DiagnosticBuilder<'a, Noted> {
+        note.into_diagnostic(&self.span_diagnostic)
+    }
+
+    pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted {
+        self.create_note(note).emit()
+    }
+
     pub fn create_fatal<'a>(
         &'a self,
         fatal: impl IntoDiagnostic<'a, !>,
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 5926cdc9dad..beb22ab3eb9 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -28,7 +28,7 @@ use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
 use rustc_errors::{
     error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
-    ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan,
+    ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -489,6 +489,15 @@ impl Session {
     pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) {
         self.parse_sess.emit_warning(warning)
     }
+    pub fn create_note<'a>(
+        &'a self,
+        note: impl IntoDiagnostic<'a, Noted>,
+    ) -> DiagnosticBuilder<'a, Noted> {
+        self.parse_sess.create_note(note)
+    }
+    pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted {
+        self.parse_sess.emit_note(note)
+    }
     pub fn create_fatal<'a>(
         &'a self,
         fatal: impl IntoDiagnostic<'a, !>,
diff --git a/src/test/ui/associated-types/issue-85103.rs b/src/test/ui/associated-types/issue-85103.rs
index c5e13856178..9c6a419e9f7 100644
--- a/src/test/ui/associated-types/issue-85103.rs
+++ b/src/test/ui/associated-types/issue-85103.rs
@@ -4,6 +4,6 @@ use std::borrow::Cow;
 
 #[rustc_layout(debug)]
 type Edges<'a, E> = Cow<'a, [E]>;
-//~^ ERROR layout error: NormalizationFailure
+//~^ 6:1: 6:18: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized
 
 fn main() {}
diff --git a/src/test/ui/associated-types/issue-85103.stderr b/src/test/ui/associated-types/issue-85103.stderr
index bddd1dce8e6..17f7148074c 100644
--- a/src/test/ui/associated-types/issue-85103.stderr
+++ b/src/test/ui/associated-types/issue-85103.stderr
@@ -1,4 +1,4 @@
-error: layout error: NormalizationFailure(<[E] as std::borrow::ToOwned>::Owned, Type(<[E] as std::borrow::ToOwned>::Owned))
+error: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized
   --> $DIR/issue-85103.rs:6:1
    |
 LL | type Edges<'a, E> = Cow<'a, [E]>;