about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-26 02:04:23 +0000
committerbors <bors@rust-lang.org>2023-02-26 02:04:23 +0000
commit70fd012439d75fd6ce561a6518b9b8fd399f455f (patch)
tree3460e49200b4697ae0acc1a036f3ba3762e47c2d
parentf37f9f6512cb6b295acb70938302704a80c29b2b (diff)
parent767c865f0751e65a6cbb231449c24116f82b4d4e (diff)
Auto merge of #108473 - matthiaskrgr:rollup-qjyae58, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - #107062 (Do some cleanup of doc/index.md)
 - #107890 (Lint against `Iterator::map` receiving a callable that returns `()`)
 - #108431 (Add regression test for #107918)
 - #108432 (test: drop unused deps)
 - #108436 (make "proc macro panicked" translatable)
 - #108444 (docs/test: add UI test and docs for `E0476`)
 - #108449 (Do not lint ineffective unstable trait impl for unresolved trait)
 - #108456 (Complete migrating `ast_passes` to derive diagnostics)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock2
-rw-r--r--compiler/rustc_ast_passes/locales/en-US.ftl147
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs558
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs476
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs54
-rw-r--r--compiler/rustc_ast_passes/src/lib.rs2
-rw-r--r--compiler/rustc_ast_passes/src/show_span.rs8
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0476.md21
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs1
-rw-r--r--compiler/rustc_expand/locales/en-US.ftl4
-rw-r--r--compiler/rustc_expand/src/errors.rs15
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs12
-rw-r--r--compiler/rustc_lint/locales/en-US.ftl7
-rw-r--r--compiler/rustc_lint/src/lib.rs6
-rw-r--r--compiler/rustc_lint/src/lints.rs16
-rw-r--r--compiler/rustc_lint/src/map_unit_fn.rs120
-rw-r--r--compiler/rustc_passes/src/stability.rs5
-rw-r--r--library/core/src/iter/mod.rs1
-rw-r--r--library/core/src/iter/traits/iterator.rs1
-rw-r--r--library/test/Cargo.toml2
-rw-r--r--src/doc/index.md228
-rw-r--r--src/tools/error_index_generator/main.rs2
-rw-r--r--src/tools/tidy/src/error_codes.rs2
-rw-r--r--tests/rustdoc-ui/auxiliary/panic-handler.rs9
-rw-r--r--tests/rustdoc-ui/issue-107918.rs12
-rw-r--r--tests/ui/auto-traits/auto-trait-validation.stderr6
-rw-r--r--tests/ui/auto-traits/issue-23080-2.stderr2
-rw-r--r--tests/ui/auto-traits/issue-23080.stderr2
-rw-r--r--tests/ui/auto-traits/issue-84075.stderr2
-rw-r--r--tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr4
-rw-r--r--tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr2
-rw-r--r--tests/ui/error-codes/E0476.rs13
-rw-r--r--tests/ui/error-codes/E0476.stderr31
-rw-r--r--tests/ui/lint/issue-106991.rs13
-rw-r--r--tests/ui/lint/issue-106991.stderr11
-rw-r--r--tests/ui/lint/lint_map_unit_fn.rs20
-rw-r--r--tests/ui/lint/lint_map_unit_fn.stderr66
-rw-r--r--tests/ui/methods/issues/issue-105732.stderr2
-rw-r--r--tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr2
-rw-r--r--tests/ui/stability-attribute/unresolved_stability_lint.rs8
-rw-r--r--tests/ui/stability-attribute/unresolved_stability_lint.stderr9
-rw-r--r--tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr2
43 files changed, 1394 insertions, 514 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d86b8018b79..a226a10f89d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5497,10 +5497,8 @@ dependencies = [
 name = "test"
 version = "0.0.0"
 dependencies = [
- "cfg-if",
  "core",
  "getopts",
- "libc",
  "panic_abort",
  "panic_unwind",
  "proc_macro",
diff --git a/compiler/rustc_ast_passes/locales/en-US.ftl b/compiler/rustc_ast_passes/locales/en-US.ftl
index 128e7255c61..747bd52b22c 100644
--- a/compiler/rustc_ast_passes/locales/en-US.ftl
+++ b/compiler/rustc_ast_passes/locales/en-US.ftl
@@ -87,3 +87,150 @@ ast_passes_fn_without_body =
     .suggestion = provide a definition for the function
 
 ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
+
+ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect
+
+ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$descr}
+    .suggestion = remove the {$remove_descr}
+    .label = `extern` block begins here
+
+ast_passes_extern_keyword_link = for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
+
+ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
+    .cannot_have = cannot have a body
+    .invalid = the invalid body
+    .existing = `extern` blocks define existing foreign {$kind}s and {$kind}s inside of them cannot have a body
+
+ast_passes_fn_body_extern = incorrect function inside `extern` block
+    .cannot_have = cannot have a body
+    .suggestion = remove the invalid body
+    .help = you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
+    .label = `extern` blocks define existing foreign functions and functions inside of them cannot have a body
+
+ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have qualifiers
+    .label = in this `extern` block
+    .suggestion = remove the qualifiers
+
+ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
+    .label = in this `extern` block
+    .note = this limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
+
+ast_passes_bad_c_variadic = only foreign or `unsafe extern "C"` functions may be C-variadic
+
+ast_passes_item_underscore = `{$kind}` items in this context need a name
+    .label = `_` is not a valid name for this `{$kind}` item
+
+ast_passes_nomangle_ascii = `#[no_mangle]` requires ASCII identifier
+
+ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
+    .help = consider using the `#[path]` attribute to specify filesystem path
+
+ast_passes_auto_generic = auto traits cannot have generic parameters
+    .label = auto trait cannot have generic parameters
+    .suggestion = remove the parameters
+
+ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetime bounds
+    .label = {ast_passes_auto_super_lifetime}
+    .suggestion = remove the super traits or lifetime bounds
+
+ast_passes_auto_items = auto traits cannot have associated items
+    .label = {ast_passes_auto_items}
+    .suggestion = remove these associated items
+
+ast_passes_generic_before_constraints = generic arguments must come before the first constraint
+    .constraints = {$constraint_len ->
+    [one] constraint
+    *[other] constraints
+    }
+    .args = generic {$args_len ->
+    [one] argument
+    *[other] arguments
+    }
+    .empty_string = {""},
+    .suggestion = move the {$constraint_len ->
+    [one] constraint
+    *[other] constraints
+    } after the generic {$args_len ->
+    [one] argument
+    *[other] arguments
+    }
+
+ast_passes_pattern_in_fn_pointer = patterns aren't allowed in function pointer types
+
+ast_passes_trait_object_single_bound = only a single explicit lifetime bound is permitted
+
+ast_passes_impl_trait_path = `impl Trait` is not allowed in path parameters
+
+ast_passes_nested_impl_trait = nested `impl Trait` is not allowed
+    .outer = outer `impl Trait`
+    .inner = nested `impl Trait` here
+
+ast_passes_at_least_one_trait = at least one trait must be specified
+
+ast_passes_extern_without_abi = extern declarations without an explicit ABI are deprecated
+
+ast_passes_out_of_order_params = {$param_ord} parameters must be declared prior to {$max_param} parameters
+    .suggestion = reorder the parameters: lifetimes, then consts and types
+
+ast_passes_obsolete_auto = `impl Trait for .. {"{}"}` is an obsolete syntax
+    .help = use `auto trait Trait {"{}"}` instead
+
+ast_passes_unsafe_negative_impl = negative impls cannot be unsafe
+    .negative = negative because of this
+    .unsafe = unsafe because of this
+
+ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
+    .because = {$annotation} because of this
+    .type = inherent impl for this type
+    .only_trait = only trait implementations may be annotated with {$annotation}
+
+ast_passes_unsafe_item = {$kind} cannot be declared unsafe
+
+ast_passes_fieldless_union = unions cannot have zero fields
+
+ast_passes_where_after_type_alias = where clauses are not allowed after the type for type aliases
+    .note = see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information
+
+ast_passes_generic_default_trailing = generic parameters with a default must be trailing
+
+ast_passes_nested_lifetimes = nested quantification of lifetimes
+
+ast_passes_optional_trait_supertrait = `?Trait` is not permitted in supertraits
+    .note = traits are `?{$path_str}` by default
+
+ast_passes_optional_trait_object = `?Trait` is not permitted in trait object types
+
+ast_passes_tilde_const_disallowed = `~const` is not allowed here
+    .trait = trait objects cannot have `~const` trait bounds
+    .closure = closures cannot have `~const` trait bounds
+    .function = this function is not `const`, so it cannot have `~const` trait bounds
+
+ast_passes_optional_const_exclusive = `~const` and `?` are mutually exclusive
+
+ast_passes_const_and_async = functions cannot be both `const` and `async`
+    .const = `const` because of this
+    .async = `async` because of this
+    .label = {""}
+
+ast_passes_pattern_in_foreign = patterns aren't allowed in foreign function declarations
+    .label = pattern not allowed in foreign function
+
+ast_passes_pattern_in_bodiless = patterns aren't allowed in functions without bodies
+    .label = pattern not allowed in function without body
+
+ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses
+    .label = not supported
+    .suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax
+    .suggestion_path = if `{$trait_segment}::{$potential_assoc}` is an associated type you're trying to set, use the associated type binding syntax
+    .note = see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
+
+ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
+
+ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
+    .suggestion = remove the attribute
+    .stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable
+
+ast_passes_incompatbile_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
+    .help = remove one of these features
+
+ast_passes_show_span = {$msg}
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index ee861e87355..1c561375626 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -13,7 +13,6 @@ use rustc_ast::walk_list;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::{self, State};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{error_code, pluralize, struct_span_err, Applicability};
 use rustc_macros::Subdiagnostic;
 use rustc_parse::validate_attr;
 use rustc_session::lint::builtin::{
@@ -29,12 +28,9 @@ use std::mem;
 use std::ops::{Deref, DerefMut};
 use thin_vec::thin_vec;
 
-use crate::errors::*;
+use crate::errors;
 use crate::fluent_generated as fluent;
 
-const MORE_EXTERN: &str =
-    "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
-
 /// Is `self` allowed semantically as the first parameter in an `FnDecl`?
 enum SelfSemantic {
     Yes,
@@ -134,9 +130,9 @@ impl<'a> AstValidator<'a> {
     fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) {
         let sess = &self.session;
         if sess.opts.unstable_features.is_nightly_build() {
-            sess.emit_err(ForbiddenLet { span: expr.span, reason: forbidden_let_reason });
+            sess.emit_err(errors::ForbiddenLet { span: expr.span, reason: forbidden_let_reason });
         } else {
-            sess.emit_err(ForbiddenLetStable { span: expr.span });
+            sess.emit_err(errors::ForbiddenLetStable { span: expr.span });
         }
     }
 
@@ -234,22 +230,22 @@ impl<'a> AstValidator<'a> {
     fn check_lifetime(&self, ident: Ident) {
         let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty];
         if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
-            self.session.emit_err(KeywordLifetime { span: ident.span });
+            self.session.emit_err(errors::KeywordLifetime { span: ident.span });
         }
     }
 
     fn check_label(&self, ident: Ident) {
         if ident.without_first_quote().is_reserved() {
-            self.session.emit_err(InvalidLabel { span: ident.span, name: ident.name });
+            self.session.emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
         }
     }
 
-    fn invalid_visibility(&self, vis: &Visibility, note: Option<InvalidVisibilityNote>) {
+    fn invalid_visibility(&self, vis: &Visibility, note: Option<errors::InvalidVisibilityNote>) {
         if let VisibilityKind::Inherited = vis.kind {
             return;
         }
 
-        self.session.emit_err(InvalidVisibility {
+        self.session.emit_err(errors::InvalidVisibility {
             span: vis.span,
             implied: vis.kind.is_pub().then_some(vis.span),
             note,
@@ -270,7 +266,7 @@ impl<'a> AstValidator<'a> {
 
     fn check_trait_fn_not_const(&self, constness: Const) {
         if let Const::Yes(span) = constness {
-            self.session.emit_err(TraitFnConst { span });
+            self.session.emit_err(errors::TraitFnConst { span });
         }
     }
 
@@ -287,7 +283,7 @@ impl<'a> AstValidator<'a> {
         let max_num_args: usize = u16::MAX.into();
         if fn_decl.inputs.len() > max_num_args {
             let Param { span, .. } = fn_decl.inputs[0];
-            self.session.emit_fatal(FnParamTooMany { span, max_num_args });
+            self.session.emit_fatal(errors::FnParamTooMany { span, max_num_args });
         }
     }
 
@@ -295,13 +291,13 @@ impl<'a> AstValidator<'a> {
         match &*fn_decl.inputs {
             [Param { ty, span, .. }] => {
                 if let TyKind::CVarArgs = ty.kind {
-                    self.session.emit_err(FnParamCVarArgsOnly { span: *span });
+                    self.session.emit_err(errors::FnParamCVarArgsOnly { span: *span });
                 }
             }
             [ps @ .., _] => {
                 for Param { ty, span, .. } in ps {
                     if let TyKind::CVarArgs = ty.kind {
-                        self.session.emit_err(FnParamCVarArgsNotLast { span: *span });
+                        self.session.emit_err(errors::FnParamCVarArgsNotLast { span: *span });
                     }
                 }
             }
@@ -328,9 +324,9 @@ impl<'a> AstValidator<'a> {
             })
             .for_each(|attr| {
                 if attr.is_doc_comment() {
-                    self.session.emit_err(FnParamDocComment { span: attr.span });
+                    self.session.emit_err(errors::FnParamDocComment { span: attr.span });
                 } else {
-                    self.session.emit_err(FnParamForbiddenAttr { span: attr.span });
+                    self.session.emit_err(errors::FnParamForbiddenAttr { span: attr.span });
                 }
             });
     }
@@ -338,7 +334,7 @@ impl<'a> AstValidator<'a> {
     fn check_decl_self_param(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
         if let (SelfSemantic::No, [param, ..]) = (self_semantic, &*fn_decl.inputs) {
             if param.is_self() {
-                self.session.emit_err(FnParamForbiddenSelf { span: param.span });
+                self.session.emit_err(errors::FnParamForbiddenSelf { span: param.span });
             }
         }
     }
@@ -346,7 +342,7 @@ impl<'a> AstValidator<'a> {
     fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
         if let Defaultness::Default(def_span) = defaultness {
             let span = self.session.source_map().guess_head_span(span);
-            self.session.emit_err(ForbiddenDefault { span, def_span });
+            self.session.emit_err(errors::ForbiddenDefault { span, def_span });
         }
     }
 
@@ -369,27 +365,17 @@ impl<'a> AstValidator<'a> {
             [b0] => b0.span(),
             [b0, .., bl] => b0.span().to(bl.span()),
         };
-        self.err_handler()
-            .struct_span_err(span, &format!("bounds on `type`s in {} have no effect", ctx))
-            .emit();
+        self.err_handler().emit_err(errors::BoundInContext { span, ctx });
     }
 
     fn check_foreign_ty_genericless(&self, generics: &Generics, where_span: Span) {
         let cannot_have = |span, descr, remove_descr| {
-            self.err_handler()
-                .struct_span_err(
-                    span,
-                    &format!("`type`s inside `extern` blocks cannot have {}", descr),
-                )
-                .span_suggestion(
-                    span,
-                    &format!("remove the {}", remove_descr),
-                    "",
-                    Applicability::MaybeIncorrect,
-                )
-                .span_label(self.current_extern_span(), "`extern` block begins here")
-                .note(MORE_EXTERN)
-                .emit();
+            self.err_handler().emit_err(errors::ExternTypesCannotHave {
+                span,
+                descr,
+                remove_descr,
+                block_span: self.current_extern_span(),
+            });
         };
 
         if !generics.params.is_empty() {
@@ -405,20 +391,12 @@ impl<'a> AstValidator<'a> {
         let Some(body) = body else {
             return;
         };
-        self.err_handler()
-            .struct_span_err(ident.span, &format!("incorrect `{}` inside `extern` block", kind))
-            .span_label(ident.span, "cannot have a body")
-            .span_label(body, "the invalid body")
-            .span_label(
-                self.current_extern_span(),
-                format!(
-                    "`extern` blocks define existing foreign {0}s and {0}s \
-                    inside of them cannot have a body",
-                    kind
-                ),
-            )
-            .note(MORE_EXTERN)
-            .emit();
+        self.err_handler().emit_err(errors::BodyInExtern {
+            span: ident.span,
+            body,
+            block: self.current_extern_span(),
+            kind,
+        });
     }
 
     /// An `fn` in `extern { ... }` cannot have a body `{ ... }`.
@@ -426,26 +404,11 @@ impl<'a> AstValidator<'a> {
         let Some(body) = body else {
             return;
         };
-        self.err_handler()
-            .struct_span_err(ident.span, "incorrect function inside `extern` block")
-            .span_label(ident.span, "cannot have a body")
-            .span_suggestion(
-                body.span,
-                "remove the invalid body",
-                ";",
-                Applicability::MaybeIncorrect,
-            )
-            .help(
-                "you might have meant to write a function accessible through FFI, \
-                which can be done by writing `extern fn` outside of the `extern` block",
-            )
-            .span_label(
-                self.current_extern_span(),
-                "`extern` blocks define existing foreign functions and functions \
-                inside of them cannot have a body",
-            )
-            .note(MORE_EXTERN)
-            .emit();
+        self.err_handler().emit_err(errors::FnBodyInExtern {
+            span: ident.span,
+            body: body.span,
+            block: self.current_extern_span(),
+        });
     }
 
     fn current_extern_span(&self) -> Span {
@@ -455,34 +418,21 @@ impl<'a> AstValidator<'a> {
     /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
     fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) {
         if header.has_qualifiers() {
-            self.err_handler()
-                .struct_span_err(ident.span, "functions in `extern` blocks cannot have qualifiers")
-                .span_label(self.current_extern_span(), "in this `extern` block")
-                .span_suggestion_verbose(
-                    span.until(ident.span.shrink_to_lo()),
-                    "remove the qualifiers",
-                    "fn ",
-                    Applicability::MaybeIncorrect,
-                )
-                .emit();
+            self.err_handler().emit_err(errors::FnQualifierInExtern {
+                span: ident.span,
+                block: self.current_extern_span(),
+                sugg_span: span.until(ident.span.shrink_to_lo()),
+            });
         }
     }
 
     /// An item in `extern { ... }` cannot use non-ascii identifier.
     fn check_foreign_item_ascii_only(&self, ident: Ident) {
         if !ident.as_str().is_ascii() {
-            let n = 83942;
-            self.err_handler()
-                .struct_span_err(
-                    ident.span,
-                    "items in `extern` blocks cannot use non-ascii identifiers",
-                )
-                .span_label(self.current_extern_span(), "in this `extern` block")
-                .note(&format!(
-                    "this limitation may be lifted in the future; see issue #{} <https://github.com/rust-lang/rust/issues/{}> for more information",
-                    n, n,
-                ))
-                .emit();
+            self.err_handler().emit_err(errors::ExternItemAscii {
+                span: ident.span,
+                block: self.current_extern_span(),
+            });
         }
     }
 
@@ -505,12 +455,7 @@ impl<'a> AstValidator<'a> {
 
         for Param { ty, span, .. } in &fk.decl().inputs {
             if let TyKind::CVarArgs = ty.kind {
-                self.err_handler()
-                    .struct_span_err(
-                        *span,
-                        "only foreign or `unsafe extern \"C\"` functions may be C-variadic",
-                    )
-                    .emit();
+                self.err_handler().emit_err(errors::BadCVariadic { span: *span });
             }
         }
     }
@@ -519,75 +464,32 @@ impl<'a> AstValidator<'a> {
         if ident.name != kw::Underscore {
             return;
         }
-        self.err_handler()
-            .struct_span_err(ident.span, &format!("`{}` items in this context need a name", kind))
-            .span_label(ident.span, format!("`_` is not a valid name for this `{}` item", kind))
-            .emit();
+        self.err_handler().emit_err(errors::ItemUnderscore { span: ident.span, kind });
     }
 
     fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) {
         if ident.name.as_str().is_ascii() {
             return;
         }
-        let head_span = self.session.source_map().guess_head_span(item_span);
-        struct_span_err!(
-            self.session,
-            head_span,
-            E0754,
-            "`#[no_mangle]` requires ASCII identifier"
-        )
-        .emit();
+        let span = self.session.source_map().guess_head_span(item_span);
+        self.session.emit_err(errors::NoMangleAscii { span });
     }
 
     fn check_mod_file_item_asciionly(&self, ident: Ident) {
         if ident.name.as_str().is_ascii() {
             return;
         }
-        struct_span_err!(
-            self.session,
-            ident.span,
-            E0754,
-            "trying to load file for module `{}` with non-ascii identifier name",
-            ident.name
-        )
-        .help("consider using `#[path]` attribute to specify filesystem path")
-        .emit();
+        self.session.emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name });
     }
 
-    fn deny_generic_params(&self, generics: &Generics, ident_span: Span) {
+    fn deny_generic_params(&self, generics: &Generics, ident: Span) {
         if !generics.params.is_empty() {
-            struct_span_err!(
-                self.session,
-                generics.span,
-                E0567,
-                "auto traits cannot have generic parameters"
-            )
-            .span_label(ident_span, "auto trait cannot have generic parameters")
-            .span_suggestion(
-                generics.span,
-                "remove the parameters",
-                "",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+            self.session.emit_err(errors::AutoTraitGeneric { span: generics.span, ident });
         }
     }
 
-    fn emit_e0568(&self, span: Span, ident_span: Span) {
-        struct_span_err!(
-            self.session,
-            span,
-            E0568,
-            "auto traits cannot have super traits or lifetime bounds"
-        )
-        .span_label(ident_span, "auto trait cannot have super traits or lifetime bounds")
-        .span_suggestion(
-            span,
-            "remove the super traits or lifetime bounds",
-            "",
-            Applicability::MachineApplicable,
-        )
-        .emit();
+    fn emit_e0568(&self, span: Span, ident: Span) {
+        self.session.emit_err(errors::AutoTraitBounds { span, ident });
     }
 
     fn deny_super_traits(&self, bounds: &GenericBounds, ident_span: Span) {
@@ -603,24 +505,11 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn deny_items(&self, trait_items: &[P<AssocItem>], ident_span: Span) {
+    fn deny_items(&self, trait_items: &[P<AssocItem>], ident: Span) {
         if !trait_items.is_empty() {
             let spans: Vec<_> = trait_items.iter().map(|i| i.ident.span).collect();
-            let total_span = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
-            struct_span_err!(
-                self.session,
-                spans,
-                E0380,
-                "auto traits cannot have associated items"
-            )
-            .span_suggestion(
-                total_span,
-                "remove these associated items",
-                "",
-                Applicability::MachineApplicable,
-            )
-            .span_label(ident_span, "auto trait cannot have associated items")
-            .emit();
+            let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
+            self.session.emit_err(errors::AutoTraitItems { spans, total, ident });
         }
     }
 
@@ -666,29 +555,17 @@ impl<'a> AstValidator<'a> {
         let args_len = arg_spans.len();
         let constraint_len = constraint_spans.len();
         // ...and then error:
-        self.err_handler()
-            .struct_span_err(
-                arg_spans.clone(),
-                "generic arguments must come before the first constraint",
-            )
-            .span_label(constraint_spans[0], &format!("constraint{}", pluralize!(constraint_len)))
-            .span_label(
-                *arg_spans.iter().last().unwrap(),
-                &format!("generic argument{}", pluralize!(args_len)),
-            )
-            .span_labels(constraint_spans, "")
-            .span_labels(arg_spans, "")
-            .span_suggestion_verbose(
-                data.span,
-                &format!(
-                    "move the constraint{} after the generic argument{}",
-                    pluralize!(constraint_len),
-                    pluralize!(args_len)
-                ),
-                self.correct_generic_order_suggestion(&data),
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        self.err_handler().emit_err(errors::ArgsBeforeConstraint {
+            arg_spans: arg_spans.clone(),
+            constraints: constraint_spans[0],
+            args: *arg_spans.iter().last().unwrap(),
+            data: data.span,
+            constraint_spans: errors::EmptyLabelManySpans(constraint_spans),
+            arg_spans2: errors::EmptyLabelManySpans(arg_spans),
+            suggestion: self.correct_generic_order_suggestion(&data),
+            constraint_len,
+            args_len,
+        });
     }
 
     fn visit_ty_common(&mut self, ty: &'a Ty) {
@@ -696,13 +573,7 @@ impl<'a> AstValidator<'a> {
             TyKind::BareFn(bfty) => {
                 self.check_fn_decl(&bfty.decl, SelfSemantic::No);
                 Self::check_decl_no_pat(&bfty.decl, |span, _, _| {
-                    struct_span_err!(
-                        self.session,
-                        span,
-                        E0561,
-                        "patterns aren't allowed in function pointer types"
-                    )
-                    .emit();
+                    self.session.emit_err(errors::PatternFnPointer { span });
                 });
                 if let Extern::Implicit(_) = bfty.ext {
                     let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo());
@@ -714,13 +585,8 @@ impl<'a> AstValidator<'a> {
                 for bound in bounds {
                     if let GenericBound::Outlives(lifetime) = bound {
                         if any_lifetime_bounds {
-                            struct_span_err!(
-                                self.session,
-                                lifetime.ident.span,
-                                E0226,
-                                "only a single explicit lifetime bound is permitted"
-                            )
-                            .emit();
+                            self.session
+                                .emit_err(errors::TraitObjectBound { span: lifetime.ident.span });
                             break;
                         }
                         any_lifetime_bounds = true;
@@ -729,29 +595,19 @@ impl<'a> AstValidator<'a> {
             }
             TyKind::ImplTrait(_, bounds) => {
                 if self.is_impl_trait_banned {
-                    struct_span_err!(
-                        self.session,
-                        ty.span,
-                        E0667,
-                        "`impl Trait` is not allowed in path parameters"
-                    )
-                    .emit();
+                    self.session.emit_err(errors::ImplTraitPath { span: ty.span });
                 }
 
                 if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
-                    struct_span_err!(
-                        self.session,
-                        ty.span,
-                        E0666,
-                        "nested `impl Trait` is not allowed"
-                    )
-                    .span_label(outer_impl_trait_sp, "outer `impl Trait`")
-                    .span_label(ty.span, "nested `impl Trait` here")
-                    .emit();
+                    self.session.emit_err(errors::NestedImplTrait {
+                        span: ty.span,
+                        outer: outer_impl_trait_sp,
+                        inner: ty.span,
+                    });
                 }
 
                 if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) {
-                    self.err_handler().span_err(ty.span, "at least one trait must be specified");
+                    self.err_handler().emit_err(errors::AtLeastOneTrait { span: ty.span });
                 }
             }
             _ => {}
@@ -772,7 +628,7 @@ impl<'a> AstValidator<'a> {
                 MISSING_ABI,
                 id,
                 span,
-                "extern declarations without an explicit ABI are deprecated",
+                fluent::ast_passes_extern_without_abi,
                 BuiltinLintDiagnostics::MissingAbi(span, abi::Abi::FALLBACK),
             )
         }
@@ -845,20 +701,13 @@ fn validate_generic_param_order(
         ordered_params += ">";
 
         for (param_ord, (max_param, spans)) in &out_of_order {
-            let mut err = handler.struct_span_err(
-                spans.clone(),
-                &format!(
-                    "{} parameters must be declared prior to {} parameters",
-                    param_ord, max_param,
-                ),
-            );
-            err.span_suggestion(
-                span,
-                "reorder the parameters: lifetimes, then consts and types",
-                &ordered_params,
-                Applicability::MachineApplicable,
-            );
-            err.emit();
+            handler.emit_err(errors::OutOfOrderParams {
+                spans: spans.clone(),
+                sugg_span: span,
+                param_ord,
+                max_param,
+                ordered_params: &ordered_params,
+            });
         }
     }
 }
@@ -972,25 +821,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.with_in_trait_impl(true, Some(*constness), |this| {
                     this.invalid_visibility(&item.vis, None);
                     if let TyKind::Err = self_ty.kind {
-                        this.err_handler()
-                            .struct_span_err(
-                                item.span,
-                                "`impl Trait for .. {}` is an obsolete syntax",
-                            )
-                            .help("use `auto trait Trait {}` instead")
-                            .emit();
+                        this.err_handler().emit_err(errors::ObsoleteAuto { span: item.span });
                     }
                     if let (&Unsafe::Yes(span), &ImplPolarity::Negative(sp)) = (unsafety, polarity)
                     {
-                        struct_span_err!(
-                            this.session,
-                            sp.to(t.path.span),
-                            E0198,
-                            "negative impls cannot be unsafe"
-                        )
-                        .span_label(sp, "negative because of this")
-                        .span_label(span, "unsafe because of this")
-                        .emit();
+                        this.session.emit_err(errors::UnsafeNegativeImpl {
+                            span: sp.to(t.path.span),
+                            negative: sp,
+                            r#unsafe: span,
+                        });
                     }
 
                     this.visit_vis(&item.vis);
@@ -1018,52 +857,54 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self_ty,
                 items: _,
             }) => {
-                let error = |annotation_span, annotation| {
-                    let mut err = self.err_handler().struct_span_err(
-                        self_ty.span,
-                        &format!("inherent impls cannot be {}", annotation),
-                    );
-                    err.span_label(annotation_span, &format!("{} because of this", annotation));
-                    err.span_label(self_ty.span, "inherent impl for this type");
-                    err
-                };
+                let error =
+                    |annotation_span, annotation, only_trait: bool| errors::InherentImplCannot {
+                        span: self_ty.span,
+                        annotation_span,
+                        annotation,
+                        self_ty: self_ty.span,
+                        only_trait: only_trait.then_some(()),
+                    };
 
                 self.invalid_visibility(
                     &item.vis,
-                    Some(InvalidVisibilityNote::IndividualImplItems),
+                    Some(errors::InvalidVisibilityNote::IndividualImplItems),
                 );
                 if let &Unsafe::Yes(span) = unsafety {
-                    error(span, "unsafe").code(error_code!(E0197)).emit();
+                    self.err_handler().emit_err(errors::InherentImplCannotUnsafe {
+                        span: self_ty.span,
+                        annotation_span: span,
+                        annotation: "unsafe",
+                        self_ty: self_ty.span,
+                    });
                 }
                 if let &ImplPolarity::Negative(span) = polarity {
-                    error(span, "negative").emit();
+                    self.err_handler().emit_err(error(span, "negative", false));
                 }
                 if let &Defaultness::Default(def_span) = defaultness {
-                    error(def_span, "`default`")
-                        .note("only trait implementations may be annotated with `default`")
-                        .emit();
+                    self.err_handler().emit_err(error(def_span, "`default`", true));
                 }
                 if let &Const::Yes(span) = constness {
-                    error(span, "`const`")
-                        .note("only trait implementations may be annotated with `const`")
-                        .emit();
+                    self.err_handler().emit_err(error(span, "`const`", true));
                 }
             }
             ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => {
                 self.check_defaultness(item.span, *defaultness);
 
                 if body.is_none() {
-                    self.session.emit_err(FnWithoutBody {
+                    self.session.emit_err(errors::FnWithoutBody {
                         span: item.span,
                         replace_span: self.ending_semi_or_hi(item.span),
                         extern_block_suggestion: match sig.header.ext {
                             Extern::None => None,
-                            Extern::Implicit(start_span) => Some(ExternBlockSuggestion::Implicit {
-                                start_span,
-                                end_span: item.span.shrink_to_hi(),
-                            }),
+                            Extern::Implicit(start_span) => {
+                                Some(errors::ExternBlockSuggestion::Implicit {
+                                    start_span,
+                                    end_span: item.span.shrink_to_hi(),
+                                })
+                            }
                             Extern::Explicit(abi, start_span) => {
-                                Some(ExternBlockSuggestion::Explicit {
+                                Some(errors::ExternBlockSuggestion::Explicit {
                                     start_span,
                                     end_span: item.span.shrink_to_hi(),
                                     abi: abi.symbol_unescaped,
@@ -1085,10 +926,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 let old_item = mem::replace(&mut self.extern_mod, Some(item));
                 self.invalid_visibility(
                     &item.vis,
-                    Some(InvalidVisibilityNote::IndividualForeignItems),
+                    Some(errors::InvalidVisibilityNote::IndividualForeignItems),
                 );
                 if let &Unsafe::Yes(span) = unsafety {
-                    self.err_handler().span_err(span, "extern block cannot be declared unsafe");
+                    self.err_handler().emit_err(errors::UnsafeItem { span, kind: "extern block" });
                 }
                 if abi.is_none() {
                     self.maybe_lint_missing_abi(item.span, item.id);
@@ -1128,7 +969,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
             ItemKind::Mod(unsafety, mod_kind) => {
                 if let &Unsafe::Yes(span) = unsafety {
-                    self.err_handler().span_err(span, "module cannot be declared unsafe");
+                    self.err_handler().emit_err(errors::UnsafeItem { span, kind: "module" });
                 }
                 // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
                 if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
@@ -1139,18 +980,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
             ItemKind::Union(vdata, ..) => {
                 if vdata.fields().is_empty() {
-                    self.err_handler().span_err(item.span, "unions cannot have zero fields");
+                    self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
                 }
             }
             ItemKind::Const(def, .., None) => {
                 self.check_defaultness(item.span, *def);
-                self.session.emit_err(ConstWithoutBody {
+                self.session.emit_err(errors::ConstWithoutBody {
                     span: item.span,
                     replace_span: self.ending_semi_or_hi(item.span),
                 });
             }
             ItemKind::Static(.., None) => {
-                self.session.emit_err(StaticWithoutBody {
+                self.session.emit_err(errors::StaticWithoutBody {
                     span: item.span,
                     replace_span: self.ending_semi_or_hi(item.span),
                 });
@@ -1158,21 +999,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             ItemKind::TyAlias(box TyAlias { defaultness, where_clauses, bounds, ty, .. }) => {
                 self.check_defaultness(item.span, *defaultness);
                 if ty.is_none() {
-                    self.session.emit_err(TyAliasWithoutBody {
+                    self.session.emit_err(errors::TyAliasWithoutBody {
                         span: item.span,
                         replace_span: self.ending_semi_or_hi(item.span),
                     });
                 }
                 self.check_type_no_bounds(bounds, "this context");
                 if where_clauses.1.0 {
-                    let mut err = self.err_handler().struct_span_err(
-                        where_clauses.1.1,
-                        "where clauses are not allowed after the type for type aliases",
-                    );
-                    err.note(
-                        "see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
-                    );
-                    err.emit();
+                    self.err_handler()
+                        .emit_err(errors::WhereAfterTypeAlias { span: where_clauses.1.1 });
                 }
             }
             _ => {}
@@ -1254,11 +1089,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
                 GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
                     if let Some(span) = prev_param_default {
-                        let mut err = self.err_handler().struct_span_err(
-                            span,
-                            "generic parameters with a default must be trailing",
-                        );
-                        err.emit();
+                        self.err_handler().emit_err(errors::GenericDefaultTrailing { span });
                         break;
                     }
                 }
@@ -1286,13 +1117,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                             match bound {
                                 GenericBound::Trait(t, _) => {
                                     if !t.bound_generic_params.is_empty() {
-                                        struct_span_err!(
-                                            self.err_handler(),
-                                            t.span,
-                                            E0316,
-                                            "nested quantification of lifetimes"
-                                        )
-                                        .emit();
+                                        self.err_handler()
+                                            .emit_err(errors::NestedLifetimes { span: t.span });
                                     }
                                 }
                                 GenericBound::Outlives(_) => {}
@@ -1317,32 +1143,27 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         if let GenericBound::Trait(poly, modify) = bound {
             match (ctxt, modify) {
                 (BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
-                    let mut err = self
-                        .err_handler()
-                        .struct_span_err(poly.span, "`?Trait` is not permitted in supertraits");
-                    let path_str = pprust::path_to_string(&poly.trait_ref.path);
-                    err.note(&format!("traits are `?{}` by default", path_str));
-                    err.emit();
+                    self.err_handler().emit_err(errors::OptionalTraitSupertrait {
+                        span: poly.span,
+                        path_str: pprust::path_to_string(&poly.trait_ref.path)
+                    });
                 }
                 (BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
-                    let mut err = self.err_handler().struct_span_err(
-                        poly.span,
-                        "`?Trait` is not permitted in trait object types",
-                    );
-                    err.emit();
+                    self.err_handler().emit_err(errors::OptionalTraitObject {span: poly.span});
                 }
                 (_, TraitBoundModifier::MaybeConst) if let Some(reason) = &self.disallow_tilde_const => {
-                    let mut err = self.err_handler().struct_span_err(bound.span(), "`~const` is not allowed here");
-                    match reason {
-                        DisallowTildeConstContext::TraitObject => err.note("trait objects cannot have `~const` trait bounds"),
-                        DisallowTildeConstContext::Fn(FnKind::Closure(..)) => err.note("closures cannot have `~const` trait bounds"),
-                        DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => err.span_note(ident.span, "this function is not `const`, so it cannot have `~const` trait bounds"),
+                    let reason = match reason {
+                        DisallowTildeConstContext::TraitObject => errors::TildeConstReason::TraitObject,
+                        DisallowTildeConstContext::Fn(FnKind::Closure(..)) => errors::TildeConstReason::Closure,
+                        DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => errors::TildeConstReason::Function { ident: ident.span },
                     };
-                    err.emit();
+                    self.err_handler().emit_err(errors::TildeConstDisallowed {
+                        span: bound.span(),
+                        reason
+                    });
                 }
                 (_, TraitBoundModifier::MaybeConstMaybe) => {
-                    self.err_handler()
-                        .span_err(bound.span(), "`~const` and `?` are mutually exclusive");
+                    self.err_handler().emit_err(errors::OptionalConstExclusive {span: bound.span()});
                 }
                 _ => {}
             }
@@ -1362,21 +1183,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         self.check_c_variadic_type(fk);
 
         // Functions cannot both be `const async`
-        if let Some(FnHeader {
+        if let Some(&FnHeader {
             constness: Const::Yes(cspan),
             asyncness: Async::Yes { span: aspan, .. },
             ..
         }) = fk.header()
         {
-            self.err_handler()
-                .struct_span_err(
-                    vec![*cspan, *aspan],
-                    "functions cannot be both `const` and `async`",
-                )
-                .span_label(*cspan, "`const` because of this")
-                .span_label(*aspan, "`async` because of this")
-                .span_label(span, "") // Point at the fn header.
-                .emit();
+            self.err_handler().emit_err(errors::ConstAndAsync {
+                spans: vec![cspan, aspan],
+                cspan,
+                aspan,
+                span,
+            });
         }
 
         if let FnKind::Fn(
@@ -1394,20 +1212,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         // Functions without bodies cannot have patterns.
         if let FnKind::Fn(ctxt, _, sig, _, _, None) = fk {
             Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| {
-                let (code, msg, label) = match ctxt {
-                    FnCtxt::Foreign => (
-                        error_code!(E0130),
-                        "patterns aren't allowed in foreign function declarations",
-                        "pattern not allowed in foreign function",
-                    ),
-                    _ => (
-                        error_code!(E0642),
-                        "patterns aren't allowed in functions without bodies",
-                        "pattern not allowed in function without body",
-                    ),
-                };
                 if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) {
                     if let Some(ident) = ident {
+                        let msg = match ctxt {
+                            FnCtxt::Foreign => fluent::ast_passes_pattern_in_foreign,
+                            _ => fluent::ast_passes_pattern_in_bodiless,
+                        };
                         let diag = BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident);
                         self.lint_buffer.buffer_lint_with_diagnostic(
                             PATTERNS_IN_FNS_WITHOUT_BODY,
@@ -1418,11 +1228,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         )
                     }
                 } else {
-                    self.err_handler()
-                        .struct_span_err(span, msg)
-                        .span_label(span, label)
-                        .code(code)
-                        .emit();
+                    match ctxt {
+                        FnCtxt::Foreign => {
+                            self.err_handler().emit_err(errors::PatternInForeign { span })
+                        }
+                        _ => self.err_handler().emit_err(errors::PatternInBodiless { span }),
+                    };
                 }
             });
         }
@@ -1449,7 +1260,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             match &item.kind {
                 AssocItemKind::Const(_, _, body) => {
                     if body.is_none() {
-                        self.session.emit_err(AssocConstWithoutBody {
+                        self.session.emit_err(errors::AssocConstWithoutBody {
                             span: item.span,
                             replace_span: self.ending_semi_or_hi(item.span),
                         });
@@ -1457,7 +1268,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
                 AssocItemKind::Fn(box Fn { body, .. }) => {
                     if body.is_none() {
-                        self.session.emit_err(AssocFnWithoutBody {
+                        self.session.emit_err(errors::AssocFnWithoutBody {
                             span: item.span,
                             replace_span: self.ending_semi_or_hi(item.span),
                         });
@@ -1472,7 +1283,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     ..
                 }) => {
                     if ty.is_none() {
-                        self.session.emit_err(AssocTypeWithoutBody {
+                        self.session.emit_err(errors::AssocTypeWithoutBody {
                             span: item.span,
                             replace_span: self.ending_semi_or_hi(item.span),
                         });
@@ -1544,11 +1355,7 @@ fn deny_equality_constraints(
     predicate: &WhereEqPredicate,
     generics: &Generics,
 ) {
-    let mut err = this.err_handler().struct_span_err(
-        predicate.span,
-        "equality constraints are not yet supported in `where` clauses",
-    );
-    err.span_label(predicate.span, "not supported");
+    let mut err = errors::EqualityInWhere { span: predicate.span, assoc: None, assoc2: None };
 
     // Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
     if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind {
@@ -1592,20 +1399,12 @@ fn deny_equality_constraints(
                                             .into();
                                         }
                                     }
-                                    err.span_suggestion_verbose(
-                                        predicate.span,
-                                        &format!(
-                                            "if `{}` is an associated type you're trying to set, \
-                                            use the associated type binding syntax",
-                                            ident
-                                        ),
-                                        format!(
-                                            "{}: {}",
-                                            param,
-                                            pprust::path_to_string(&assoc_path)
-                                        ),
-                                        Applicability::MaybeIncorrect,
-                                    );
+                                    err.assoc = Some(errors::AssociatedSuggestion {
+                                        span: predicate.span,
+                                        ident: *ident,
+                                        param: *param,
+                                        path: pprust::path_to_string(&assoc_path),
+                                    })
                                 }
                                 _ => {}
                             };
@@ -1647,15 +1446,13 @@ fn deny_equality_constraints(
                                         trait_segment.span().shrink_to_hi(),
                                     ),
                                 };
-                                err.multipart_suggestion(
-                                    &format!(
-                                        "if `{}::{}` is an associated type you're trying to set, \
-                                        use the associated type binding syntax",
-                                        trait_segment.ident, potential_assoc.ident,
-                                    ),
-                                    vec![(span, args), (predicate.span, String::new())],
-                                    Applicability::MaybeIncorrect,
-                                );
+                                err.assoc2 = Some(errors::AssociatedSuggestion2 {
+                                    span,
+                                    args,
+                                    predicate: predicate.span,
+                                    trait_segment: trait_segment.ident,
+                                    potential_assoc: potential_assoc.ident,
+                                });
                             }
                         }
                     }
@@ -1663,10 +1460,7 @@ fn deny_equality_constraints(
             }
         }
     }
-    err.note(
-        "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
-    );
-    err.emit();
+    this.err_handler().emit_err(err);
 }
 
 pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index f304f5a1956..d007097d918 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -1,9 +1,12 @@
 //! Errors emitted by ast_passes.
 
+use rustc_ast::ParamKindOrd;
+use rustc_errors::AddToDiagnostic;
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_span::{Span, Symbol};
+use rustc_span::{symbol::Ident, Span, Symbol};
 
 use crate::ast_validation::ForbiddenLetReason;
+use crate::fluent_generated as fluent;
 
 #[derive(Diagnostic)]
 #[diag(ast_passes_forbidden_let)]
@@ -217,3 +220,474 @@ pub enum ExternBlockSuggestion {
         abi: Symbol,
     },
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_bound_in_context)]
+pub struct BoundInContext<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub ctx: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_extern_types_cannot)]
+#[note(ast_passes_extern_keyword_link)]
+pub struct ExternTypesCannotHave<'a> {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "maybe-incorrect")]
+    pub span: Span,
+    pub descr: &'a str,
+    pub remove_descr: &'a str,
+    #[label]
+    pub block_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_body_in_extern)]
+#[note(ast_passes_extern_keyword_link)]
+pub struct BodyInExtern<'a> {
+    #[primary_span]
+    #[label(ast_passes_cannot_have)]
+    pub span: Span,
+    #[label(ast_passes_invalid)]
+    pub body: Span,
+    #[label(ast_passes_existing)]
+    pub block: Span,
+    pub kind: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_fn_body_extern)]
+#[help]
+#[note(ast_passes_extern_keyword_link)]
+pub struct FnBodyInExtern {
+    #[primary_span]
+    #[label(ast_passes_cannot_have)]
+    pub span: Span,
+    #[suggestion(code = ";", applicability = "maybe-incorrect")]
+    pub body: Span,
+    #[label]
+    pub block: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_extern_fn_qualifiers)]
+pub struct FnQualifierInExtern {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub block: Span,
+    #[suggestion(code = "fn ", applicability = "maybe-incorrect", style = "verbose")]
+    pub sugg_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_extern_item_ascii)]
+#[note]
+pub struct ExternItemAscii {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub block: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_bad_c_variadic)]
+pub struct BadCVariadic {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_item_underscore)]
+pub struct ItemUnderscore<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub kind: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_nomangle_ascii, code = "E0754")]
+pub struct NoMangleAscii {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_module_nonascii, code = "E0754")]
+#[help]
+pub struct ModuleNonAscii {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_auto_generic, code = "E0567")]
+pub struct AutoTraitGeneric {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub span: Span,
+    #[label]
+    pub ident: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_auto_super_lifetime, code = "E0568")]
+pub struct AutoTraitBounds {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub span: Span,
+    #[label]
+    pub ident: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_auto_items, code = "E0380")]
+pub struct AutoTraitItems {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub total: Span,
+    #[label]
+    pub ident: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_generic_before_constraints)]
+pub struct ArgsBeforeConstraint {
+    #[primary_span]
+    pub arg_spans: Vec<Span>,
+    #[label(ast_passes_constraints)]
+    pub constraints: Span,
+    #[label(ast_passes_args)]
+    pub args: Span,
+    #[suggestion(code = "{suggestion}", applicability = "machine-applicable", style = "verbose")]
+    pub data: Span,
+    pub suggestion: String,
+    pub constraint_len: usize,
+    pub args_len: usize,
+    #[subdiagnostic]
+    pub constraint_spans: EmptyLabelManySpans,
+    #[subdiagnostic]
+    pub arg_spans2: EmptyLabelManySpans,
+}
+
+pub struct EmptyLabelManySpans(pub Vec<Span>);
+
+// The derive for `Vec<Span>` does multiple calls to `span_label`, adding commas between each
+impl AddToDiagnostic for EmptyLabelManySpans {
+    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    where
+        F: Fn(
+            &mut rustc_errors::Diagnostic,
+            rustc_errors::SubdiagnosticMessage,
+        ) -> rustc_errors::SubdiagnosticMessage,
+    {
+        diag.span_labels(self.0, "");
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_pattern_in_fn_pointer, code = "E0561")]
+pub struct PatternFnPointer {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_trait_object_single_bound, code = "E0226")]
+pub struct TraitObjectBound {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_impl_trait_path, code = "E0667")]
+pub struct ImplTraitPath {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_nested_impl_trait, code = "E0666")]
+pub struct NestedImplTrait {
+    #[primary_span]
+    pub span: Span,
+    #[label(ast_passes_outer)]
+    pub outer: Span,
+    #[label(ast_passes_inner)]
+    pub inner: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_at_least_one_trait)]
+pub struct AtLeastOneTrait {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_out_of_order_params)]
+pub struct OutOfOrderParams<'a> {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    #[suggestion(code = "{ordered_params}", applicability = "machine-applicable")]
+    pub sugg_span: Span,
+    pub param_ord: &'a ParamKindOrd,
+    pub max_param: &'a ParamKindOrd,
+    pub ordered_params: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_obsolete_auto)]
+#[help]
+pub struct ObsoleteAuto {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_unsafe_negative_impl, code = "E0198")]
+pub struct UnsafeNegativeImpl {
+    #[primary_span]
+    pub span: Span,
+    #[label(ast_passes_negative)]
+    pub negative: Span,
+    #[label(ast_passes_unsafe)]
+    pub r#unsafe: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_inherent_cannot_be)]
+pub struct InherentImplCannot<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[label(ast_passes_because)]
+    pub annotation_span: Span,
+    pub annotation: &'a str,
+    #[label(ast_passes_type)]
+    pub self_ty: Span,
+    #[note(ast_passes_only_trait)]
+    pub only_trait: Option<()>,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_inherent_cannot_be, code = "E0197")]
+pub struct InherentImplCannotUnsafe<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[label(ast_passes_because)]
+    pub annotation_span: Span,
+    pub annotation: &'a str,
+    #[label(ast_passes_type)]
+    pub self_ty: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_unsafe_item)]
+pub struct UnsafeItem {
+    #[primary_span]
+    pub span: Span,
+    pub kind: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_fieldless_union)]
+pub struct FieldlessUnion {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_where_after_type_alias)]
+#[note]
+pub struct WhereAfterTypeAlias {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_generic_default_trailing)]
+pub struct GenericDefaultTrailing {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_nested_lifetimes, code = "E0316")]
+pub struct NestedLifetimes {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_optional_trait_supertrait)]
+#[note]
+pub struct OptionalTraitSupertrait {
+    #[primary_span]
+    pub span: Span,
+    pub path_str: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_optional_trait_object)]
+pub struct OptionalTraitObject {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_tilde_const_disallowed)]
+pub struct TildeConstDisallowed {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub reason: TildeConstReason,
+}
+
+#[derive(Subdiagnostic)]
+pub enum TildeConstReason {
+    #[note(ast_passes_trait)]
+    TraitObject,
+    #[note(ast_passes_closure)]
+    Closure,
+    #[note(ast_passes_function)]
+    Function {
+        #[primary_span]
+        ident: Span,
+    },
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_optional_const_exclusive)]
+pub struct OptionalConstExclusive {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_const_and_async)]
+pub struct ConstAndAsync {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    #[label(ast_passes_const)]
+    pub cspan: Span,
+    #[label(ast_passes_async)]
+    pub aspan: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_pattern_in_foreign, code = "E0130")]
+pub struct PatternInForeign {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_pattern_in_bodiless, code = "E0642")]
+pub struct PatternInBodiless {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_equality_in_where)]
+#[note]
+pub struct EqualityInWhere {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[subdiagnostic]
+    pub assoc: Option<AssociatedSuggestion>,
+    #[subdiagnostic]
+    pub assoc2: Option<AssociatedSuggestion2>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    ast_passes_suggestion,
+    code = "{param}: {path}",
+    style = "verbose",
+    applicability = "maybe-incorrect"
+)]
+pub struct AssociatedSuggestion {
+    #[primary_span]
+    pub span: Span,
+    pub ident: Ident,
+    pub param: Ident,
+    pub path: String,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(ast_passes_suggestion_path, applicability = "maybe-incorrect")]
+pub struct AssociatedSuggestion2 {
+    #[suggestion_part(code = "{args}")]
+    pub span: Span,
+    pub args: String,
+    #[suggestion_part(code = "")]
+    pub predicate: Span,
+    pub trait_segment: Ident,
+    pub potential_assoc: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_stability_outside_std, code = "E0734")]
+pub struct StabilityOutsideStd {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_feature_on_non_nightly, code = "E0554")]
+pub struct FeatureOnNonNightly {
+    #[primary_span]
+    pub span: Span,
+    pub channel: &'static str,
+    #[subdiagnostic]
+    pub stable_features: Vec<StableFeature>,
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub sugg: Option<Span>,
+}
+
+pub struct StableFeature {
+    pub name: Symbol,
+    pub since: Symbol,
+}
+
+impl AddToDiagnostic for StableFeature {
+    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    where
+        F: Fn(
+            &mut rustc_errors::Diagnostic,
+            rustc_errors::SubdiagnosticMessage,
+        ) -> rustc_errors::SubdiagnosticMessage,
+    {
+        diag.set_arg("name", self.name);
+        diag.set_arg("since", self.since);
+        diag.help(fluent::ast_passes_stable_since);
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_incompatbile_features)]
+#[help]
+pub struct IncompatibleFeatures {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    pub f1: Symbol,
+    pub f2: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_show_span)]
+pub struct ShowSpan {
+    #[primary_span]
+    pub span: Span,
+    pub msg: &'static str,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index d69c84bf4d1..926b0da2ec6 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -2,7 +2,7 @@ use rustc_ast as ast;
 use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
 use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId};
 use rustc_ast::{PatKind, RangeEnd};
-use rustc_errors::{struct_span_err, Applicability, StashKey};
+use rustc_errors::{Applicability, StashKey};
 use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP};
 use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
 use rustc_session::Session;
@@ -13,7 +13,7 @@ use rustc_target::spec::abi;
 use thin_vec::ThinVec;
 use tracing::debug;
 
-use crate::errors::ForbiddenLifetimeBound;
+use crate::errors;
 
 macro_rules! gate_feature_fn {
     ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
@@ -164,7 +164,7 @@ impl<'a> PostExpansionVisitor<'a> {
         for param in params {
             if !param.bounds.is_empty() {
                 let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
-                self.sess.emit_err(ForbiddenLifetimeBound { spans });
+                self.sess.emit_err(errors::ForbiddenLifetimeBound { spans });
             }
         }
     }
@@ -218,13 +218,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 || attr.has_name(sym::rustc_const_stable)
                 || attr.has_name(sym::rustc_default_body_unstable)
             {
-                struct_span_err!(
-                    self.sess,
-                    attr.span,
-                    E0734,
-                    "stability attributes may not be used outside of the standard library",
-                )
-                .emit();
+                self.sess.emit_err(errors::StabilityOutsideStd { span: attr.span });
             }
         }
     }
@@ -635,13 +629,13 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
             return;
         }
         for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
-            let mut err = struct_span_err!(
-                sess.parse_sess.span_diagnostic,
-                attr.span,
-                E0554,
-                "`#![feature]` may not be used on the {} release channel",
-                option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
-            );
+            let mut err = errors::FeatureOnNonNightly {
+                span: attr.span,
+                channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
+                stable_features: vec![],
+                sugg: None,
+            };
+
             let mut all_stable = true;
             for ident in
                 attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident())
@@ -652,24 +646,15 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
                     .flat_map(|&(feature, _, since)| if feature == name { since } else { None })
                     .next();
                 if let Some(since) = stable_since {
-                    err.help(&format!(
-                        "the feature `{}` has been stable since {} and no longer requires \
-                                  an attribute to enable",
-                        name, since
-                    ));
+                    err.stable_features.push(errors::StableFeature { name, since });
                 } else {
                     all_stable = false;
                 }
             }
             if all_stable {
-                err.span_suggestion(
-                    attr.span,
-                    "remove the attribute",
-                    "",
-                    Applicability::MachineApplicable,
-                );
+                err.sugg = Some(attr.span);
             }
-            err.emit();
+            sess.parse_sess.span_diagnostic.emit_err(err);
         }
     }
 }
@@ -692,16 +677,7 @@ fn check_incompatible_features(sess: &Session) {
             if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
             {
                 let spans = vec![f1_span, f2_span];
-                sess.struct_span_err(
-                    spans,
-                    &format!(
-                        "features `{}` and `{}` are incompatible, using them at the same time \
-                        is not allowed",
-                        f1_name, f2_name
-                    ),
-                )
-                .help("remove one of these features")
-                .emit();
+                sess.emit_err(errors::IncompatibleFeatures { spans, f1: f1_name, f2: f2_name });
             }
         }
     }
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
index 1be959b0de6..b9dcaee2373 100644
--- a/compiler/rustc_ast_passes/src/lib.rs
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -10,6 +10,8 @@
 #![feature(iter_is_partitioned)]
 #![feature(let_chains)]
 #![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_macros::fluent_messages;
diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs
index 27637e311f4..280cf3284c3 100644
--- a/compiler/rustc_ast_passes/src/show_span.rs
+++ b/compiler/rustc_ast_passes/src/show_span.rs
@@ -9,6 +9,8 @@ use rustc_ast as ast;
 use rustc_ast::visit;
 use rustc_ast::visit::Visitor;
 
+use crate::errors;
+
 enum Mode {
     Expression,
     Pattern,
@@ -36,21 +38,21 @@ struct ShowSpanVisitor<'a> {
 impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
     fn visit_expr(&mut self, e: &'a ast::Expr) {
         if let Mode::Expression = self.mode {
-            self.span_diagnostic.span_warn(e.span, "expression");
+            self.span_diagnostic.emit_warning(errors::ShowSpan { span: e.span, msg: "expression" });
         }
         visit::walk_expr(self, e);
     }
 
     fn visit_pat(&mut self, p: &'a ast::Pat) {
         if let Mode::Pattern = self.mode {
-            self.span_diagnostic.span_warn(p.span, "pattern");
+            self.span_diagnostic.emit_warning(errors::ShowSpan { span: p.span, msg: "pattern" });
         }
         visit::walk_pat(self, p);
     }
 
     fn visit_ty(&mut self, t: &'a ast::Ty) {
         if let Mode::Type = self.mode {
-            self.span_diagnostic.span_warn(t.span, "type");
+            self.span_diagnostic.emit_warning(errors::ShowSpan { span: t.span, msg: "type" });
         }
         visit::walk_ty(self, t);
     }
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 800f3c52177..97201219cdf 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -253,6 +253,7 @@ E0466: include_str!("./error_codes/E0466.md"),
 E0468: include_str!("./error_codes/E0468.md"),
 E0469: include_str!("./error_codes/E0469.md"),
 E0472: include_str!("./error_codes/E0472.md"),
+E0476: include_str!("./error_codes/E0476.md"),
 E0477: include_str!("./error_codes/E0477.md"),
 E0478: include_str!("./error_codes/E0478.md"),
 E0482: include_str!("./error_codes/E0482.md"),
@@ -611,7 +612,6 @@ E0793: include_str!("./error_codes/E0793.md"),
 //  E0473, // dereference of reference outside its lifetime
 //  E0474, // captured variable `..` does not outlive the enclosing closure
 //  E0475, // index of slice outside its lifetime
-    E0476, // lifetime of the source pointer does not outlive lifetime bound...
 //  E0479, // the type `..` (provided as the value of a type parameter) is...
 //  E0480, // lifetime of method receiver does not outlive the method call
 //  E0481, // lifetime of function argument does not outlive the function call
diff --git a/compiler/rustc_error_codes/src/error_codes/E0476.md b/compiler/rustc_error_codes/src/error_codes/E0476.md
new file mode 100644
index 00000000000..fc141ba77f5
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0476.md
@@ -0,0 +1,21 @@
+The coerced type does not outlive the value being coerced to.
+
+Example of erroneous code:
+
+```compile_fail,E0476
+#![feature(coerce_unsized)]
+#![feature(unsize)]
+
+use std::marker::Unsize;
+use std::ops::CoerceUnsized;
+
+// error: lifetime of the source pointer does not outlive lifetime bound of the
+//        object type
+impl<'a, 'b, T, S> CoerceUnsized<&'a T> for &'b S where S: Unsize<T> {}
+```
+
+During a coercion, the "source pointer" (the coerced type) did not outlive the
+"object type" (value being coerced to). In the above example, `'b` is not a
+subtype of `'a`. This error can currently only be encountered with the unstable
+`CoerceUnsized` trait which allows custom coercions of unsized types behind a
+smart pointer to be implemented.
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index d4ddd0c53bf..e82bad67b21 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -54,6 +54,7 @@ macro_rules! into_diagnostic_arg_using_display {
 }
 
 into_diagnostic_arg_using_display!(
+    ast::ParamKindOrd,
     i8,
     u8,
     i16,
diff --git a/compiler/rustc_expand/locales/en-US.ftl b/compiler/rustc_expand/locales/en-US.ftl
index dbd80954382..b475d285f6b 100644
--- a/compiler/rustc_expand/locales/en-US.ftl
+++ b/compiler/rustc_expand/locales/en-US.ftl
@@ -129,3 +129,7 @@ expand_module_multiple_candidates =
     .help = delete or rename one of them to remove the ambiguity
 
 expand_trace_macro = trace_macro
+
+expand_proc_macro_panicked =
+    proc macro panicked
+    .help = message: {$message}
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index d9b2b5f4802..70ab222b484 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -375,3 +375,18 @@ pub struct TraceMacro {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(expand_proc_macro_panicked)]
+pub(crate) struct ProcMacroPanicked {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub message: Option<ProcMacroPanickedHelp>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(expand_help)]
+pub(crate) struct ProcMacroPanickedHelp {
+    pub message: String,
+}
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index e9a69192068..cef64a10479 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -1,4 +1,5 @@
 use crate::base::{self, *};
+use crate::errors;
 use crate::proc_macro_server;
 
 use rustc_ast as ast;
@@ -60,11 +61,12 @@ impl base::BangProcMacro for BangProcMacro {
         let strategy = exec_strategy(ecx);
         let server = proc_macro_server::Rustc::new(ecx);
         self.client.run(&strategy, server, input, proc_macro_backtrace).map_err(|e| {
-            let mut err = ecx.struct_span_err(span, "proc macro panicked");
-            if let Some(s) = e.as_str() {
-                err.help(&format!("message: {}", s));
-            }
-            err.emit()
+            ecx.sess.emit_err(errors::ProcMacroPanicked {
+                span,
+                message: e
+                    .as_str()
+                    .map(|message| errors::ProcMacroPanickedHelp { message: message.into() }),
+            })
         })
     }
 }
diff --git a/compiler/rustc_lint/locales/en-US.ftl b/compiler/rustc_lint/locales/en-US.ftl
index b1e7cc69a80..68e62c9789a 100644
--- a/compiler/rustc_lint/locales/en-US.ftl
+++ b/compiler/rustc_lint/locales/en-US.ftl
@@ -24,6 +24,13 @@ lint_for_loops_over_fallibles =
     .use_while_let = to check pattern in a loop use `while let`
     .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents
 
+lint_map_unit_fn = `Iterator::map` call that discard the iterator's values
+    .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated
+    .function_label = this function returns `()`, which is likely not what you wanted
+    .argument_label = called `Iterator::map` with callable that returns `()`
+    .map_label = after this call to map, the resulting iterator is `impl Iterator<Item = ()>`, which means the only information carried by the iterator is the number of items
+    .suggestion = you might have meant to use `Iterator::for_each`
+
 lint_non_binding_let_on_sync_lock =
     non-binding let on a synchronization lock
 
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 2070ffea4d9..35dc533e56c 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -63,6 +63,7 @@ mod late;
 mod let_underscore;
 mod levels;
 mod lints;
+mod map_unit_fn;
 mod methods;
 mod multiple_supertrait_upcastable;
 mod non_ascii_idents;
@@ -100,6 +101,7 @@ use for_loops_over_fallibles::*;
 use hidden_unicode_codepoints::*;
 use internal::*;
 use let_underscore::*;
+use map_unit_fn::*;
 use methods::*;
 use multiple_supertrait_upcastable::*;
 use non_ascii_idents::*;
@@ -239,6 +241,7 @@ late_lint_methods!(
             NamedAsmLabels: NamedAsmLabels,
             OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
             MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
+            MapUnitFn: MapUnitFn,
         ]
     ]
 );
@@ -298,7 +301,8 @@ fn register_builtins(store: &mut LintStore) {
         UNUSED_LABELS,
         UNUSED_PARENS,
         UNUSED_BRACES,
-        REDUNDANT_SEMICOLONS
+        REDUNDANT_SEMICOLONS,
+        MAP_UNIT_FN
     );
 
     add_lint_group!("let_underscore", LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK);
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 2d9aa9074be..20ab0af5856 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -748,6 +748,22 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
     }
 }
 
+// map_unit_fn.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_map_unit_fn)]
+#[note]
+pub struct MappingToUnit {
+    #[label(lint_function_label)]
+    pub function_label: Span,
+    #[label(lint_argument_label)]
+    pub argument_label: Span,
+    #[label(lint_map_label)]
+    pub map_label: Span,
+    #[suggestion(style = "verbose", code = "{replace}", applicability = "maybe-incorrect")]
+    pub suggestion: Span,
+    pub replace: String,
+}
+
 // internal.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_default_hash_types)]
diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs
new file mode 100644
index 00000000000..62e8b4fe9e4
--- /dev/null
+++ b/compiler/rustc_lint/src/map_unit_fn.rs
@@ -0,0 +1,120 @@
+use crate::lints::MappingToUnit;
+use crate::{LateContext, LateLintPass, LintContext};
+
+use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind};
+use rustc_middle::{
+    query::Key,
+    ty::{self, Ty},
+};
+
+declare_lint! {
+    /// The `map_unit_fn` lint checks for `Iterator::map` receive
+    /// a callable that returns `()`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn foo(items: &mut Vec<u8>) {
+    ///     items.sort();
+    /// }
+    ///
+    /// fn main() {
+    ///     let mut x: Vec<Vec<u8>> = vec![
+    ///         vec![0, 2, 1],
+    ///         vec![5, 4, 3],
+    ///     ];
+    ///     x.iter_mut().map(foo);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Mapping to `()` is almost always a mistake.
+    pub MAP_UNIT_FN,
+    Warn,
+    "`Iterator::map` call that discard the iterator's values"
+}
+
+declare_lint_pass!(MapUnitFn => [MAP_UNIT_FN]);
+
+impl<'tcx> LateLintPass<'tcx> for MapUnitFn {
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) {
+        if stmt.span.from_expansion() {
+            return;
+        }
+
+        if let StmtKind::Semi(expr) = stmt.kind {
+            if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind {
+                if path.ident.name.as_str() == "map" {
+                    if receiver.span.from_expansion()
+                        || args.iter().any(|e| e.span.from_expansion())
+                        || !is_impl_slice(cx, receiver)
+                        || !is_diagnostic_name(cx, expr.hir_id, "IteratorMap")
+                    {
+                        return;
+                    }
+                    let arg_ty = cx.typeck_results().expr_ty(&args[0]);
+                    if let ty::FnDef(id, _) = arg_ty.kind() {
+                        let fn_ty = cx.tcx.fn_sig(id).skip_binder();
+                        let ret_ty = fn_ty.output().skip_binder();
+                        if is_unit_type(ret_ty) {
+                            cx.emit_spanned_lint(
+                                MAP_UNIT_FN,
+                                span,
+                                MappingToUnit {
+                                    function_label: cx.tcx.span_of_impl(*id).unwrap(),
+                                    argument_label: args[0].span,
+                                    map_label: arg_ty.default_span(cx.tcx),
+                                    suggestion: path.ident.span,
+                                    replace: "for_each".to_string(),
+                                },
+                            )
+                        }
+                    } else if let ty::Closure(id, subs) = arg_ty.kind() {
+                        let cl_ty = subs.as_closure().sig();
+                        let ret_ty = cl_ty.output().skip_binder();
+                        if is_unit_type(ret_ty) {
+                            cx.emit_spanned_lint(
+                                MAP_UNIT_FN,
+                                span,
+                                MappingToUnit {
+                                    function_label: cx.tcx.span_of_impl(*id).unwrap(),
+                                    argument_label: args[0].span,
+                                    map_label: arg_ty.default_span(cx.tcx),
+                                    suggestion: path.ident.span,
+                                    replace: "for_each".to_string(),
+                                },
+                            )
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+fn is_impl_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id) {
+            return cx.tcx.type_of(impl_id).skip_binder().is_slice();
+        }
+    }
+    false
+}
+
+fn is_unit_type(ty: Ty<'_>) -> bool {
+    ty.is_unit() || ty.is_never()
+}
+
+fn is_diagnostic_name(cx: &LateContext<'_>, id: HirId, name: &str) -> bool {
+    if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) {
+        if let Some(item) = cx.tcx.get_diagnostic_name(def_id) {
+            if item.as_str() == name {
+                return true;
+            }
+        }
+    }
+    false
+}
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 88a55dc8319..16194a6f196 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -748,7 +748,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                         let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
                         c.visit_ty(self_ty);
                         c.visit_trait_ref(t);
-                        if c.fully_stable {
+
+                        // do not lint when the trait isn't resolved, since resolution error should
+                        // be fixed first
+                        if t.path.res != Res::Err && c.fully_stable {
                             self.tcx.struct_span_lint_hir(
                                 INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
                                 item.hir_id(),
diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs
index 156b925de77..ae00232c12c 100644
--- a/library/core/src/iter/mod.rs
+++ b/library/core/src/iter/mod.rs
@@ -278,6 +278,7 @@
 //!
 //! ```
 //! # #![allow(unused_must_use)]
+//! # #![cfg_attr(not(bootstrap), allow(map_unit_fn))]
 //! let v = vec![1, 2, 3, 4, 5];
 //! v.iter().map(|x| println!("{x}"));
 //! ```
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index b3a630d9559..b8e7d0a68da 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -789,6 +789,7 @@ pub trait Iterator {
     ///     println!("{x}");
     /// }
     /// ```
+    #[rustc_diagnostic_item = "IteratorMap"]
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_do_not_const_check]
diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml
index 61b6f33bce0..18cb023d274 100644
--- a/library/test/Cargo.toml
+++ b/library/test/Cargo.toml
@@ -7,11 +7,9 @@ edition = "2021"
 crate-type = ["dylib", "rlib"]
 
 [dependencies]
-cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
 getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] }
 std = { path = "../std" }
 core = { path = "../core" }
-libc = { version = "0.2", default-features = false }
 panic_unwind = { path = "../panic_unwind" }
 panic_abort = { path = "../panic_abort" }
 
diff --git a/src/doc/index.md b/src/doc/index.md
index bf08960f338..7c97c16c20b 100644
--- a/src/doc/index.md
+++ b/src/doc/index.md
@@ -4,6 +4,20 @@
 nav {
     display: none;
 }
+body {
+    font-family: serif;
+}
+h1, h2, h3, h4, h5, h6 {
+    font-family: sans-serif;
+}
+h3 {
+    font-size: 1.35rem;
+}
+h4 {
+    font-size: 1.1rem;
+}
+
+/* Formatting for docs search bar */
 #search-input {
     width: calc(100% - 58px);
 }
@@ -21,53 +35,74 @@ nav {
 #search-but:hover, #search-input:focus {
     border-color: #55a9ff;
 }
-h2 {
-    font-size: 18px;
+
+/* Formatting for external link icon */
+svg.external-link {
+  display: inline-block;
+  position: relative;
+  vertical-align: super;
+  width: 0.7rem;
+  height: 0.7rem;
+  padding-left: 2px;
+  top: 3px;
 }
 </style>
 
-Welcome to an overview of the documentation provided by the [Rust project].
-All of these projects are managed by the Docs Team; there are other
-unofficial documentation resources as well!
+Welcome to an overview of the documentation provided by the [Rust
+project]. This page contains links to various helpful references,
+most of which are available offline (if opened with `rustup doc`). Many of these
+resources take the form of "books"; we collectively call these "The Rust
+Bookshelf." Some are large, some are small.
 
-Many of these resources take the form of "books"; we collectively call these
-"The Rust Bookshelf." Some are large, some are small.
+All of these books are managed by the Rust Organization, but other unofficial
+documentation resources are included here as well!
 
-# Learn Rust
+If you're just looking for the standard library reference, here it is:
+[Rust API documentation](std/index.html)
 
-If you'd like to learn Rust, this is the spot for you! All of these resources
+
+## Learning Rust
+
+If you'd like to learn Rust, this is the section for you! All of these resources
 assume that you have programmed before, but not in any specific language:
 
-## The Rust Programming Language
+### The Rust Programming Language
 
-Affectionately nicknamed "the book," [The Rust Programming
-Language](book/index.html) will give you an overview of the language from
-first principles. You'll build a few projects along the way, and by the end,
-you'll have a solid grasp of the language.
+Affectionately nicknamed "the book," [The Rust Programming Language](book/index.html)
+will give you an overview of the language from first principles. You'll build a
+few projects along the way, and by the end, you'll have a solid grasp of how to
+use the language.
 
-## Rust By Example
+### Rust By Example
 
 If reading multiple hundreds of pages about a language isn't your style, then
-[Rust By Example](rust-by-example/index.html) has you covered. While the book talks about code with
-a lot of words, RBE shows off a bunch of code, and keeps the talking to a
-minimum. It also includes exercises!
+[Rust By Example](rust-by-example/index.html) has you covered. RBE shows off a
+bunch of code without using a lot of words. It also includes exercises!
+
+### Rustlings
+
+[Rustlings](https://github.com/rust-lang/rustlings) guides you
+through downloading and setting up the Rust toolchain, then provides an
+interactive tool that teaches you how to solve coding challenges in Rust.
+
+### Rust Playground
 
-## Rustlings
+The [Rust Playground](https://play.rust-lang.org) is a great place
+to try out and share small bits of code, or experiment with some of the most
+popular crates.
 
-[Rustlings](https://github.com/rust-lang/rustlings) guides you through downloading and setting up the Rust toolchain,
-and teaches you the basics of reading and writing Rust syntax. It's an
-alternative to Rust by Example that works with your own environment.
 
-# Use Rust
+## Using Rust
 
-Once you've gotten familiar with the language, these resources can help you
-when you're actually using it day-to-day.
+Once you've gotten familiar with the language, these resources can help you put
+it to work.
 
-## The Standard Library
+### The Standard Library
 
-Rust's standard library has [extensive API documentation](std/index.html),
-with explanations of how to use various things, as well as example code for
-accomplishing various tasks.
+Rust's standard library has [extensive API documentation](std/index.html), with
+explanations of how to use various things, as well as example code for
+accomplishing various tasks. Code examples have a "Run" button on hover that
+opens the sample in the playground.
 
 <div>
   <form action="std/index.html" method="get">
@@ -77,76 +112,143 @@ accomplishing various tasks.
   </form>
 </div>
 
-## The Edition Guide
+### Your Personal Documentation
 
-[The Edition Guide](edition-guide/index.html) describes the Rust editions.
+Whenever you are working in a crate, `cargo doc --open` will generate
+documentation for your project _and_ all its dependencies in their correct
+version, and open it in your browser. Add the flag `--document-private-items` to
+also show items not marked `pub`.
 
-## The Rustc Book
+### The Edition Guide
 
-[The Rustc Book](rustc/index.html) describes the Rust compiler, `rustc`.
+[The Edition Guide](edition-guide/index.html) describes the Rust editions and
+their differences.
 
-## The Cargo Book
+### The `rustc` Book
 
-[The Cargo Book](cargo/index.html) is a guide to Cargo, Rust's build tool and dependency manager.
+[The `rustc` Book](rustc/index.html) describes the Rust compiler, `rustc`.
 
-## The Rustdoc Book
+### The Cargo Book
+
+[The Cargo Book](cargo/index.html) is a guide to Cargo, Rust's build tool and
+dependency manager.
+
+### The Rustdoc Book
 
 [The Rustdoc Book](rustdoc/index.html) describes our documentation tool, `rustdoc`.
 
-## The Clippy Book
+### The Clippy Book
 
 [The Clippy Book](clippy/index.html) describes our static analyzer, Clippy.
 
-## Extended Error Listing
+### Extended Error Listing
 
 Many of Rust's errors come with error codes, and you can request extended
-diagnostics from the compiler on those errors. You can also [read them
-here](error_codes/index.html), if you prefer to read them that way.
+diagnostics from the compiler on those errors (with `rustc --explain`). You can
+also read them here if you prefer: [rustc error codes](error_codes/index.html)
+
 
-# Master Rust
+## Mastering Rust
 
 Once you're quite familiar with the language, you may find these advanced
 resources useful.
 
-## The Reference
+### The Reference
 
-[The Reference](reference/index.html) is not a formal spec, but is more detailed and
-comprehensive than the book.
+[The Reference](reference/index.html) is not a formal spec, but is more detailed
+and comprehensive than the book.
 
-## The Style Guide
+### The Style Guide
 
-[The Rust Style Guide](style-guide/index.html) describes the standard formatting of Rust
-code. Most developers use rustfmt to format their code, and rustfmt's default
-formatting matches this style guide.
+[The Rust Style Guide](style-guide/index.html) describes the standard formatting
+of Rust code. Most developers use `cargo fmt` to invoke `rustfmt` and format the
+code automatically (the result matches this style guide).
 
-## The Rustonomicon
+### The Rustonomicon
 
-[The Rustonomicon](nomicon/index.html) is your guidebook to the dark arts of unsafe
-Rust. It's also sometimes called "the 'nomicon."
+[The Rustonomicon](nomicon/index.html) is your guidebook to the dark arts of
+unsafe Rust. It's also sometimes called "the 'nomicon."
 
-## The Unstable Book
+### The Unstable Book
 
-[The Unstable Book](unstable-book/index.html) has documentation for unstable features.
+[The Unstable Book](unstable-book/index.html) has documentation for unstable
+features.
 
-## The `rustc` Contribution Guide
+### The `rustc` Contribution Guide
 
-[The `rustc` Guide](https://rustc-dev-guide.rust-lang.org/) documents how
-the compiler works and how to contribute to it. This is useful if you want to build
-or modify the Rust compiler from source (e.g. to target something non-standard).
+[The `rustc` Guide](https://rustc-dev-guide.rust-lang.org/)
+documents how the compiler works and how to contribute to it. This is useful if
+you want to build or modify the Rust compiler from source (e.g. to target
+something non-standard).
 
-# Specialize Rust
 
-When using Rust in specific domain areas, consider using the following resources tailored to each domain.
+## Specialized Rust
 
-## Embedded Systems
+When using Rust in specific domains, consider using the following resources
+tailored to each area.
 
-When developing for Bare Metal or Embedded Linux systems, you may find these resources maintained by the [Embedded Working Group] useful.
+### Embedded Systems
+
+When developing for Bare Metal or Embedded Linux systems, you may find these
+resources maintained by the [Embedded Working Group] useful.
 
 [Embedded Working Group]: https://github.com/rust-embedded
 
-### The Embedded Rust Book
+#### The Embedded Rust Book
 
-[The Embedded Rust Book] is targeted at developers familiar with embedded development and familiar with Rust, but have not used Rust for embedded development.
+[The Embedded Rust Book] is targeted at developers familiar with embedded
+development and familiar with Rust, but have not used Rust for embedded
+development.
 
 [The Embedded Rust Book]: embedded-book/index.html
 [Rust project]: https://www.rust-lang.org
+
+<script>
+// check if a given link is external
+function isExternalLink(url) {
+  const tmp = document.createElement('a');
+  tmp.href = url;
+  return tmp.host !== window.location.host;
+}
+
+// Add the `external` class to all <a> tags with external links and append the external link SVG
+function updateExternalAnchors() {
+  /*
+    External link SVG from Font-Awesome
+    CC BY-SA 3.0 https://creativecommons.org/licenses/by-sa/3.0
+    via Wikimedia Commons
+  */
+  const svgText = `<svg
+     class='external-link'
+     xmlns='http://www.w3.org/2000/svg'
+     viewBox='0 -256 1850 1850'
+     width='100%'
+     height='100%'>
+       <g transform='matrix(1,0,0,-1,30,1427)'>
+         <path d='M 1408,608 V 288 Q 1408,169 1323.5,84.5 1239,0 1120,
+           0 H 288 Q 169,0 84.5,84.5 0,169 0,288 v 832 Q 0,1239 84.5,1323.5 169,
+           1408 288,1408 h 704 q 14,0 23,-9 9,-9 9,-23 v -64 q 0,-14 -9,-23 -9,
+           -9 -23,-9 H 288 q -66,0 -113,-47 -47,-47 -47,-113 V 288 q 0,-66 47,
+           -113 47,-47 113,-47 h 832 q 66,0 113,47 47,47 47,113 v 320 q 0,14 9,
+           23 9,9 23,9 h 64 q 14,0 23,-9 9,-9 9,-23 z m 384,864 V 960 q 0,
+           -26 -19,-45 -19,-19 -45,-19 -26,0 -45,19 L 1507,1091 855,439 q -10,
+           -10 -23,-10 -13,0 -23,10 L 695,553 q -10,10 -10,23 0,13 10,23 l 652,
+           652 -176,176 q -19,19 -19,45 0,26 19,45 19,19 45,19 h 512 q 26,0 45,
+           -19 19,-19 19,-45 z' style='fill:currentColor' />
+         </g>
+     </svg>`;
+  let allAnchors = document.getElementsByTagName("a");
+
+  for (var i = 0; i < allAnchors.length; ++i) {
+    let anchor = allAnchors[i];
+    if (isExternalLink(anchor.href)) {
+      anchor.classList.add("external");
+      anchor.innerHTML += svgText;
+    }
+  }
+}
+
+// on page load, update external anchors
+document.addEventListener("DOMContentLoaded", updateExternalAnchors);
+
+</script>
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 98eda97e236..373196b6642 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -22,7 +22,7 @@ macro_rules! register_diagnostics {
         pub fn error_codes() -> Vec<(&'static str, Option<&'static str>)> {
             let mut errors: Vec<(&str, Option<&str>)> = vec![
                 $((stringify!($error_code), Some($message)),)+
-                $((stringify!($undocumented), None),)+
+                $((stringify!($undocumented), None),)*
             ];
             errors.sort();
             errors
diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs
index dd2fd1911f2..8c904e8d712 100644
--- a/src/tools/tidy/src/error_codes.rs
+++ b/src/tools/tidy/src/error_codes.rs
@@ -31,7 +31,7 @@ const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E06
 
 // Error codes that don't yet have a UI test. This list will eventually be removed.
 const IGNORE_UI_TEST_CHECK: &[&str] =
-    &["E0461", "E0465", "E0476", "E0514", "E0554", "E0640", "E0717", "E0729"];
+    &["E0461", "E0465", "E0514", "E0554", "E0640", "E0717", "E0729"];
 
 macro_rules! verbose_print {
     ($verbose:expr, $($fmt:tt)*) => {
diff --git a/tests/rustdoc-ui/auxiliary/panic-handler.rs b/tests/rustdoc-ui/auxiliary/panic-handler.rs
new file mode 100644
index 00000000000..0aaaeee1051
--- /dev/null
+++ b/tests/rustdoc-ui/auxiliary/panic-handler.rs
@@ -0,0 +1,9 @@
+// compile-flags: -C panic=abort
+
+#![no_std]
+#![no_main]
+
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
diff --git a/tests/rustdoc-ui/issue-107918.rs b/tests/rustdoc-ui/issue-107918.rs
new file mode 100644
index 00000000000..13788df0fc9
--- /dev/null
+++ b/tests/rustdoc-ui/issue-107918.rs
@@ -0,0 +1,12 @@
+// aux-build:panic-handler.rs
+// compile-flags: --document-private-items
+// build-pass
+// ignore-windows
+
+#![no_std]
+#![no_main]
+
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
diff --git a/tests/ui/auto-traits/auto-trait-validation.stderr b/tests/ui/auto-traits/auto-trait-validation.stderr
index 2c380e5b09a..89b63d23d4c 100644
--- a/tests/ui/auto-traits/auto-trait-validation.stderr
+++ b/tests/ui/auto-traits/auto-trait-validation.stderr
@@ -12,7 +12,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds
 LL | auto trait Bound : Copy {}
    |            -----^^^^^^^ help: remove the super traits or lifetime bounds
    |            |
-   |            auto trait cannot have super traits or lifetime bounds
+   |            auto traits cannot have super traits or lifetime bounds
 
 error[E0568]: auto traits cannot have super traits or lifetime bounds
   --> $DIR/auto-trait-validation.rs:9:25
@@ -20,7 +20,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds
 LL | auto trait LifetimeBound : 'static {}
    |            -------------^^^^^^^^^^ help: remove the super traits or lifetime bounds
    |            |
-   |            auto trait cannot have super traits or lifetime bounds
+   |            auto traits cannot have super traits or lifetime bounds
 
 error[E0380]: auto traits cannot have associated items
   --> $DIR/auto-trait-validation.rs:11:25
@@ -29,7 +29,7 @@ LL | auto trait MyTrait { fn foo() {} }
    |            -------   ---^^^-----
    |            |         |
    |            |         help: remove these associated items
-   |            auto trait cannot have associated items
+   |            auto traits cannot have associated items
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/auto-traits/issue-23080-2.stderr b/tests/ui/auto-traits/issue-23080-2.stderr
index 267a712f62f..fed485612da 100644
--- a/tests/ui/auto-traits/issue-23080-2.stderr
+++ b/tests/ui/auto-traits/issue-23080-2.stderr
@@ -2,7 +2,7 @@ error[E0380]: auto traits cannot have associated items
   --> $DIR/issue-23080-2.rs:5:10
    |
 LL | unsafe auto trait Trait {
-   |                   ----- auto trait cannot have associated items
+   |                   ----- auto traits cannot have associated items
 LL |     type Output;
    |     -----^^^^^^- help: remove these associated items
 
diff --git a/tests/ui/auto-traits/issue-23080.stderr b/tests/ui/auto-traits/issue-23080.stderr
index c1b16b2f403..f5d607298b7 100644
--- a/tests/ui/auto-traits/issue-23080.stderr
+++ b/tests/ui/auto-traits/issue-23080.stderr
@@ -2,7 +2,7 @@ error[E0380]: auto traits cannot have associated items
   --> $DIR/issue-23080.rs:5:8
    |
 LL |   unsafe auto trait Trait {
-   |                     ----- auto trait cannot have associated items
+   |                     ----- auto traits cannot have associated items
 LL |       fn method(&self) {
    |  _____-  ^^^^^^
 LL | |         println!("Hello");
diff --git a/tests/ui/auto-traits/issue-84075.stderr b/tests/ui/auto-traits/issue-84075.stderr
index 02dca598ec2..6fbdc669b6f 100644
--- a/tests/ui/auto-traits/issue-84075.stderr
+++ b/tests/ui/auto-traits/issue-84075.stderr
@@ -4,7 +4,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds
 LL | auto trait Magic where Self: Copy {}
    |            ----- ^^^^^^^^^^^^^^^^ help: remove the super traits or lifetime bounds
    |            |
-   |            auto trait cannot have super traits or lifetime bounds
+   |            auto traits cannot have super traits or lifetime bounds
 
 error: aborting due to previous error
 
diff --git a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr
index 4827916fa5c..547b4bb5448 100644
--- a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr
+++ b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits-2.stderr
@@ -4,7 +4,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds
 LL | auto trait Magic : Sized where Option<Self> : Magic {}
    |            -----^^^^^^^^ help: remove the super traits or lifetime bounds
    |            |
-   |            auto trait cannot have super traits or lifetime bounds
+   |            auto traits cannot have super traits or lifetime bounds
 
 error[E0568]: auto traits cannot have super traits or lifetime bounds
   --> $DIR/typeck-auto-trait-no-supertraits-2.rs:4:26
@@ -12,7 +12,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds
 LL | auto trait Magic : Sized where Option<Self> : Magic {}
    |            -----         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the super traits or lifetime bounds
    |            |
-   |            auto trait cannot have super traits or lifetime bounds
+   |            auto traits cannot have super traits or lifetime bounds
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr
index d7716f4b61f..80f07410381 100644
--- a/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr
+++ b/tests/ui/auto-traits/typeck-auto-trait-no-supertraits.stderr
@@ -4,7 +4,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds
 LL | auto trait Magic: Copy {}
    |            -----^^^^^^ help: remove the super traits or lifetime bounds
    |            |
-   |            auto trait cannot have super traits or lifetime bounds
+   |            auto traits cannot have super traits or lifetime bounds
 
 error: aborting due to previous error
 
diff --git a/tests/ui/error-codes/E0476.rs b/tests/ui/error-codes/E0476.rs
new file mode 100644
index 00000000000..d5e4b8d2372
--- /dev/null
+++ b/tests/ui/error-codes/E0476.rs
@@ -0,0 +1,13 @@
+#![feature(coerce_unsized)]
+#![feature(unsize)]
+
+use std::marker::Unsize;
+use std::ops::CoerceUnsized;
+
+struct Wrapper<T>(T);
+
+impl<'a, 'b, T, S> CoerceUnsized<&'a Wrapper<T>> for &'b Wrapper<S> where S: Unsize<T> {}
+//~^ ERROR lifetime of the source pointer does not outlive lifetime bound of the object type [E0476]
+//~^^ ERROR E0119
+
+fn main() {}
diff --git a/tests/ui/error-codes/E0476.stderr b/tests/ui/error-codes/E0476.stderr
new file mode 100644
index 00000000000..a4bb26532a2
--- /dev/null
+++ b/tests/ui/error-codes/E0476.stderr
@@ -0,0 +1,31 @@
+error[E0119]: conflicting implementations of trait `CoerceUnsized<&Wrapper<_>>` for type `&Wrapper<_>`
+  --> $DIR/E0476.rs:9:1
+   |
+LL | impl<'a, 'b, T, S> CoerceUnsized<&'a Wrapper<T>> for &'b Wrapper<S> where S: Unsize<T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: conflicting implementation in crate `core`:
+           - impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b T
+             where 'b: 'a, T: Unsize<U>, T: ?Sized, U: ?Sized;
+
+error[E0476]: lifetime of the source pointer does not outlive lifetime bound of the object type
+  --> $DIR/E0476.rs:9:1
+   |
+LL | impl<'a, 'b, T, S> CoerceUnsized<&'a Wrapper<T>> for &'b Wrapper<S> where S: Unsize<T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: object type is valid for the lifetime `'a` as defined here
+  --> $DIR/E0476.rs:9:6
+   |
+LL | impl<'a, 'b, T, S> CoerceUnsized<&'a Wrapper<T>> for &'b Wrapper<S> where S: Unsize<T> {}
+   |      ^^
+note: source pointer is only valid for the lifetime `'b` as defined here
+  --> $DIR/E0476.rs:9:10
+   |
+LL | impl<'a, 'b, T, S> CoerceUnsized<&'a Wrapper<T>> for &'b Wrapper<S> where S: Unsize<T> {}
+   |          ^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0119, E0476.
+For more information about an error, try `rustc --explain E0119`.
diff --git a/tests/ui/lint/issue-106991.rs b/tests/ui/lint/issue-106991.rs
new file mode 100644
index 00000000000..e4d7f765b4a
--- /dev/null
+++ b/tests/ui/lint/issue-106991.rs
@@ -0,0 +1,13 @@
+fn foo(items: &mut Vec<u8>) {
+    items.sort();
+}
+
+fn bar() -> impl Iterator<Item = i32> {
+    //~^ ERROR expected `foo` to be a fn item that returns `i32`, but it returns `()` [E0271]
+    let mut x: Vec<Vec<u8>> = vec![vec![0, 2, 1], vec![5, 4, 3]];
+    x.iter_mut().map(foo)
+}
+
+fn main() {
+    bar();
+}
diff --git a/tests/ui/lint/issue-106991.stderr b/tests/ui/lint/issue-106991.stderr
new file mode 100644
index 00000000000..7b43f0b2ca8
--- /dev/null
+++ b/tests/ui/lint/issue-106991.stderr
@@ -0,0 +1,11 @@
+error[E0271]: expected `foo` to be a fn item that returns `i32`, but it returns `()`
+  --> $DIR/issue-106991.rs:5:13
+   |
+LL | fn bar() -> impl Iterator<Item = i32> {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `i32`
+   |
+   = note: required for `Map<std::slice::IterMut<'_, Vec<u8>>, for<'a> fn(&'a mut Vec<u8>) {foo}>` to implement `Iterator`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0271`.
diff --git a/tests/ui/lint/lint_map_unit_fn.rs b/tests/ui/lint/lint_map_unit_fn.rs
new file mode 100644
index 00000000000..dc1ecbf8424
--- /dev/null
+++ b/tests/ui/lint/lint_map_unit_fn.rs
@@ -0,0 +1,20 @@
+#![deny(map_unit_fn)]
+
+fn foo(items: &mut Vec<u8>) {
+    items.sort();
+}
+
+fn main() {
+    let mut x: Vec<Vec<u8>> = vec![vec![0, 2, 1], vec![5, 4, 3]];
+    x.iter_mut().map(foo);
+    //~^ ERROR `Iterator::map` call that discard the iterator's values
+    x.iter_mut().map(|items| {
+    //~^ ERROR `Iterator::map` call that discard the iterator's values
+        items.sort();
+    });
+    let f = |items: &mut Vec<u8>| {
+        items.sort();
+    };
+    x.iter_mut().map(f);
+    //~^ ERROR `Iterator::map` call that discard the iterator's values
+}
diff --git a/tests/ui/lint/lint_map_unit_fn.stderr b/tests/ui/lint/lint_map_unit_fn.stderr
new file mode 100644
index 00000000000..fbf689c5421
--- /dev/null
+++ b/tests/ui/lint/lint_map_unit_fn.stderr
@@ -0,0 +1,66 @@
+error: `Iterator::map` call that discard the iterator's values
+  --> $DIR/lint_map_unit_fn.rs:9:18
+   |
+LL | fn foo(items: &mut Vec<u8>) {
+   | --------------------------- this function returns `()`, which is likely not what you wanted
+...
+LL |     x.iter_mut().map(foo);
+   |                  ^^^^---^
+   |                  |   |
+   |                  |   called `Iterator::map` with callable that returns `()`
+   |                  after this call to map, the resulting iterator is `impl Iterator<Item = ()>`, which means the only information carried by the iterator is the number of items
+   |
+   = note: `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated
+note: the lint level is defined here
+  --> $DIR/lint_map_unit_fn.rs:1:9
+   |
+LL | #![deny(map_unit_fn)]
+   |         ^^^^^^^^^^^
+help: you might have meant to use `Iterator::for_each`
+   |
+LL |     x.iter_mut().for_each(foo);
+   |                  ~~~~~~~~
+
+error: `Iterator::map` call that discard the iterator's values
+  --> $DIR/lint_map_unit_fn.rs:11:18
+   |
+LL |         x.iter_mut().map(|items| {
+   |                      ^   -------
+   |                      |   |
+   |  ____________________|___this function returns `()`, which is likely not what you wanted
+   | |  __________________|
+   | | |
+LL | | |
+LL | | |         items.sort();
+LL | | |     });
+   | | |     -^ after this call to map, the resulting iterator is `impl Iterator<Item = ()>`, which means the only information carried by the iterator is the number of items
+   | | |_____||
+   | |_______|
+   |         called `Iterator::map` with callable that returns `()`
+   |
+   = note: `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated
+help: you might have meant to use `Iterator::for_each`
+   |
+LL |     x.iter_mut().for_each(|items| {
+   |                  ~~~~~~~~
+
+error: `Iterator::map` call that discard the iterator's values
+  --> $DIR/lint_map_unit_fn.rs:18:18
+   |
+LL |     let f = |items: &mut Vec<u8>| {
+   |             --------------------- this function returns `()`, which is likely not what you wanted
+...
+LL |     x.iter_mut().map(f);
+   |                  ^^^^-^
+   |                  |   |
+   |                  |   called `Iterator::map` with callable that returns `()`
+   |                  after this call to map, the resulting iterator is `impl Iterator<Item = ()>`, which means the only information carried by the iterator is the number of items
+   |
+   = note: `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated
+help: you might have meant to use `Iterator::for_each`
+   |
+LL |     x.iter_mut().for_each(f);
+   |                  ~~~~~~~~
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/methods/issues/issue-105732.stderr b/tests/ui/methods/issues/issue-105732.stderr
index 7696642548d..19ccd2de685 100644
--- a/tests/ui/methods/issues/issue-105732.stderr
+++ b/tests/ui/methods/issues/issue-105732.stderr
@@ -2,7 +2,7 @@ error[E0380]: auto traits cannot have associated items
   --> $DIR/issue-105732.rs:4:8
    |
 LL | auto trait Foo {
-   |            --- auto trait cannot have associated items
+   |            --- auto traits cannot have associated items
 LL |     fn g(&self);
    |     ---^-------- help: remove these associated items
 
diff --git a/tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr b/tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr
index dd0dac95e36..7639ae9f6a4 100644
--- a/tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr
+++ b/tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr
@@ -12,7 +12,7 @@ error[E0754]: trying to load file for module `řųśť` with non-ascii identifie
 LL | mod řųśť;
    |     ^^^^
    |
-   = help: consider using `#[path]` attribute to specify filesystem path
+   = help: consider using the `#[path]` attribute to specify filesystem path
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/stability-attribute/unresolved_stability_lint.rs b/tests/ui/stability-attribute/unresolved_stability_lint.rs
new file mode 100644
index 00000000000..818d228bc91
--- /dev/null
+++ b/tests/ui/stability-attribute/unresolved_stability_lint.rs
@@ -0,0 +1,8 @@
+#![feature(staged_api)]
+#![stable(feature = "uwu", since = "1.0.0")]
+
+#[unstable(feature = "foo", issue = "none")]
+impl Foo for () {}
+//~^ ERROR cannot find trait `Foo` in this scope
+
+fn main() {}
diff --git a/tests/ui/stability-attribute/unresolved_stability_lint.stderr b/tests/ui/stability-attribute/unresolved_stability_lint.stderr
new file mode 100644
index 00000000000..11d6abcaf36
--- /dev/null
+++ b/tests/ui/stability-attribute/unresolved_stability_lint.stderr
@@ -0,0 +1,9 @@
+error[E0405]: cannot find trait `Foo` in this scope
+  --> $DIR/unresolved_stability_lint.rs:5:6
+   |
+LL | impl Foo for () {}
+   |      ^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0405`.
diff --git a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr
index 3ec288d1382..dc967d51298 100644
--- a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr
+++ b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr
@@ -4,7 +4,7 @@ error[E0568]: auto traits cannot have super traits or lifetime bounds
 LL | auto trait Magic: Copy {}
    |            -----^^^^^^ help: remove the super traits or lifetime bounds
    |            |
-   |            auto trait cannot have super traits or lifetime bounds
+   |            auto traits cannot have super traits or lifetime bounds
 
 error[E0277]: the trait bound `NoClone: Copy` is not satisfied
   --> $DIR/supertrait-auto-trait.rs:16:23