diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 6c4b2b3724e34..dc0f0e7cd3c4c 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -584,6 +584,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         // modify their locations.
         let all_facts = &mut None;
         let mut constraints = Default::default();
+        let mut type_tests = Default::default();
         let mut closure_bounds = Default::default();
         let mut liveness_constraints =
             LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body)));
@@ -595,6 +596,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
                 &mut this.cx.borrowck_context.constraints.outlives_constraints,
                 &mut constraints,
             );
+            mem::swap(&mut this.cx.borrowck_context.constraints.type_tests, &mut type_tests);
             mem::swap(
                 &mut this.cx.borrowck_context.constraints.closure_bounds_mapping,
                 &mut closure_bounds,
@@ -619,6 +621,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         swap_constraints(self);
 
         let locations = location.to_locations();
+
+        // Use location of promoted const in collected constraints
+        for type_test in type_tests.iter() {
+            let mut type_test = type_test.clone();
+            type_test.locations = locations;
+            self.cx.borrowck_context.constraints.type_tests.push(type_test)
+        }
         for constraint in constraints.outlives().iter() {
             let mut constraint = constraint.clone();
             constraint.locations = locations;
diff --git a/compiler/rustc_error_messages/locales/en-US/middle.ftl b/compiler/rustc_error_messages/locales/en-US/middle.ftl
index ca3c91ce24a1d..b9e4499d47f36 100644
--- a/compiler/rustc_error_messages/locales/en-US/middle.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/middle.ftl
@@ -18,3 +18,12 @@ middle_limit_invalid =
 
 middle_const_eval_non_int =
     constant evaluation of enum discriminant resulted in non-integer
+
+middle_unknown_layout =
+    the type `{$ty}` has an unknown layout
+
+middle_values_too_big =
+    values of the type `{$ty}` are too big for the current architecture
+
+middle_cannot_be_normalized =
+    unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized
diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl
index 995ad4fe25859..1f1c9c29d665a 100644
--- a/compiler/rustc_error_messages/locales/en-US/passes.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl
@@ -10,88 +10,119 @@ passes_outer_crate_level_attr =
 passes_inner_crate_level_attr =
     crate-level attribute should be in the root module
 
-passes_ignored_attr_with_macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs
+passes_ignored_attr_with_macro =
+    `#[{$sym}]` is ignored on struct fields, match arms and macro defs
     .warn = {-passes_previously_accepted}
     .note = {-passes_see_issue(issue: "80564")}
 
-passes_ignored_attr = `#[{$sym}]` is ignored on struct fields and match arms
+passes_ignored_attr =
+    `#[{$sym}]` is ignored on struct fields and match arms
     .warn = {-passes_previously_accepted}
     .note = {-passes_see_issue(issue: "80564")}
 
-passes_inline_ignored_function_prototype = `#[inline]` is ignored on function prototypes
+passes_inline_ignored_function_prototype =
+    `#[inline]` is ignored on function prototypes
 
-passes_inline_ignored_constants = `#[inline]` is ignored on constants
+passes_inline_ignored_constants =
+    `#[inline]` is ignored on constants
     .warn = {-passes_previously_accepted}
     .note = {-passes_see_issue(issue: "65833")}
 
-passes_inline_not_fn_or_closure = attribute should be applied to function or closure
+passes_inline_not_fn_or_closure =
+    attribute should be applied to function or closure
     .label = not a function or closure
 
-passes_no_coverage_ignored_function_prototype = `#[no_coverage]` is ignored on function prototypes
+passes_no_coverage_ignored_function_prototype =
+    `#[no_coverage]` is ignored on function prototypes
 
 passes_no_coverage_propagate =
     `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
 
-passes_no_coverage_fn_defn = `#[no_coverage]` may only be applied to function definitions
+passes_no_coverage_fn_defn =
+    `#[no_coverage]` may only be applied to function definitions
 
-passes_no_coverage_not_coverable = `#[no_coverage]` must be applied to coverable code
+passes_no_coverage_not_coverable =
+    `#[no_coverage]` must be applied to coverable code
     .label = not coverable code
 
-passes_should_be_applied_to_fn = attribute should be applied to a function definition
+passes_should_be_applied_to_fn =
+    attribute should be applied to a function definition
     .label = not a function definition
 
-passes_naked_tracked_caller = cannot use `#[track_caller]` with `#[naked]`
+passes_naked_tracked_caller =
+    cannot use `#[track_caller]` with `#[naked]`
 
-passes_should_be_applied_to_struct_enum = attribute should be applied to a struct or enum
+passes_should_be_applied_to_struct_enum =
+    attribute should be applied to a struct or enum
     .label = not a struct or enum
 
-passes_should_be_applied_to_trait = attribute should be applied to a trait
+passes_should_be_applied_to_trait =
+    attribute should be applied to a trait
     .label = not a trait
 
-passes_target_feature_on_statement = {passes_should_be_applied_to_fn}
+passes_target_feature_on_statement =
+    {passes_should_be_applied_to_fn}
     .warn = {-passes_previously_accepted}
     .label = {passes_should_be_applied_to_fn.label}
 
-passes_should_be_applied_to_static = attribute should be applied to a static
+passes_should_be_applied_to_static =
+    attribute should be applied to a static
     .label = not a static
 
-passes_doc_expect_str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
+passes_doc_expect_str =
+    doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
 
-passes_doc_alias_empty = {$attr_str} attribute cannot have empty value
+passes_doc_alias_empty =
+    {$attr_str} attribute cannot have empty value
 
-passes_doc_alias_bad_char = {$char_} character isn't allowed in {$attr_str}
+passes_doc_alias_bad_char =
+    {$char_} character isn't allowed in {$attr_str}
 
-passes_doc_alias_start_end = {$attr_str} cannot start or end with ' '
+passes_doc_alias_start_end =
+    {$attr_str} cannot start or end with ' '
 
-passes_doc_alias_bad_location = {$attr_str} isn't allowed on {$location}
+passes_doc_alias_bad_location =
+    {$attr_str} isn't allowed on {$location}
 
-passes_doc_alias_not_an_alias = {$attr_str} is the same as the item's name
+passes_doc_alias_not_an_alias =
+    {$attr_str} is the same as the item's name
 
 passes_doc_alias_duplicated = doc alias is duplicated
     .label = first defined here
 
-passes_doc_alias_not_string_literal = `#[doc(alias("a"))]` expects string literals
+passes_doc_alias_not_string_literal =
+    `#[doc(alias("a"))]` expects string literals
 
 passes_doc_alias_malformed =
     doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
 
-passes_doc_keyword_empty_mod = `#[doc(keyword = "...")]` should be used on empty modules
+passes_doc_keyword_empty_mod =
+    `#[doc(keyword = "...")]` should be used on empty modules
 
-passes_doc_keyword_not_mod = `#[doc(keyword = "...")]` should be used on modules
+passes_doc_keyword_not_mod =
+    `#[doc(keyword = "...")]` should be used on modules
 
-passes_doc_keyword_invalid_ident = `{$doc_keyword}` is not a valid identifier
+passes_doc_keyword_invalid_ident =
+    `{$doc_keyword}` is not a valid identifier
 
 passes_doc_fake_variadic_not_valid =
     `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity
 
-passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks
+passes_doc_keyword_only_impl =
+    `#[doc(keyword = "...")]` should be used on impl blocks
 
-passes_doc_inline_conflict_first = this attribute...
-passes_doc_inline_conflict_second = ...conflicts with this attribute
-passes_doc_inline_conflict = conflicting doc inlining attributes
+passes_doc_inline_conflict_first =
+    this attribute...
+
+passes_doc_inline_conflict_second =
+    {"."}..conflicts with this attribute
+
+passes_doc_inline_conflict =
+    conflicting doc inlining attributes
     .help = remove one of the conflicting attributes
 
-passes_doc_inline_only_use = this attribute can only be applied to a `use` item
+passes_doc_inline_only_use =
+    this attribute can only be applied to a `use` item
     .label = only applicable on `use` items
     .not_a_use_item_label = not a `use` item
     .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
@@ -99,30 +130,39 @@ passes_doc_inline_only_use = this attribute can only be applied to a `use` item
 passes_doc_attr_not_crate_level =
     `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
 
-passes_attr_crate_level = this attribute can only be applied at the crate level
+passes_attr_crate_level =
+    this attribute can only be applied at the crate level
     .suggestion = to apply to the crate, use an inner attribute
     .help = to apply to the crate, use an inner attribute
     .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
 
-passes_doc_test_unknown = unknown `doc(test)` attribute `{$path}`
+passes_doc_test_unknown =
+    unknown `doc(test)` attribute `{$path}`
 
-passes_doc_test_takes_list = `#[doc(test(...)]` takes a list of attributes
+passes_doc_test_takes_list =
+    `#[doc(test(...)]` takes a list of attributes
 
-passes_doc_primitive = `doc(primitive)` should never have been stable
+passes_doc_primitive =
+    `doc(primitive)` should never have been stable
 
-passes_doc_test_unknown_any = unknown `doc` attribute `{$path}`
+passes_doc_test_unknown_any =
+    unknown `doc` attribute `{$path}`
 
-passes_doc_test_unknown_spotlight = unknown `doc` attribute `{$path}`
+passes_doc_test_unknown_spotlight =
+    unknown `doc` attribute `{$path}`
     .note = `doc(spotlight)` was renamed to `doc(notable_trait)`
     .suggestion = use `notable_trait` instead
     .no_op_note = `doc(spotlight)` is now a no-op
 
-passes_doc_test_unknown_include = unknown `doc` attribute `{$path}`
+passes_doc_test_unknown_include =
+    unknown `doc` attribute `{$path}`
     .suggestion = use `doc = include_str!` instead
 
-passes_doc_invalid = invalid `doc` attribute
+passes_doc_invalid =
+    invalid `doc` attribute
 
-passes_pass_by_value = `pass_by_value` attribute should be applied to a struct, enum or type alias
+passes_pass_by_value =
+    `pass_by_value` attribute should be applied to a struct, enum or type alias
     .label = is not a struct, enum or type alias
 
 passes_allow_incoherent_impl =
@@ -137,42 +177,54 @@ passes_must_use_async =
     `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
     .label = this attribute does nothing, the `Future`s returned by async functions are already `must_use`
 
-passes_must_use_no_effect = `#[must_use]` has no effect when applied to {$article} {$target}
+passes_must_use_no_effect =
+    `#[must_use]` has no effect when applied to {$article} {$target}
 
-passes_must_not_suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait
+passes_must_not_suspend =
+    `must_not_suspend` attribute should be applied to a struct, enum, or trait
     .label = is not a struct, enum, or trait
 
-passes_cold = {passes_should_be_applied_to_fn}
+passes_cold =
+    {passes_should_be_applied_to_fn}
     .warn = {-passes_previously_accepted}
     .label = {passes_should_be_applied_to_fn.label}
 
-passes_link = attribute should be applied to an `extern` block with non-Rust ABI
+passes_link =
+    attribute should be applied to an `extern` block with non-Rust ABI
     .warn = {-passes_previously_accepted}
     .label = not an `extern` block
 
-passes_link_name = attribute should be applied to a foreign function or static
+passes_link_name =
+    attribute should be applied to a foreign function or static
     .warn = {-passes_previously_accepted}
     .label = not a foreign function or static
     .help = try `#[link(name = "{$value}")]` instead
 
-passes_no_link = attribute should be applied to an `extern crate` item
+passes_no_link =
+    attribute should be applied to an `extern crate` item
     .label = not an `extern crate` item
 
-passes_export_name = attribute should be applied to a free function, impl method or static
+passes_export_name =
+    attribute should be applied to a free function, impl method or static
     .label = not a free function, impl method or static
 
-passes_rustc_layout_scalar_valid_range_not_struct = attribute should be applied to a struct
+passes_rustc_layout_scalar_valid_range_not_struct =
+    attribute should be applied to a struct
     .label = not a struct
 
-passes_rustc_layout_scalar_valid_range_arg = expected exactly one integer literal argument
+passes_rustc_layout_scalar_valid_range_arg =
+    expected exactly one integer literal argument
 
-passes_rustc_legacy_const_generics_only = #[rustc_legacy_const_generics] functions must only have const generics
+passes_rustc_legacy_const_generics_only =
+    #[rustc_legacy_const_generics] functions must only have const generics
     .label = non-const generic parameter
 
-passes_rustc_legacy_const_generics_index = #[rustc_legacy_const_generics] must have one index for each generic parameter
+passes_rustc_legacy_const_generics_index =
+    #[rustc_legacy_const_generics] must have one index for each generic parameter
     .label = generic parameters
 
-passes_rustc_legacy_const_generics_index_exceed = index exceeds number of arguments
+passes_rustc_legacy_const_generics_index_exceed =
+    index exceeds number of arguments
     .label = there {$arg_count ->
         [one] is
         *[other] are
@@ -181,93 +233,438 @@ passes_rustc_legacy_const_generics_index_exceed = index exceeds number of argume
         *[other] arguments
     }
 
-passes_rustc_legacy_const_generics_index_negative = arguments should be non-negative integers
+passes_rustc_legacy_const_generics_index_negative =
+    arguments should be non-negative integers
 
-passes_rustc_dirty_clean = attribute requires -Z query-dep-graph to be enabled
+passes_rustc_dirty_clean =
+    attribute requires -Z query-dep-graph to be enabled
 
-passes_link_section = attribute should be applied to a function or static
+passes_link_section =
+    attribute should be applied to a function or static
     .warn = {-passes_previously_accepted}
     .label = not a function or static
 
-passes_no_mangle_foreign = `#[no_mangle]` has no effect on a foreign {$foreign_item_kind}
+passes_no_mangle_foreign =
+    `#[no_mangle]` has no effect on a foreign {$foreign_item_kind}
     .warn = {-passes_previously_accepted}
     .label = foreign {$foreign_item_kind}
     .note = symbol names in extern blocks are not mangled
     .suggestion = remove this attribute
 
-passes_no_mangle = attribute should be applied to a free function, impl method or static
+passes_no_mangle =
+    attribute should be applied to a free function, impl method or static
     .warn = {-passes_previously_accepted}
     .label = not a free function, impl method or static
 
-passes_repr_ident = meta item in `repr` must be an identifier
+passes_repr_ident =
+    meta item in `repr` must be an identifier
 
-passes_repr_conflicting = conflicting representation hints
+passes_repr_conflicting =
+    conflicting representation hints
 
-passes_used_static = attribute must be applied to a `static` variable
+passes_used_static =
+    attribute must be applied to a `static` variable
 
-passes_used_compiler_linker = `used(compiler)` and `used(linker)` can't be used together
+passes_used_compiler_linker =
+    `used(compiler)` and `used(linker)` can't be used together
 
-passes_allow_internal_unstable = attribute should be applied to a macro
+passes_allow_internal_unstable =
+    attribute should be applied to a macro
     .label = not a macro
 
-passes_debug_visualizer_placement = attribute should be applied to a module
+passes_debug_visualizer_placement =
+    attribute should be applied to a module
 
-passes_debug_visualizer_invalid = invalid argument
+passes_debug_visualizer_invalid =
+    invalid argument
     .note_1 = expected: `natvis_file = "..."`
     .note_2 = OR
     .note_3 = expected: `gdb_script_file = "..."`
 
-passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn`
+passes_debug_visualizer_unreadable =
+    couldn't read {$file}: {$error}
+
+passes_rustc_allow_const_fn_unstable =
+    attribute should be applied to `const fn`
     .label = not a `const fn`
 
-passes_rustc_std_internal_symbol = attribute should be applied to functions or statics
+passes_rustc_std_internal_symbol =
+    attribute should be applied to functions or statics
     .label = not a function or static
 
-passes_const_trait = attribute should be applied to a trait
+passes_const_trait =
+    attribute should be applied to a trait
 
-passes_stability_promotable = attribute cannot be applied to an expression
+passes_stability_promotable =
+    attribute cannot be applied to an expression
 
-passes_deprecated = attribute is ignored here
+passes_deprecated =
+    attribute is ignored here
 
-passes_macro_use = `#[{$name}]` only has an effect on `extern crate` and modules
+passes_macro_use =
+    `#[{$name}]` only has an effect on `extern crate` and modules
 
-passes_macro_export = `#[macro_export]` only has an effect on macro definitions
+passes_macro_export =
+    `#[macro_export]` only has an effect on macro definitions
 
-passes_plugin_registrar = `#[plugin_registrar]` only has an effect on functions
+passes_plugin_registrar =
+    `#[plugin_registrar]` only has an effect on functions
 
-passes_unused_empty_lints_note = attribute `{$name}` with an empty list has no effect
+passes_unused_empty_lints_note =
+    attribute `{$name}` with an empty list has no effect
 
-passes_unused_no_lints_note = attribute `{$name}` without any lints has no effect
+passes_unused_no_lints_note =
+    attribute `{$name}` without any lints has no effect
 
 passes_unused_default_method_body_const_note =
     `default_method_body_is_const` has been replaced with `#[const_trait]` on traits
 
-passes_unused = unused attribute
+passes_unused =
+    unused attribute
     .suggestion = remove this attribute
 
-passes_non_exported_macro_invalid_attrs = attribute should be applied to function or closure
+passes_non_exported_macro_invalid_attrs =
+    attribute should be applied to function or closure
     .label = not a function or closure
 
-passes_unused_duplicate = unused attribute
+passes_unused_duplicate =
+    unused attribute
     .suggestion = remove this attribute
     .note = attribute also specified here
     .warn = {-passes_previously_accepted}
 
-passes_unused_multiple = multiple `{$name}` attributes
+passes_unused_multiple =
+    multiple `{$name}` attributes
     .suggestion = remove this attribute
     .note = attribute also specified here
 
-passes_rustc_lint_opt_ty = `#[rustc_lint_opt_ty]` should be applied to a struct
+passes_rustc_lint_opt_ty =
+    `#[rustc_lint_opt_ty]` should be applied to a struct
     .label = not a struct
 
-passes_rustc_lint_opt_deny_field_access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field
+passes_rustc_lint_opt_deny_field_access =
+    `#[rustc_lint_opt_deny_field_access]` should be applied to a field
     .label = not a field
 
-passes_link_ordinal = attribute should be applied to a foreign function or static
+passes_link_ordinal =
+    attribute should be applied to a foreign function or static
     .label = not a foreign function or static
 
-passes_collapse_debuginfo = `collapse_debuginfo` attribute should be applied to macro definitions
+passes_collapse_debuginfo =
+    `collapse_debuginfo` attribute should be applied to macro definitions
     .label = not a macro definition
 
-passes_deprecated_annotation_has_no_effect = this `#[deprecated]` annotation has no effect
+passes_deprecated_annotation_has_no_effect =
+    this `#[deprecated]` annotation has no effect
     .suggestion = remove the unnecessary deprecation attribute
+
+passes_unknown_external_lang_item =
+    unknown external lang item: `{$lang_item}`
+
+passes_missing_panic_handler =
+    `#[panic_handler]` function required, but not found
+
+passes_alloc_func_required =
+    `#[alloc_error_handler]` function required, but not found
+
+passes_missing_alloc_error_handler =
+    use `#![feature(default_alloc_error_handler)]` for a default error handler
+
+passes_missing_lang_item =
+    language item required, but not found: `{$name}`
+    .note = this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library
+    .help = you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config`
+
+passes_lang_item_on_incorrect_target =
+    `{$name}` language item must be applied to a {$expected_target}
+    .label = attribute should be applied to a {$expected_target}, not a {$actual_target}
+
+passes_unknown_lang_item =
+    definition of an unknown language item: `{$name}`
+    .label = definition of unknown language item `{$name}`
+
+passes_invalid_attr_at_crate_level =
+    `{$name}` attribute cannot be used at crate level
+    .suggestion = perhaps you meant to use an outer attribute
+
+passes_duplicate_diagnostic_item =
+    duplicate diagnostic item found: `{$name}`.
+
+passes_duplicate_diagnostic_item_in_crate =
+    duplicate diagnostic item in crate `{$crate_name}`: `{$name}`.
+
+passes_diagnostic_item_first_defined =
+    the diagnostic item is first defined here
+    .note = the diagnostic item is first defined in crate `{$orig_crate_name}`.
+
+passes_abi =
+    abi: {$abi}
+
+passes_align =
+    align: {$align}
+
+passes_size =
+    size: {$size}
+
+passes_homogeneous_aggregate =
+    homogeneous_aggregate: {$homogeneous_aggregate}
+
+passes_layout_of =
+    layout_of({$normalized_ty}) = {$ty_layout}
+
+passes_unrecognized_field =
+    unrecognized field name `{$name}`
+
+passes_layout =
+    layout error: {$layout_error}
+
+passes_feature_stable_twice =
+    feature `{$feature}` is declared stable since {$since}, but was previously declared stable since {$prev_since}
+
+passes_feature_previously_declared =
+    feature `{$feature}` is declared {$declared}, but was previously declared {$prev_declared}
+
+passes_expr_not_allowed_in_context =
+    {$expr} is not allowed in a `{$context}`
+
+passes_const_impl_const_trait =
+    const `impl`s must be for traits marked with `#[const_trait]`
+    .note = this trait must be annotated with `#[const_trait]`
+
+passes_break_non_loop =
+    `break` with value from a `{$kind}` loop
+    .label = can only break with a value inside `loop` or breakable block
+    .label2 = you can't `break` with a value in a `{$kind}` loop
+    .suggestion = use `break` on its own without a value inside this `{$kind}` loop
+    .break_expr_suggestion = alternatively, you might have meant to use the available loop label
+
+passes_continue_labeled_block =
+    `continue` pointing to a labeled block
+    .label = labeled blocks cannot be `continue`'d
+    .block_label = labeled block the `continue` points to
+
+passes_break_inside_closure =
+    `{$name}` inside of a closure
+    .label = cannot `{$name}` inside of a closure
+    .closure_label = enclosing closure
+
+passes_break_inside_async_block =
+    `{$name}` inside of an `async` block
+    .label = cannot `{$name}` inside of an `async` block
+    .async_block_label = enclosing `async` block
+
+passes_outside_loop =
+    `{$name}` outside of a loop
+    .label = cannot `{$name}` outside of a loop
+
+passes_unlabeled_in_labeled_block =
+    unlabeled `{$cf_type}` inside of a labeled block
+    .label = `{$cf_type}` statements that would diverge to or through a labeled block need to bear a label
+
+passes_unlabeled_cf_in_while_condition =
+    `break` or `continue` with no label in the condition of a `while` loop
+    .label = unlabeled `{$cf_type}` in the condition of a `while` loop
+
+passes_cannot_inline_naked_function =
+    naked functions cannot be inlined
+
+passes_undefined_naked_function_abi =
+    Rust ABI is unsupported in naked functions
+
+passes_no_patterns =
+    patterns not allowed in naked function parameters
+
+passes_params_not_allowed =
+    referencing function parameters is not allowed in naked functions
+    .help = follow the calling convention in asm block to use parameters
+
+passes_naked_functions_asm_block =
+    naked functions must contain a single asm block
+    .label_multiple_asm = multiple asm blocks are unsupported in naked functions
+    .label_non_asm = non-asm is unsupported in naked functions
+
+passes_naked_functions_operands =
+    only `const` and `sym` operands are supported in naked functions
+
+passes_naked_functions_asm_options =
+    asm options unsupported in naked functions: {$unsupported_options}
+
+passes_naked_functions_must_use_noreturn =
+    asm in naked functions must use `noreturn` option
+    .suggestion = consider specifying that the asm block is responsible for returning from the function
+
+passes_attr_only_on_main =
+    `{$attr}` attribute can only be used on `fn main()`
+
+passes_attr_only_on_root_main =
+    `{$attr}` attribute can only be used on root `fn main()`
+
+passes_attr_only_in_functions =
+    `{$attr}` attribute can only be used on functions
+
+passes_multiple_rustc_main =
+    multiple functions with a `#[rustc_main]` attribute
+    .first = first `#[rustc_main]` function
+    .additional = additional `#[rustc_main]` function
+
+passes_multiple_start_functions =
+    multiple `start` functions
+    .label = multiple `start` functions
+    .previous = previous `#[start]` function here
+
+passes_extern_main =
+    the `main` function cannot be declared in an `extern` block
+
+passes_unix_sigpipe_values =
+    valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
+
+passes_no_main_function =
+    `main` function not found in crate `{$crate_name}`
+    .here_is_main = here is a function named `main`
+    .one_or_more_possible_main = you have one or more functions named `main` not defined at the crate level
+    .consider_moving_main = consider moving the `main` function definitions
+    .main_must_be_defined_at_crate = the main function must be defined at the crate level{$has_filename ->
+        [true] {" "}(in `{$filename}`)
+        *[false] {""}
+    }
+    .consider_adding_main_to_file = consider adding a `main` function to `{$filename}`
+    .consider_adding_main_at_crate = consider adding a `main` function at the crate level
+    .teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/
+    .non_function_main = non-function item at `crate::main` is found
+
+passes_duplicate_lang_item =
+    found duplicate lang item `{$lang_item_name}`
+    .first_defined_span = the lang item is first defined here
+    .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)
+    .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`.
+    .first_definition_local = first definition in the local crate (`{$orig_crate_name}`)
+    .second_definition_local = second definition in the local crate (`{$crate_name}`)
+    .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
+    .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
+
+passes_duplicate_lang_item_crate =
+    duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`.
+    .first_defined_span = the lang item is first defined here
+    .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)
+    .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`.
+    .first_definition_local = first definition in the local crate (`{$orig_crate_name}`)
+    .second_definition_local = second definition in the local crate (`{$crate_name}`)
+    .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
+    .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
+
+passes_duplicate_lang_item_crate_depends =
+    duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`.
+    .first_defined_span = the lang item is first defined here
+    .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)
+    .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`.
+    .first_definition_local = first definition in the local crate (`{$orig_crate_name}`)
+    .second_definition_local = second definition in the local crate (`{$crate_name}`)
+    .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
+    .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
+
+passes_incorrect_target =
+    `{$name}` language item must be applied to a {$kind} with {$at_least ->
+        [true] at least {$num}
+        *[false] {$num}
+    } generic {$num ->
+        [one] argument
+        *[other] arguments
+    }
+    .label = this {$kind} has {$actual_num} generic {$actual_num ->
+        [one] argument
+        *[other] arguments
+    }
+
+passes_useless_assignment =
+    useless assignment of {$is_field_assign ->
+        [true] field
+        *[false] variable
+    } of type `{$ty}` to itself
+
+passes_only_has_effect_on =
+    `#[{$attr_name}]` only has an effect on {$target_name ->
+        [function] functions
+        [module] modules
+        [implementation_block] implementation blocks
+        *[unspecified] (unspecified--this is a compiler bug)
+    }
+
+passes_object_lifetime_err =
+    {$repr}
+
+passes_unrecognized_repr_hint =
+    unrecognized representation hint
+    .help = valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+
+passes_attr_application_enum =
+    attribute should be applied to an enum
+    .label = not an enum
+
+passes_attr_application_struct =
+    attribute should be applied to a struct
+    .label = not a struct
+
+passes_attr_application_struct_union =
+    attribute should be applied to a struct or union
+    .label = not a struct or union
+
+passes_attr_application_struct_enum_union =
+    attribute should be applied to a struct, enum, or union
+    .label = not a struct, enum, or union
+
+passes_attr_application_struct_enum_function_union =
+    attribute should be applied to a struct, enum, function, or union
+    .label = not a struct, enum, function, or union
+
+passes_transparent_incompatible =
+    transparent {$target} cannot have other repr hints
+
+passes_deprecated_attribute =
+    deprecated attribute must be paired with either stable or unstable attribute
+
+passes_useless_stability =
+    this stability annotation is useless
+    .label = useless stability annotation
+    .item = the stability attribute annotates this item
+
+passes_invalid_stability =
+    invalid stability version found
+    .label = invalid stability version
+    .item = the stability attribute annotates this item
+
+passes_cannot_stabilize_deprecated =
+    an API can't be stabilized after it is deprecated
+    .label = invalid version
+    .item = the stability attribute annotates this item
+
+passes_invalid_deprecation_version =
+    invalid deprecation version found
+    .label = invalid deprecation version
+    .item = the stability attribute annotates this item
+
+passes_missing_stability_attr =
+    {$descr} has missing stability attribute
+
+passes_missing_const_stab_attr =
+    {$descr} has missing const stability attribute
+
+passes_trait_impl_const_stable =
+    trait implementations cannot be const stable yet
+    .note = see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
+
+passes_feature_only_on_nightly =
+    `#![feature]` may not be used on the {$release_channel} release channel
+
+passes_unknown_feature =
+    unknown feature `{$feature}`
+
+passes_implied_feature_not_exist =
+    feature `{$implied_by}` implying `{$feature}` does not exist
+
+passes_duplicate_feature_err =
+    the feature `{$feature}` has already been declared
+
+passes_missing_const_err =
+    attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const`
+    .help = make the function or method const
+    .label = attribute specified here
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index bbe6435be59f4..9b41234dcfb66 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -255,6 +255,56 @@ impl EmissionGuarantee for () {
     }
 }
 
+/// Marker type which enables implementation of `create_note` and `emit_note` functions for
+/// note-without-error struct diagnostics.
+#[derive(Copy, Clone)]
+pub struct Noted;
+
+impl<'a> DiagnosticBuilder<'a, Noted> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    pub(crate) fn new_note(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
+        let diagnostic = Diagnostic::new_with_code(Level::Note, None, message);
+        Self::new_diagnostic_note(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    pub(crate) fn new_diagnostic_note(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+        debug!("Created new diagnostic");
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(diagnostic),
+            },
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl EmissionGuarantee for Noted {
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+        match db.inner.state {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+                handler.emit_diagnostic(&mut db.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+
+        Noted
+    }
+
+    fn make_diagnostic_builder(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self> {
+        DiagnosticBuilder::new_note(handler, msg)
+    }
+}
+
 impl<'a> DiagnosticBuilder<'a, !> {
     /// Convenience function for internal use, clients should use one of the
     /// `struct_*` methods on [`Handler`].
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 2f6686f81965b..c8ccdc539af5a 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -374,7 +374,7 @@ pub use diagnostic::{
     AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgFromDisplay,
     DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
 };
-pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee};
+pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
 use std::backtrace::Backtrace;
 
 /// A handler deals with errors and other compiler output.
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 77ab27266b286..f6f25603581b2 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -380,7 +380,6 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
-    let _ = tcx.representability(def_id);
 
     if def.repr().simd() {
         check_simd(tcx, span, def_id);
@@ -394,7 +393,6 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
-    let _ = tcx.representability(def_id);
     check_transparent(tcx, span, def);
     check_union_fields(tcx, span, def_id);
     check_packed(tcx, span, def);
@@ -1489,7 +1487,6 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
 
     detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
 
-    let _ = tcx.representability(def_id);
     check_transparent(tcx, sp, def);
 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 0bd45bb1c9153..0a8a1bec9b8a3 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1041,6 +1041,8 @@ fn check_type_defn<'tcx, F>(
 ) where
     F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>,
 {
+    let _ = tcx.representability(item.def_id.def_id);
+
     enter_wf_checking_ctxt(tcx, item.span, item.def_id.def_id, |wfcx| {
         let variants = lookup_fields(wfcx);
         let packed = tcx.adt_def(item.def_id).repr().packed();
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index a175b1eb46733..06eb10c9137a1 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -613,16 +613,8 @@ rustc_queries! {
         separate_provide_extern
     }
 
-    // The cycle error here should be reported as an error by `check_representable`.
-    // We consider the type as Sized in the meanwhile to avoid
-    // further errors (done in impl Value for AdtSizedConstraint).
-    // Use `cycle_delay_bug` to delay the cycle error here to be emitted later
-    // in case we accidentally otherwise don't emit an error.
-    query adt_sized_constraint(
-        key: DefId
-    ) -> AdtSizedConstraint<'tcx> {
+    query adt_sized_constraint(key: DefId) -> &'tcx [Ty<'tcx>] {
         desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) }
-        cycle_delay_bug
     }
 
     query adt_dtorck_constraint(
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 26b6be7a1b891..b0a2412ab153f 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -26,9 +26,6 @@ use super::{
     Destructor, FieldDef, GenericPredicates, ReprOptions, Ty, TyCtxt, VariantDef, VariantDiscr,
 };
 
-#[derive(Copy, Clone, HashStable, Debug)]
-pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]);
-
 bitflags! {
     #[derive(HashStable, TyEncodable, TyDecodable)]
     pub struct AdtFlags: u32 {
@@ -563,7 +560,7 @@ impl<'tcx> AdtDef<'tcx> {
     /// Due to normalization being eager, this applies even if
     /// the associated type is behind a pointer (e.g., issue #31299).
     pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx [Ty<'tcx>]> {
-        ty::EarlyBinder(tcx.adt_sized_constraint(self.did()).0)
+        ty::EarlyBinder(tcx.adt_sized_constraint(self.did()))
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 5f8729a8ddf3c..6045c1acdd032 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -191,10 +191,29 @@ pub enum LayoutError<'tcx> {
 
 impl<'a> IntoDiagnostic<'a, !> for LayoutError<'a> {
     fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> {
-        handler.struct_fatal(self.to_string())
+        let mut diag = handler.struct_fatal("");
+
+        match self {
+            LayoutError::Unknown(ty) => {
+                diag.set_arg("ty", ty);
+                diag.set_primary_message(rustc_errors::fluent::middle::unknown_layout);
+            }
+            LayoutError::SizeOverflow(ty) => {
+                diag.set_arg("ty", ty);
+                diag.set_primary_message(rustc_errors::fluent::middle::values_too_big);
+            }
+            LayoutError::NormalizationFailure(ty, e) => {
+                diag.set_arg("ty", ty);
+                diag.set_arg("failure_ty", e.get_type_for_failure());
+                diag.set_primary_message(rustc_errors::fluent::middle::cannot_be_normalized);
+            }
+        }
+        diag
     }
 }
 
+// FIXME: Once the other errors that embed this error have been converted to translateable
+// diagnostics, this Display impl should be removed.
 impl<'tcx> fmt::Display for LayoutError<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs
index b6cda34c51f61..ce1b69935f27a 100644
--- a/compiler/rustc_middle/src/ty/query.rs
+++ b/compiler/rustc_middle/src/ty/query.rs
@@ -32,7 +32,7 @@ use crate::ty::layout::TyAndLayout;
 use crate::ty::subst::{GenericArg, SubstsRef};
 use crate::ty::util::AlwaysRequiresDrop;
 use crate::ty::GeneratorDiagnosticData;
-use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
+use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
 use rustc_ast as ast;
 use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_attr as attr;
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index bb89959b29ded..f4b4c3fb05a7c 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -3,7 +3,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_middle::ty::Representability;
-use rustc_middle::ty::{self, AdtSizedConstraint, DefIdTree, Ty, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
 use rustc_query_system::query::QueryInfo;
 use rustc_query_system::Value;
 use rustc_span::def_id::LocalDefId;
@@ -31,18 +31,6 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
     }
 }
 
-impl<'tcx> Value<TyCtxt<'tcx>> for AdtSizedConstraint<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
-        // SAFETY: This is never called when `Self` is not `AdtSizedConstraint<'tcx>`.
-        // FIXME: Represent the above fact in the trait system somehow.
-        unsafe {
-            std::mem::transmute::<AdtSizedConstraint<'tcx>, AdtSizedConstraint<'_>>(
-                AdtSizedConstraint(tcx.intern_type_list(&[tcx.ty_error()])),
-            )
-        }
-    }
-}
-
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
     fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
         let err = tcx.ty_error();
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 5455d063c13a9..73fb89bbc385e 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -4,10 +4,13 @@
 //! conflicts between multiple such attributes attached to the same
 //! item.
 
-use crate::errors;
+use crate::errors::{
+    self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
+    OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint,
+};
 use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{fluent, Applicability, MultiSpan};
 use rustc_expand::base::resolve_path;
 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
@@ -164,17 +167,17 @@ impl CheckAttrVisitor<'_> {
                 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
                 sym::deprecated => self.check_deprecated(hir_id, attr, span, target),
                 sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
-                sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
+                sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod),
                 sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
                 sym::macro_export => self.check_macro_export(hir_id, attr, target),
                 sym::ignore | sym::should_panic | sym::proc_macro_derive => {
-                    self.check_generic_attr(hir_id, attr, target, &[Target::Fn])
+                    self.check_generic_attr(hir_id, attr, target, Target::Fn)
                 }
                 sym::automatically_derived => {
-                    self.check_generic_attr(hir_id, attr, target, &[Target::Impl])
+                    self.check_generic_attr(hir_id, attr, target, Target::Impl)
                 }
                 sym::no_implicit_prelude => {
-                    self.check_generic_attr(hir_id, attr, target, &[Target::Mod])
+                    self.check_generic_attr(hir_id, attr, target, Target::Mod)
                 }
                 sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
                 _ => {}
@@ -351,31 +354,17 @@ impl CheckAttrVisitor<'_> {
         hir_id: HirId,
         attr: &Attribute,
         target: Target,
-        allowed_targets: &[Target],
+        allowed_target: Target,
     ) {
-        if !allowed_targets.iter().any(|t| t == &target) {
-            let name = attr.name_or_empty();
-            let mut i = allowed_targets.iter();
-            // Pluralize
-            let b = i.next().map_or_else(String::new, |t| t.to_string() + "s");
-            let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| {
-                if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 {
-                    b.push_str(", and ");
-                } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 {
-                    b.push_str(" and ");
-                } else {
-                    b.push_str(", ");
-                }
-                // Pluralize
-                b.push_str(&(allowed_target.to_string() + "s"));
-                b
-            });
-            self.tcx.struct_span_lint_hir(
+        if target != allowed_target {
+            self.tcx.emit_spanned_lint(
                 UNUSED_ATTRIBUTES,
                 hir_id,
                 attr.span,
-                &format!("`#[{name}]` only has an effect on {}", supported_names),
-                |lint| lint,
+                OnlyHasEffectOn {
+                    attr_name: attr.name_or_empty(),
+                    target_name: allowed_target.name().replace(" ", "_"),
+                },
             );
         }
     }
@@ -432,7 +421,7 @@ impl CheckAttrVisitor<'_> {
                     ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
                     ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
                 };
-                tcx.sess.span_err(p.span, &repr);
+                tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr });
             }
         }
     }
@@ -1605,12 +1594,17 @@ impl CheckAttrVisitor<'_> {
                 continue;
             }
 
-            let (article, allowed_targets) = match hint.name_or_empty() {
+            match hint.name_or_empty() {
                 sym::C => {
                     is_c = true;
                     match target {
                         Target::Struct | Target::Union | Target::Enum => continue,
-                        _ => ("a", "struct, enum, or union"),
+                        _ => {
+                            self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
+                                hint_span: hint.span(),
+                                span,
+                            });
+                        }
                     }
                 }
                 sym::align => {
@@ -1626,12 +1620,20 @@ impl CheckAttrVisitor<'_> {
 
                     match target {
                         Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
-                        _ => ("a", "struct, enum, function, or union"),
+                        _ => {
+                            self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion {
+                                hint_span: hint.span(),
+                                span,
+                            });
+                        }
                     }
                 }
                 sym::packed => {
                     if target != Target::Struct && target != Target::Union {
-                        ("a", "struct or union")
+                        self.tcx.sess.emit_err(AttrApplication::StructUnion {
+                            hint_span: hint.span(),
+                            span,
+                        });
                     } else {
                         continue;
                     }
@@ -1639,7 +1641,9 @@ impl CheckAttrVisitor<'_> {
                 sym::simd => {
                     is_simd = true;
                     if target != Target::Struct {
-                        ("a", "struct")
+                        self.tcx
+                            .sess
+                            .emit_err(AttrApplication::Struct { hint_span: hint.span(), span });
                     } else {
                         continue;
                     }
@@ -1648,7 +1652,12 @@ impl CheckAttrVisitor<'_> {
                     is_transparent = true;
                     match target {
                         Target::Struct | Target::Union | Target::Enum => continue,
-                        _ => ("a", "struct, enum, or union"),
+                        _ => {
+                            self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
+                                hint_span: hint.span(),
+                                span,
+                            });
+                        }
                     }
                 }
                 sym::i8
@@ -1665,35 +1674,18 @@ impl CheckAttrVisitor<'_> {
                 | sym::usize => {
                     int_reprs += 1;
                     if target != Target::Enum {
-                        ("an", "enum")
+                        self.tcx
+                            .sess
+                            .emit_err(AttrApplication::Enum { hint_span: hint.span(), span });
                     } else {
                         continue;
                     }
                 }
                 _ => {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        hint.span(),
-                        E0552,
-                        "unrecognized representation hint"
-                    )
-                    .help("valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, \
-                          `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`")
-                    .emit();
-
+                    self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() });
                     continue;
                 }
             };
-
-            struct_span_err!(
-                self.tcx.sess,
-                hint.span(),
-                E0517,
-                "{}",
-                &format!("attribute should be applied to {article} {allowed_targets}")
-            )
-            .span_label(span, &format!("not {article} {allowed_targets}"))
-            .emit();
         }
 
         // Just point at all repr hints if there are any incompatibilities.
@@ -1703,14 +1695,9 @@ impl CheckAttrVisitor<'_> {
         // Error on repr(transparent, <anything else>).
         if is_transparent && hints.len() > 1 {
             let hint_spans: Vec<_> = hint_spans.clone().collect();
-            struct_span_err!(
-                self.tcx.sess,
-                hint_spans,
-                E0692,
-                "transparent {} cannot have other repr hints",
-                target
-            )
-            .emit();
+            self.tcx
+                .sess
+                .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() });
         }
         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
         if (int_reprs > 1)
@@ -1862,14 +1849,12 @@ impl CheckAttrVisitor<'_> {
 
         match std::fs::File::open(&file) {
             Ok(_) => true,
-            Err(err) => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        meta_item.span,
-                        &format!("couldn't read {}: {}", file.display(), err),
-                    )
-                    .emit();
+            Err(error) => {
+                self.tcx.sess.emit_err(DebugVisualizerUnreadable {
+                    span: meta_item.span,
+                    file: &file,
+                    error,
+                });
                 false
             }
         }
@@ -2180,25 +2165,11 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
         if attr.style == AttrStyle::Inner {
             for attr_to_check in ATTRS_TO_CHECK {
                 if attr.has_name(*attr_to_check) {
-                    let mut err = tcx.sess.struct_span_err(
-                        attr.span,
-                        &format!(
-                            "`{}` attribute cannot be used at crate level",
-                            attr_to_check.to_ident_string()
-                        ),
-                    );
-                    // Only emit an error with a suggestion if we can create a
-                    // string out of the attribute span
-                    if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) {
-                        let replacement = src.replace("#!", "#");
-                        err.span_suggestion_verbose(
-                            attr.span,
-                            "perhaps you meant to use an outer attribute",
-                            replacement,
-                            rustc_errors::Applicability::MachineApplicable,
-                        );
-                    }
-                    err.emit();
+                    tcx.sess.emit_err(InvalidAttrAtCrateLevel {
+                        span: attr.span,
+                        snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(),
+                        name: *attr_to_check,
+                    });
                 }
             }
         }
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index e502b9b54e302..aa726d6cd92aa 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -8,7 +8,6 @@
 //! through, but errors for structured control flow in a `const` should be emitted here.
 
 use rustc_attr as attr;
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -18,6 +17,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::feature_err;
 use rustc_span::{sym, Span, Symbol};
 
+use crate::errors::ExprNotAllowedInContext;
+
 /// An expression that is not *always* legal in a const context.
 #[derive(Clone, Copy)]
 enum NonConstExpr {
@@ -133,18 +134,22 @@ impl<'tcx> CheckConstVisitor<'tcx> {
         let const_kind =
             const_kind.expect("`const_check_violated` may only be called inside a const context");
 
-        let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());
-
         let required_gates = required_gates.unwrap_or(&[]);
         let missing_gates: Vec<_> =
             required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
 
         match missing_gates.as_slice() {
             [] => {
-                struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit();
+                tcx.sess.emit_err(ExprNotAllowedInContext {
+                    span,
+                    expr: expr.name(),
+                    context: const_kind.keyword_name(),
+                });
             }
 
             [missing_primary, ref missing_secondary @ ..] => {
+                let msg =
+                    format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());
                 let mut err = feature_err(&tcx.sess.parse_sess, *missing_primary, span, &msg);
 
                 // If multiple feature gates would be required to enable this expression, include
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 08f704da62c2f..6a97ad3fe86e2 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -4,7 +4,7 @@
 
 use itertools::Itertools;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, Applicability, DelayDm, MultiSpan};
+use rustc_errors::{pluralize, Applicability, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -18,6 +18,8 @@ use rustc_session::lint;
 use rustc_span::symbol::{sym, Symbol};
 use std::mem;
 
+use crate::errors::UselessAssignment;
+
 // Any local node that may call something in its body block should be
 // explored. For example, if it's a live Node::Item that is a
 // function, then we should explore its block to check for codes that
@@ -180,19 +182,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 && !assign.span.from_expansion()
         {
                 let is_field_assign = matches!(lhs.kind, hir::ExprKind::Field(..));
-                self.tcx.struct_span_lint_hir(
+                self.tcx.emit_spanned_lint(
                     lint::builtin::DEAD_CODE,
                     assign.hir_id,
                     assign.span,
-                    DelayDm(|| format!(
-                            "useless assignment of {} of type `{}` to itself",
-                            if is_field_assign { "field" } else { "variable" },
-                            self.typeck_results().expr_ty(lhs),
-                        )),
-                    |lint| {
-                        lint
-
-                    },
+                    UselessAssignment { is_field_assign, ty: self.typeck_results().expr_ty(lhs) }
                 )
         }
     }
diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs
index e08683fe23b20..253b0a88e48aa 100644
--- a/compiler/rustc_passes/src/debugger_visualizer.rs
+++ b/compiler/rustc_passes/src/debugger_visualizer.rs
@@ -13,6 +13,8 @@ use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType};
 
 use std::sync::Arc;
 
+use crate::errors::DebugVisualizerUnreadable;
+
 fn check_for_debugger_visualizer<'tcx>(
     tcx: TyCtxt<'tcx>,
     hir_id: HirId,
@@ -54,13 +56,12 @@ fn check_for_debugger_visualizer<'tcx>(
                     debugger_visualizers
                         .insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type));
                 }
-                Err(err) => {
-                    tcx.sess
-                        .struct_span_err(
-                            meta_item.span,
-                            &format!("couldn't read {}: {}", file.display(), err),
-                        )
-                        .emit();
+                Err(error) => {
+                    tcx.sess.emit_err(DebugVisualizerUnreadable {
+                        span: meta_item.span,
+                        file: &file,
+                        error,
+                    });
                 }
             }
         }
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index e428d9293db17..3f991cf657241 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -14,7 +14,9 @@ use rustc_hir::diagnostic_items::DiagnosticItems;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{kw::Empty, sym, Symbol};
+
+use crate::errors::{DuplicateDiagnosticItem, DuplicateDiagnosticItemInCrate};
 
 fn observe_item<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -33,25 +35,22 @@ fn collect_item(tcx: TyCtxt<'_>, items: &mut DiagnosticItems, name: Symbol, item
     items.id_to_name.insert(item_def_id, name);
     if let Some(original_def_id) = items.name_to_id.insert(name, item_def_id) {
         if original_def_id != item_def_id {
-            let mut err = match tcx.hir().span_if_local(item_def_id) {
-                Some(span) => tcx
-                    .sess
-                    .struct_span_err(span, &format!("duplicate diagnostic item found: `{name}`.")),
-                None => tcx.sess.struct_err(&format!(
-                    "duplicate diagnostic item in crate `{}`: `{}`.",
-                    tcx.crate_name(item_def_id.krate),
-                    name
-                )),
-            };
-            if let Some(span) = tcx.hir().span_if_local(original_def_id) {
-                err.span_note(span, "the diagnostic item is first defined here");
+            let orig_span = tcx.hir().span_if_local(original_def_id);
+            let orig_crate_name = if orig_span.is_some() {
+                None
             } else {
-                err.note(&format!(
-                    "the diagnostic item is first defined in crate `{}`.",
-                    tcx.crate_name(original_def_id.krate)
-                ));
-            }
-            err.emit();
+                Some(tcx.crate_name(original_def_id.krate))
+            };
+            match tcx.hir().span_if_local(item_def_id) {
+                Some(span) => tcx.sess.emit_err(DuplicateDiagnosticItem { span, name }),
+                None => tcx.sess.emit_err(DuplicateDiagnosticItemInCrate {
+                    span: orig_span,
+                    orig_crate_name: orig_crate_name.unwrap_or(Empty),
+                    have_orig_crate_name: orig_crate_name.map(|_| ()),
+                    crate_name: tcx.crate_name(item_def_id.krate),
+                    name,
+                }),
+            };
         }
     }
 }
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index eec37d19a8831..38a259ca8846f 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -1,5 +1,5 @@
 use rustc_ast::entry::EntryPointType;
-use rustc_errors::struct_span_err;
+use rustc_errors::error_code;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
@@ -8,7 +8,12 @@ use rustc_middle::ty::{DefIdTree, TyCtxt};
 use rustc_session::config::{sigpipe, CrateType, EntryFnType};
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_span::{Span, Symbol};
+
+use crate::errors::{
+    AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain,
+    MultipleStartFunctions, NoMainErr, UnixSigpipeValues,
+};
 
 struct EntryContext<'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -71,14 +76,9 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry
     }
 }
 
-fn err_if_attr_found(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol, details: &str) {
+fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> {
     let attrs = ctxt.tcx.hir().attrs(id.hir_id());
-    if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) {
-        ctxt.tcx
-            .sess
-            .struct_span_err(attr.span, &format!("`{}` attribute {}", sym, details))
-            .emit();
-    }
+    ctxt.tcx.sess.find_by_name(attrs, sym).map(|attr| attr.span)
 }
 
 fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
@@ -86,49 +86,47 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
 
     match entry_point_type(ctxt, id, at_root) {
         EntryPointType::None => {
-            err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
+            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
+                ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
+            }
         }
         _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => {
-            err_if_attr_found(ctxt, id, sym::start, "can only be used on functions");
-            err_if_attr_found(ctxt, id, sym::rustc_main, "can only be used on functions");
+            for attr in [sym::start, sym::rustc_main] {
+                if let Some(span) = attr_span_by_symbol(ctxt, id, attr) {
+                    ctxt.tcx.sess.emit_err(AttrOnlyInFunctions { span, attr });
+                }
+            }
         }
         EntryPointType::MainNamed => (),
         EntryPointType::OtherMain => {
-            err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on root `fn main()`");
+            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
+                ctxt.tcx.sess.emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe });
+            }
             ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id));
         }
         EntryPointType::RustcMainAttr => {
             if ctxt.attr_main_fn.is_none() {
                 ctxt.attr_main_fn = Some((id.def_id.def_id, ctxt.tcx.def_span(id.def_id)));
             } else {
-                struct_span_err!(
-                    ctxt.tcx.sess,
-                    ctxt.tcx.def_span(id.def_id.to_def_id()),
-                    E0137,
-                    "multiple functions with a `#[rustc_main]` attribute"
-                )
-                .span_label(
-                    ctxt.tcx.def_span(id.def_id.to_def_id()),
-                    "additional `#[rustc_main]` function",
-                )
-                .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[rustc_main]` function")
-                .emit();
+                ctxt.tcx.sess.emit_err(MultipleRustcMain {
+                    span: ctxt.tcx.def_span(id.def_id.to_def_id()),
+                    first: ctxt.attr_main_fn.unwrap().1,
+                    additional: ctxt.tcx.def_span(id.def_id.to_def_id()),
+                });
             }
         }
         EntryPointType::Start => {
-            err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
+            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
+                ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
+            }
             if ctxt.start_fn.is_none() {
                 ctxt.start_fn = Some((id.def_id.def_id, ctxt.tcx.def_span(id.def_id)));
             } else {
-                struct_span_err!(
-                    ctxt.tcx.sess,
-                    ctxt.tcx.def_span(id.def_id),
-                    E0138,
-                    "multiple `start` functions"
-                )
-                .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here")
-                .span_label(ctxt.tcx.def_span(id.def_id.to_def_id()), "multiple `start` functions")
-                .emit();
+                ctxt.tcx.sess.emit_err(MultipleStartFunctions {
+                    span: ctxt.tcx.def_span(id.def_id),
+                    labeled: ctxt.tcx.def_span(id.def_id.to_def_id()),
+                    previous: ctxt.start_fn.unwrap().1,
+                });
             }
         }
     }
@@ -144,12 +142,7 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
         if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() {
             // non-local main imports are handled below
             if let Some(def_id) = def_id.as_local() && matches!(tcx.hir().find_by_def_id(def_id), Some(Node::ForeignItem(_))) {
-                tcx.sess
-                    .struct_span_err(
-                        tcx.def_span(def_id),
-                        "the `main` function cannot be declared in an `extern` block",
-                    )
-                    .emit();
+                tcx.sess.emit_err(ExternMain { span: tcx.def_span(def_id) });
                 return None;
             }
 
@@ -182,12 +175,7 @@ fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
                 sigpipe::DEFAULT
             }
             _ => {
-                tcx.sess
-                    .struct_span_err(
-                        attr.span,
-                        "valid values for `#[unix_sigpipe = \"...\"]` are `inherit`, `sig_ign`, or `sig_dfl`",
-                    )
-                    .emit();
+                tcx.sess.emit_err(UnixSigpipeValues { span: attr.span });
                 sigpipe::DEFAULT
             }
         }
@@ -206,52 +194,29 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
     }
 
     // There is no main function.
-    let mut err = struct_span_err!(
-        tcx.sess,
-        DUMMY_SP,
-        E0601,
-        "`main` function not found in crate `{}`",
-        tcx.crate_name(LOCAL_CRATE)
-    );
-    let filename = &tcx.sess.local_crate_source_file;
-    let note = if !visitor.non_main_fns.is_empty() {
-        for &span in &visitor.non_main_fns {
-            err.span_note(span, "here is a function named `main`");
-        }
-        err.note("you have one or more functions named `main` not defined at the crate level");
-        err.help("consider moving the `main` function definitions");
-        // There were some functions named `main` though. Try to give the user a hint.
-        format!(
-            "the main function must be defined at the crate level{}",
-            filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default()
-        )
-    } else if let Some(filename) = filename {
-        format!("consider adding a `main` function to `{}`", filename.display())
-    } else {
-        String::from("consider adding a `main` function at the crate level")
-    };
+    let mut has_filename = true;
+    let filename = tcx.sess.local_crate_source_file.clone().unwrap_or_else(|| {
+        has_filename = false;
+        Default::default()
+    });
+    let main_def_opt = tcx.resolutions(()).main_def;
+    let diagnostic_id = error_code!(E0601);
+    let add_teach_note = tcx.sess.teach(&diagnostic_id);
     // The file may be empty, which leads to the diagnostic machinery not emitting this
     // note. This is a relatively simple way to detect that case and emit a span-less
     // note instead.
-    if tcx.sess.source_map().lookup_line(sp.hi()).is_ok() {
-        err.set_span(sp.shrink_to_hi());
-        err.span_label(sp.shrink_to_hi(), &note);
-    } else {
-        err.note(&note);
-    }
-
-    if let Some(main_def) = tcx.resolutions(()).main_def && main_def.opt_fn_def_id().is_none(){
-        // There is something at `crate::main`, but it is not a function definition.
-        err.span_label(main_def.span, "non-function item at `crate::main` is found");
-    }
-
-    if tcx.sess.teach(&err.get_code().unwrap()) {
-        err.note(
-            "If you don't know the basics of Rust, you can go look to the Rust Book \
-                  to get started: https://doc.rust-lang.org/book/",
-        );
-    }
-    err.emit();
+    let file_empty = !tcx.sess.source_map().lookup_line(sp.hi()).is_ok();
+
+    tcx.sess.emit_err(NoMainErr {
+        sp,
+        crate_name: tcx.crate_name(LOCAL_CRATE),
+        has_filename,
+        filename,
+        file_empty,
+        non_main_fns: visitor.non_main_fns.clone(),
+        main_def_opt,
+        add_teach_note,
+    });
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index cc231af71a274..1cc81a9ab9884 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1,6 +1,16 @@
-use rustc_errors::{Applicability, MultiSpan};
+use std::{
+    io::Error,
+    path::{Path, PathBuf},
+};
+
+use rustc_ast::Label;
+use rustc_errors::{error_code, Applicability, ErrorGuaranteed, IntoDiagnostic, MultiSpan};
+use rustc_hir::{self as hir, ExprKind, Target};
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
-use rustc_span::{Span, Symbol};
+use rustc_middle::ty::{MainDefinition, Ty};
+use rustc_span::{Span, Symbol, DUMMY_SP};
+
+use crate::lang_items::Duplicate;
 
 #[derive(LintDiagnostic)]
 #[diag(passes::outer_crate_level_attr)]
@@ -526,6 +536,15 @@ pub struct DebugVisualizerInvalid {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(passes::debug_visualizer_unreadable)]
+pub struct DebugVisualizerUnreadable<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub file: &'a Path,
+    pub error: Error,
+}
+
 #[derive(Diagnostic)]
 #[diag(passes::rustc_allow_const_fn_unstable)]
 pub struct RustcAllowConstFnUnstable {
@@ -665,3 +684,765 @@ pub struct DeprecatedAnnotationHasNoEffect {
     #[suggestion(applicability = "machine-applicable", code = "")]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(passes::unknown_external_lang_item, code = "E0264")]
+pub struct UnknownExternLangItem {
+    #[primary_span]
+    pub span: Span,
+    pub lang_item: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_panic_handler)]
+pub struct MissingPanicHandler;
+
+#[derive(Diagnostic)]
+#[diag(passes::alloc_func_required)]
+pub struct AllocFuncRequired;
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_alloc_error_handler)]
+pub struct MissingAllocErrorHandler;
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_lang_item)]
+#[note]
+#[help]
+pub struct MissingLangItem {
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::lang_item_on_incorrect_target, code = "E0718")]
+pub struct LangItemOnIncorrectTarget {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub name: Symbol,
+    pub expected_target: Target,
+    pub actual_target: Target,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unknown_lang_item, code = "E0522")]
+pub struct UnknownLangItem {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+pub struct InvalidAttrAtCrateLevel {
+    pub span: Span,
+    pub snippet: Option<String>,
+    pub name: Symbol,
+}
+
+impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
+    fn into_diagnostic(
+        self,
+        handler: &'_ rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag =
+            handler.struct_err(rustc_errors::fluent::passes::invalid_attr_at_crate_level);
+        diag.set_span(self.span);
+        diag.set_arg("name", self.name);
+        // Only emit an error with a suggestion if we can create a string out
+        // of the attribute span
+        if let Some(src) = self.snippet {
+            let replacement = src.replace("#!", "#");
+            diag.span_suggestion_verbose(
+                self.span,
+                rustc_errors::fluent::passes::suggestion,
+                replacement,
+                rustc_errors::Applicability::MachineApplicable,
+            );
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::duplicate_diagnostic_item)]
+pub struct DuplicateDiagnosticItem {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::duplicate_diagnostic_item_in_crate)]
+pub struct DuplicateDiagnosticItemInCrate {
+    #[note(passes::diagnostic_item_first_defined)]
+    pub span: Option<Span>,
+    pub orig_crate_name: Symbol,
+    #[note]
+    pub have_orig_crate_name: Option<()>,
+    pub crate_name: Symbol,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::abi)]
+pub struct Abi {
+    #[primary_span]
+    pub span: Span,
+    pub abi: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::align)]
+pub struct Align {
+    #[primary_span]
+    pub span: Span,
+    pub align: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::size)]
+pub struct Size {
+    #[primary_span]
+    pub span: Span,
+    pub size: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::homogeneous_aggregate)]
+pub struct HomogeneousAggregate {
+    #[primary_span]
+    pub span: Span,
+    pub homogeneous_aggregate: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::layout_of)]
+pub struct LayoutOf {
+    #[primary_span]
+    pub span: Span,
+    pub normalized_ty: String,
+    pub ty_layout: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unrecognized_field)]
+pub struct UnrecognizedField {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::feature_stable_twice, code = "E0711")]
+pub struct FeatureStableTwice {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+    pub since: Symbol,
+    pub prev_since: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::feature_previously_declared, code = "E0711")]
+pub struct FeaturePreviouslyDeclared<'a, 'b> {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+    pub declared: &'a str,
+    pub prev_declared: &'b str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::expr_not_allowed_in_context, code = "E0744")]
+pub struct ExprNotAllowedInContext<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub expr: String,
+    pub context: &'a str,
+}
+
+pub struct BreakNonLoop<'a> {
+    pub span: Span,
+    pub head: Option<Span>,
+    pub kind: &'a str,
+    pub suggestion: String,
+    pub loop_label: Option<Label>,
+    pub break_label: Option<Label>,
+    pub break_expr_kind: &'a ExprKind<'a>,
+    pub break_expr_span: Span,
+}
+
+impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> {
+    fn into_diagnostic(
+        self,
+        handler: &rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = handler.struct_span_err_with_code(
+            self.span,
+            rustc_errors::fluent::passes::break_non_loop,
+            error_code!(E0571),
+        );
+        diag.set_arg("kind", self.kind);
+        diag.span_label(self.span, rustc_errors::fluent::passes::label);
+        if let Some(head) = self.head {
+            diag.span_label(head, rustc_errors::fluent::passes::label2);
+        }
+        diag.span_suggestion(
+            self.span,
+            rustc_errors::fluent::passes::suggestion,
+            self.suggestion,
+            Applicability::MaybeIncorrect,
+        );
+        if let (Some(label), None) = (self.loop_label, self.break_label) {
+            match self.break_expr_kind {
+                ExprKind::Path(hir::QPath::Resolved(
+                    None,
+                    hir::Path { segments: [segment], res: hir::def::Res::Err, .. },
+                )) if label.ident.to_string() == format!("'{}", segment.ident) => {
+                    // This error is redundant, we will have already emitted a
+                    // suggestion to use the label when `segment` wasn't found
+                    // (hence the `Res::Err` check).
+                    diag.delay_as_bug();
+                }
+                _ => {
+                    diag.span_suggestion(
+                        self.break_expr_span,
+                        rustc_errors::fluent::passes::break_expr_suggestion,
+                        label.ident,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::continue_labeled_block, code = "E0696")]
+pub struct ContinueLabeledBlock {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::block_label)]
+    pub block_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::break_inside_closure, code = "E0267")]
+pub struct BreakInsideClosure<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::closure_label)]
+    pub closure_span: Span,
+    pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::break_inside_async_block, code = "E0267")]
+pub struct BreakInsideAsyncBlock<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::async_block_label)]
+    pub closure_span: Span,
+    pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::outside_loop, code = "E0268")]
+pub struct OutsideLoop<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unlabeled_in_labeled_block, code = "E0695")]
+pub struct UnlabeledInLabeledBlock<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub cf_type: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unlabeled_cf_in_while_condition, code = "E0590")]
+pub struct UnlabeledCfInWhileCondition<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub cf_type: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::cannot_inline_naked_function)]
+pub struct CannotInlineNakedFunction {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes::undefined_naked_function_abi)]
+pub struct UndefinedNakedFunctionAbi;
+
+#[derive(Diagnostic)]
+#[diag(passes::no_patterns)]
+pub struct NoPatterns {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::params_not_allowed)]
+#[help]
+pub struct ParamsNotAllowed {
+    #[primary_span]
+    pub span: Span,
+}
+
+pub struct NakedFunctionsAsmBlock {
+    pub span: Span,
+    pub multiple_asms: Vec<Span>,
+    pub non_asms: Vec<Span>,
+}
+
+impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock {
+    fn into_diagnostic(
+        self,
+        handler: &rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = handler.struct_span_err_with_code(
+            self.span,
+            rustc_errors::fluent::passes::naked_functions_asm_block,
+            error_code!(E0787),
+        );
+        for span in self.multiple_asms.iter() {
+            diag.span_label(*span, rustc_errors::fluent::passes::label_multiple_asm);
+        }
+        for span in self.non_asms.iter() {
+            diag.span_label(*span, rustc_errors::fluent::passes::label_non_asm);
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::naked_functions_operands, code = "E0787")]
+pub struct NakedFunctionsOperands {
+    #[primary_span]
+    pub unsupported_operands: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::naked_functions_asm_options, code = "E0787")]
+pub struct NakedFunctionsAsmOptions {
+    #[primary_span]
+    pub span: Span,
+    pub unsupported_options: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::naked_functions_must_use_noreturn, code = "E0787")]
+pub struct NakedFunctionsMustUseNoreturn {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")]
+    pub last_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::attr_only_on_main)]
+pub struct AttrOnlyOnMain {
+    #[primary_span]
+    pub span: Span,
+    pub attr: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::attr_only_on_root_main)]
+pub struct AttrOnlyOnRootMain {
+    #[primary_span]
+    pub span: Span,
+    pub attr: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::attr_only_in_functions)]
+pub struct AttrOnlyInFunctions {
+    #[primary_span]
+    pub span: Span,
+    pub attr: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::multiple_rustc_main, code = "E0137")]
+pub struct MultipleRustcMain {
+    #[primary_span]
+    pub span: Span,
+    #[label(passes::first)]
+    pub first: Span,
+    #[label(passes::additional)]
+    pub additional: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::multiple_start_functions, code = "E0138")]
+pub struct MultipleStartFunctions {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub labeled: Span,
+    #[label(passes::previous)]
+    pub previous: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::extern_main)]
+pub struct ExternMain {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unix_sigpipe_values)]
+pub struct UnixSigpipeValues {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::no_main_function, code = "E0601")]
+pub struct NoMainFunction {
+    #[primary_span]
+    pub span: Span,
+    pub crate_name: String,
+}
+
+pub struct NoMainErr {
+    pub sp: Span,
+    pub crate_name: Symbol,
+    pub has_filename: bool,
+    pub filename: PathBuf,
+    pub file_empty: bool,
+    pub non_main_fns: Vec<Span>,
+    pub main_def_opt: Option<MainDefinition>,
+    pub add_teach_note: bool,
+}
+
+impl<'a> IntoDiagnostic<'a> for NoMainErr {
+    fn into_diagnostic(
+        self,
+        handler: &'a rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let mut diag = handler.struct_span_err_with_code(
+            DUMMY_SP,
+            rustc_errors::fluent::passes::no_main_function,
+            error_code!(E0601),
+        );
+        diag.set_arg("crate_name", self.crate_name);
+        diag.set_arg("filename", self.filename);
+        diag.set_arg("has_filename", self.has_filename);
+        let note = if !self.non_main_fns.is_empty() {
+            for &span in &self.non_main_fns {
+                diag.span_note(span, rustc_errors::fluent::passes::here_is_main);
+            }
+            diag.note(rustc_errors::fluent::passes::one_or_more_possible_main);
+            diag.help(rustc_errors::fluent::passes::consider_moving_main);
+            // There were some functions named `main` though. Try to give the user a hint.
+            rustc_errors::fluent::passes::main_must_be_defined_at_crate
+        } else if self.has_filename {
+            rustc_errors::fluent::passes::consider_adding_main_to_file
+        } else {
+            rustc_errors::fluent::passes::consider_adding_main_at_crate
+        };
+        if self.file_empty {
+            diag.note(note);
+        } else {
+            diag.set_span(self.sp.shrink_to_hi());
+            diag.span_label(self.sp.shrink_to_hi(), note);
+        }
+
+        if let Some(main_def) = self.main_def_opt && main_def.opt_fn_def_id().is_none(){
+            // There is something at `crate::main`, but it is not a function definition.
+            diag.span_label(main_def.span, rustc_errors::fluent::passes::non_function_main);
+        }
+
+        if self.add_teach_note {
+            diag.note(rustc_errors::fluent::passes::teach_note);
+        }
+        diag
+    }
+}
+
+pub struct DuplicateLangItem {
+    pub local_span: Option<Span>,
+    pub lang_item_name: Symbol,
+    pub crate_name: Symbol,
+    pub dependency_of: Symbol,
+    pub is_local: bool,
+    pub path: String,
+    pub first_defined_span: Option<Span>,
+    pub orig_crate_name: Symbol,
+    pub orig_dependency_of: Symbol,
+    pub orig_is_local: bool,
+    pub orig_path: String,
+    pub(crate) duplicate: Duplicate,
+}
+
+impl IntoDiagnostic<'_> for DuplicateLangItem {
+    fn into_diagnostic(
+        self,
+        handler: &rustc_errors::Handler,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = handler.struct_err_with_code(
+            match self.duplicate {
+                Duplicate::Plain => rustc_errors::fluent::passes::duplicate_lang_item,
+
+                Duplicate::Crate => rustc_errors::fluent::passes::duplicate_lang_item_crate,
+                Duplicate::CrateDepends => {
+                    rustc_errors::fluent::passes::duplicate_lang_item_crate_depends
+                }
+            },
+            error_code!(E0152),
+        );
+        diag.set_arg("lang_item_name", self.lang_item_name);
+        diag.set_arg("crate_name", self.crate_name);
+        diag.set_arg("dependency_of", self.dependency_of);
+        diag.set_arg("path", self.path);
+        diag.set_arg("orig_crate_name", self.orig_crate_name);
+        diag.set_arg("orig_dependency_of", self.orig_dependency_of);
+        diag.set_arg("orig_path", self.orig_path);
+        if let Some(span) = self.local_span {
+            diag.set_span(span);
+        }
+        if let Some(span) = self.first_defined_span {
+            diag.span_note(span, rustc_errors::fluent::passes::first_defined_span);
+        } else {
+            if self.orig_dependency_of.is_empty() {
+                diag.note(rustc_errors::fluent::passes::first_defined_crate);
+            } else {
+                diag.note(rustc_errors::fluent::passes::first_defined_crate_depends);
+            }
+
+            if self.orig_is_local {
+                diag.note(rustc_errors::fluent::passes::first_definition_local);
+            } else {
+                diag.note(rustc_errors::fluent::passes::first_definition_path);
+            }
+
+            if self.is_local {
+                diag.note(rustc_errors::fluent::passes::second_definition_local);
+            } else {
+                diag.note(rustc_errors::fluent::passes::second_definition_path);
+            }
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::incorrect_target, code = "E0718")]
+pub struct IncorrectTarget<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub generics_span: Span,
+    pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn`
+    pub kind: &'static str,
+    pub num: usize,
+    pub actual_num: usize,
+    pub at_least: bool,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes::useless_assignment)]
+pub struct UselessAssignment<'a> {
+    pub is_field_assign: bool,
+    pub ty: Ty<'a>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes::only_has_effect_on)]
+pub struct OnlyHasEffectOn {
+    pub attr_name: Symbol,
+    pub target_name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::object_lifetime_err)]
+pub struct ObjectLifetimeErr {
+    #[primary_span]
+    pub span: Span,
+    pub repr: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unrecognized_repr_hint, code = "E0552")]
+#[help]
+pub struct UnrecognizedReprHint {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+pub enum AttrApplication {
+    #[diag(passes::attr_application_enum, code = "E0517")]
+    Enum {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct, code = "E0517")]
+    Struct {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct_union, code = "E0517")]
+    StructUnion {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct_enum_union, code = "E0517")]
+    StructEnumUnion {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+    #[diag(passes::attr_application_struct_enum_function_union, code = "E0517")]
+    StructEnumFunctionUnion {
+        #[primary_span]
+        hint_span: Span,
+        #[label]
+        span: Span,
+    },
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::transparent_incompatible, code = "E0692")]
+pub struct TransparentIncompatible {
+    #[primary_span]
+    pub hint_spans: Vec<Span>,
+    pub target: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::deprecated_attribute, code = "E0549")]
+pub struct DeprecatedAttribute {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::useless_stability)]
+pub struct UselessStability {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::invalid_stability)]
+pub struct InvalidStability {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::cannot_stabilize_deprecated)]
+pub struct CannotStabilizeDeprecated {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::invalid_deprecation_version)]
+pub struct InvalidDeprecationVersion {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(passes::item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_stability_attr)]
+pub struct MissingStabilityAttr<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub descr: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::missing_const_stab_attr)]
+pub struct MissingConstStabAttr<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub descr: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::trait_impl_const_stable)]
+#[note]
+pub struct TraitImplConstStable {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::feature_only_on_nightly, code = "E0554")]
+pub struct FeatureOnlyOnNightly {
+    #[primary_span]
+    pub span: Span,
+    pub release_channel: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::unknown_feature, code = "E0635")]
+pub struct UnknownFeature {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::implied_feature_not_exist)]
+pub struct ImpliedFeatureNotExist {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+    pub implied_by: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes::duplicate_feature_err, code = "E0636")]
+pub struct DuplicateFeatureErr {
+    #[primary_span]
+    pub span: Span,
+    pub feature: Symbol,
+}
+#[derive(Diagnostic)]
+#[diag(passes::missing_const_err)]
+pub struct MissingConstErr {
+    #[primary_span]
+    #[help]
+    pub fn_sig_span: Span,
+    #[label]
+    pub const_span: Span,
+}
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 79900a90aed63..71b0735192ac4 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -8,9 +8,11 @@
 //! * Functions called by the compiler itself.
 
 use crate::check_attr::target_from_impl_item;
+use crate::errors::{
+    DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem,
+};
 use crate::weak_lang_items;
 
-use rustc_errors::{pluralize, struct_span_err};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -18,10 +20,16 @@ use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS};
 use rustc_hir::{HirId, LangItem, LanguageItems, Target};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::cstore::ExternCrate;
-use rustc_span::Span;
+use rustc_span::{symbol::kw::Empty, Span};
 
 use rustc_middle::ty::query::Providers;
 
+pub(crate) enum Duplicate {
+    Plain,
+    Crate,
+    CrateDepends,
+}
+
 struct LanguageItemCollector<'tcx> {
     items: LanguageItems,
     tcx: TyCtxt<'tcx>,
@@ -34,42 +42,24 @@ impl<'tcx> LanguageItemCollector<'tcx> {
 
     fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) {
         let attrs = self.tcx.hir().attrs(hir_id);
-        if let Some((value, span)) = extract(&attrs) {
-            match ITEM_REFS.get(&value).cloned() {
+        if let Some((name, span)) = extract(&attrs) {
+            match ITEM_REFS.get(&name).cloned() {
                 // Known lang item with attribute on correct target.
                 Some((item_index, expected_target)) if actual_target == expected_target => {
                     self.collect_item_extended(item_index, hir_id, span);
                 }
                 // Known lang item with attribute on incorrect target.
                 Some((_, expected_target)) => {
-                    struct_span_err!(
-                        self.tcx.sess,
+                    self.tcx.sess.emit_err(LangItemOnIncorrectTarget {
                         span,
-                        E0718,
-                        "`{}` language item must be applied to a {}",
-                        value,
+                        name,
                         expected_target,
-                    )
-                    .span_label(
-                        span,
-                        format!(
-                            "attribute should be applied to a {}, not a {}",
-                            expected_target, actual_target,
-                        ),
-                    )
-                    .emit();
+                        actual_target,
+                    });
                 }
                 // Unknown lang item.
                 _ => {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0522,
-                        "definition of an unknown language item: `{}`",
-                        value
-                    )
-                    .span_label(span, format!("definition of unknown language item `{}`", value))
-                    .emit();
+                    self.tcx.sess.emit_err(UnknownLangItem { span, name });
                 }
             }
         }
@@ -79,74 +69,72 @@ impl<'tcx> LanguageItemCollector<'tcx> {
         // Check for duplicates.
         if let Some(original_def_id) = self.items.items[item_index] {
             if original_def_id != item_def_id {
-                let lang_item = LangItem::from_u32(item_index as u32).unwrap();
-                let name = lang_item.name();
-                let mut err = match self.tcx.hir().span_if_local(item_def_id) {
-                    Some(span) => struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0152,
-                        "found duplicate lang item `{}`",
-                        name
-                    ),
-                    None => match self.tcx.extern_crate(item_def_id) {
-                        Some(ExternCrate { dependency_of, .. }) => {
-                            self.tcx.sess.struct_err(&format!(
-                                "duplicate lang item in crate `{}` (which `{}` depends on): `{}`.",
-                                self.tcx.crate_name(item_def_id.krate),
-                                self.tcx.crate_name(*dependency_of),
-                                name
-                            ))
-                        }
-                        _ => self.tcx.sess.struct_err(&format!(
-                            "duplicate lang item in crate `{}`: `{}`.",
-                            self.tcx.crate_name(item_def_id.krate),
-                            name
-                        )),
-                    },
+                let local_span = self.tcx.hir().span_if_local(item_def_id);
+                let lang_item_name = LangItem::from_u32(item_index as u32).unwrap().name();
+                let crate_name = self.tcx.crate_name(item_def_id.krate);
+                let mut dependency_of = Empty;
+                let is_local = item_def_id.is_local();
+                let path = if is_local {
+                    String::new()
+                } else {
+                    self.tcx
+                        .crate_extern_paths(item_def_id.krate)
+                        .iter()
+                        .map(|p| p.display().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                        .into()
                 };
-                if let Some(span) = self.tcx.hir().span_if_local(original_def_id) {
-                    err.span_note(span, "the lang item is first defined here");
+                let first_defined_span = self.tcx.hir().span_if_local(original_def_id);
+                let mut orig_crate_name = Empty;
+                let mut orig_dependency_of = Empty;
+                let orig_is_local = original_def_id.is_local();
+                let orig_path = if orig_is_local {
+                    String::new()
                 } else {
-                    match self.tcx.extern_crate(original_def_id) {
-                        Some(ExternCrate { dependency_of, .. }) => {
-                            err.note(&format!(
-                                "the lang item is first defined in crate `{}` (which `{}` depends on)",
-                                self.tcx.crate_name(original_def_id.krate),
-                                self.tcx.crate_name(*dependency_of)
-                            ));
-                        }
-                        _ => {
-                            err.note(&format!(
-                                "the lang item is first defined in crate `{}`.",
-                                self.tcx.crate_name(original_def_id.krate)
-                            ));
-                        }
+                    self.tcx
+                        .crate_extern_paths(original_def_id.krate)
+                        .iter()
+                        .map(|p| p.display().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                        .into()
+                };
+                if first_defined_span.is_none() {
+                    orig_crate_name = self.tcx.crate_name(original_def_id.krate);
+                    if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
+                        self.tcx.extern_crate(original_def_id)
+                    {
+                        orig_dependency_of = self.tcx.crate_name(*inner_dependency_of);
                     }
-                    let mut note_def = |which, def_id: DefId| {
-                        let crate_name = self.tcx.crate_name(def_id.krate);
-                        let note = if def_id.is_local() {
-                            format!("{} definition in the local crate (`{}`)", which, crate_name)
-                        } else {
-                            let paths: Vec<_> = self
-                                .tcx
-                                .crate_extern_paths(def_id.krate)
-                                .iter()
-                                .map(|p| p.display().to_string())
-                                .collect();
-                            format!(
-                                "{} definition in `{}` loaded from {}",
-                                which,
-                                crate_name,
-                                paths.join(", ")
-                            )
-                        };
-                        err.note(&note);
-                    };
-                    note_def("first", original_def_id);
-                    note_def("second", item_def_id);
                 }
-                err.emit();
+
+                let duplicate = if local_span.is_some() {
+                    Duplicate::Plain
+                } else {
+                    match self.tcx.extern_crate(item_def_id) {
+                        Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
+                            dependency_of = self.tcx.crate_name(*inner_dependency_of);
+                            Duplicate::CrateDepends
+                        }
+                        _ => Duplicate::Crate,
+                    }
+                };
+
+                self.tcx.sess.emit_err(DuplicateLangItem {
+                    local_span,
+                    lang_item_name,
+                    crate_name,
+                    dependency_of,
+                    is_local,
+                    path,
+                    first_defined_span,
+                    orig_crate_name,
+                    orig_dependency_of,
+                    orig_is_local,
+                    orig_path,
+                    duplicate,
+                });
             }
         }
 
@@ -179,41 +167,30 @@ impl<'tcx> LanguageItemCollector<'tcx> {
                 None => (0, *item_span),
             };
 
+            let mut at_least = false;
             let required = match lang_item.required_generics() {
-                GenericRequirement::Exact(num) if num != actual_num => {
-                    Some((format!("{}", num), pluralize!(num)))
-                }
+                GenericRequirement::Exact(num) if num != actual_num => Some(num),
                 GenericRequirement::Minimum(num) if actual_num < num => {
-                    Some((format!("at least {}", num), pluralize!(num)))
-                }
+                    at_least = true;
+                    Some(num)}
+                ,
                 // If the number matches, or there is no requirement, handle it normally
                 _ => None,
             };
 
-            if let Some((range_str, pluralized)) = required {
+            if let Some(num) = required {
                 // We are issuing E0718 "incorrect target" here, because while the
                 // item kind of the target is correct, the target is still wrong
                 // because of the wrong number of generic arguments.
-                struct_span_err!(
-                    self.tcx.sess,
+                self.tcx.sess.emit_err(IncorrectTarget {
                     span,
-                    E0718,
-                    "`{}` language item must be applied to a {} with {} generic argument{}",
-                    name,
-                    kind.descr(),
-                    range_str,
-                    pluralized,
-                )
-                .span_label(
                     generics_span,
-                    format!(
-                        "this {} has {} generic argument{}",
-                        kind.descr(),
-                        actual_num,
-                        pluralize!(actual_num),
-                    ),
-                )
-                .emit();
+                    name: name.as_str(),
+                    kind: kind.descr(),
+                    num,
+                    actual_num,
+                    at_least,
+                });
 
                 // return early to not collect the lang item
                 return;
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 46c4a702fde95..c1085094962a7 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -3,10 +3,13 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::abi::{HasDataLayout, TargetDataLayout};
 
+use crate::errors::{Abi, Align, HomogeneousAggregate, LayoutOf, Size, UnrecognizedField};
+
 pub fn test_layout(tcx: TyCtxt<'_>) {
     if tcx.features().rustc_attrs {
         // if the `rustc_attrs` feature is not enabled, don't bother testing layout
@@ -35,62 +38,64 @@ fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attri
             for meta_item in meta_items {
                 match meta_item.name_or_empty() {
                     sym::abi => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("abi: {:?}", ty_layout.abi),
-                        );
+                        tcx.sess.emit_err(Abi {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            abi: format!("{:?}", ty_layout.abi),
+                        });
                     }
 
                     sym::align => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("align: {:?}", ty_layout.align),
-                        );
+                        tcx.sess.emit_err(Align {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            align: format!("{:?}", ty_layout.align),
+                        });
                     }
 
                     sym::size => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("size: {:?}", ty_layout.size),
-                        );
+                        tcx.sess.emit_err(Size {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            size: format!("{:?}", ty_layout.size),
+                        });
                     }
 
                     sym::homogeneous_aggregate => {
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!(
-                                "homogeneous_aggregate: {:?}",
-                                ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
+                        tcx.sess.emit_err(HomogeneousAggregate {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            homogeneous_aggregate: format!(
+                                "{:?}",
+                                ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env })
                             ),
-                        );
+                        });
                     }
 
                     sym::debug => {
-                        let normalized_ty = tcx.normalize_erasing_regions(
-                            param_env.with_reveal_all_normalized(tcx),
-                            ty,
-                        );
-                        tcx.sess.span_err(
-                            tcx.def_span(item_def_id.to_def_id()),
-                            &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout),
+                        let normalized_ty = format!(
+                            "{:?}",
+                            tcx.normalize_erasing_regions(
+                                param_env.with_reveal_all_normalized(tcx),
+                                ty,
+                            )
                         );
+                        let ty_layout = format!("{:#?}", *ty_layout);
+                        tcx.sess.emit_err(LayoutOf {
+                            span: tcx.def_span(item_def_id.to_def_id()),
+                            normalized_ty,
+                            ty_layout,
+                        });
                     }
 
                     name => {
-                        tcx.sess.span_err(
-                            meta_item.span(),
-                            &format!("unrecognized field name `{}`", name),
-                        );
+                        tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name });
                     }
                 }
             }
         }
 
         Err(layout_error) => {
-            tcx.sess.span_err(
-                tcx.def_span(item_def_id.to_def_id()),
-                &format!("layout error: {:?}", layout_error),
-            );
+            tcx.sess.emit_fatal(Spanned {
+                node: layout_error,
+                span: tcx.def_span(item_def_id.to_def_id()),
+            });
         }
     }
 }
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 6e621b7eb5eb0..15f60f626c89a 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -5,6 +5,8 @@
 //! This API is completely unstable and subject to change.
 
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 04173c792a979..b5843c0ae488b 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -6,7 +6,6 @@
 
 use rustc_ast::{Attribute, MetaItemKind};
 use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER};
-use rustc_errors::struct_span_err;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::lib_features::LibFeatures;
@@ -15,6 +14,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::Symbol;
 use rustc_span::{sym, Span};
 
+use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice};
+
 fn new_lib_features() -> LibFeatures {
     LibFeatures { stable: Default::default(), unstable: Default::default() }
 }
@@ -92,14 +93,12 @@ impl<'tcx> LibFeatureCollector<'tcx> {
             (Some(since), _, false) => {
                 if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) {
                     if *prev_since != since {
-                        self.span_feature_error(
+                        self.tcx.sess.emit_err(FeatureStableTwice {
                             span,
-                            &format!(
-                                "feature `{}` is declared stable since {}, \
-                                 but was previously declared stable since {}",
-                                feature, since, prev_since,
-                            ),
-                        );
+                            feature,
+                            since,
+                            prev_since: *prev_since,
+                        });
                         return;
                     }
                 }
@@ -110,22 +109,17 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                 self.lib_features.unstable.insert(feature, span);
             }
             (Some(_), _, true) | (None, true, _) => {
-                self.span_feature_error(
+                let declared = if since.is_some() { "stable" } else { "unstable" };
+                let prev_declared = if since.is_none() { "stable" } else { "unstable" };
+                self.tcx.sess.emit_err(FeaturePreviouslyDeclared {
                     span,
-                    &format!(
-                        "feature `{}` is declared {}, but was previously declared {}",
-                        feature,
-                        if since.is_some() { "stable" } else { "unstable" },
-                        if since.is_none() { "stable" } else { "unstable" },
-                    ),
-                );
+                    feature,
+                    declared,
+                    prev_declared,
+                });
             }
         }
     }
-
-    fn span_feature_error(&self, span: Span, msg: &str) {
-        struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg).emit();
-    }
 }
 
 impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> {
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index cdda0e388ddf0..077194ec687bf 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -1,6 +1,5 @@
 use Context::*;
 
-use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -13,6 +12,11 @@ use rustc_session::Session;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::Span;
 
+use crate::errors::{
+    BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
+    UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
+};
+
 #[derive(Clone, Copy, Debug, PartialEq)]
 enum Context {
     Normal,
@@ -90,7 +94,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     Ok(loop_id) => Some(loop_id),
                     Err(hir::LoopIdError::OutsideLoopScope) => None,
                     Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
-                        self.emit_unlabled_cf_in_while_condition(e.span, "break");
+                        self.sess.emit_err(UnlabeledCfInWhileCondition {
+                            span: e.span,
+                            cf_type: "break",
+                        });
                         None
                     }
                     Err(hir::LoopIdError::UnresolvedLabel) => None,
@@ -116,69 +123,22 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     match loop_kind {
                         None | Some(hir::LoopSource::Loop) => (),
                         Some(kind) => {
-                            let mut err = struct_span_err!(
-                                self.sess,
-                                e.span,
-                                E0571,
-                                "`break` with value from a `{}` loop",
-                                kind.name()
-                            );
-                            err.span_label(
-                                e.span,
-                                "can only break with a value inside `loop` or breakable block",
+                            let suggestion = format!(
+                                "break{}",
+                                break_label
+                                    .label
+                                    .map_or_else(String::new, |l| format!(" {}", l.ident))
                             );
-                            if let Some(head) = head {
-                                err.span_label(
-                                    head,
-                                    &format!(
-                                        "you can't `break` with a value in a `{}` loop",
-                                        kind.name()
-                                    ),
-                                );
-                            }
-                            err.span_suggestion(
-                                e.span,
-                                &format!(
-                                    "use `break` on its own without a value inside this `{}` loop",
-                                    kind.name(),
-                                ),
-                                format!(
-                                    "break{}",
-                                    break_label
-                                        .label
-                                        .map_or_else(String::new, |l| format!(" {}", l.ident))
-                                ),
-                                Applicability::MaybeIncorrect,
-                            );
-                            if let (Some(label), None) = (loop_label, break_label.label) {
-                                match break_expr.kind {
-                                    hir::ExprKind::Path(hir::QPath::Resolved(
-                                        None,
-                                        hir::Path {
-                                            segments: [segment],
-                                            res: hir::def::Res::Err,
-                                            ..
-                                        },
-                                    )) if label.ident.to_string()
-                                        == format!("'{}", segment.ident) =>
-                                    {
-                                        // This error is redundant, we will have already emitted a
-                                        // suggestion to use the label when `segment` wasn't found
-                                        // (hence the `Res::Err` check).
-                                        err.delay_as_bug();
-                                    }
-                                    _ => {
-                                        err.span_suggestion(
-                                            break_expr.span,
-                                            "alternatively, you might have meant to use the \
-                                             available loop label",
-                                            label.ident,
-                                            Applicability::MaybeIncorrect,
-                                        );
-                                    }
-                                }
-                            }
-                            err.emit();
+                            self.sess.emit_err(BreakNonLoop {
+                                span: e.span,
+                                head,
+                                kind: kind.name(),
+                                suggestion,
+                                loop_label,
+                                break_label: break_label.label,
+                                break_expr_kind: &break_expr.kind,
+                                break_expr_span: break_expr.span,
+                            });
                         }
                     }
                 }
@@ -191,19 +151,17 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                 match destination.target_id {
                     Ok(loop_id) => {
                         if let Node::Block(block) = self.hir_map.find(loop_id).unwrap() {
-                            struct_span_err!(
-                                self.sess,
-                                e.span,
-                                E0696,
-                                "`continue` pointing to a labeled block"
-                            )
-                            .span_label(e.span, "labeled blocks cannot be `continue`'d")
-                            .span_label(block.span, "labeled block the `continue` points to")
-                            .emit();
+                            self.sess.emit_err(ContinueLabeledBlock {
+                                span: e.span,
+                                block_span: block.span,
+                            });
                         }
                     }
                     Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
-                        self.emit_unlabled_cf_in_while_condition(e.span, "continue");
+                        self.sess.emit_err(UnlabeledCfInWhileCondition {
+                            span: e.span,
+                            cf_type: "continue",
+                        });
                     }
                     Err(_) => {}
                 }
@@ -226,21 +184,16 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
     }
 
     fn require_break_cx(&self, name: &str, span: Span) {
-        let err_inside_of = |article, ty, closure_span| {
-            struct_span_err!(self.sess, span, E0267, "`{}` inside of {} {}", name, article, ty)
-                .span_label(span, format!("cannot `{}` inside of {} {}", name, article, ty))
-                .span_label(closure_span, &format!("enclosing {}", ty))
-                .emit();
-        };
-
         match self.cx {
             LabeledBlock | Loop(_) => {}
-            Closure(closure_span) => err_inside_of("a", "closure", closure_span),
-            AsyncClosure(closure_span) => err_inside_of("an", "`async` block", closure_span),
+            Closure(closure_span) => {
+                self.sess.emit_err(BreakInsideClosure { span, closure_span, name });
+            }
+            AsyncClosure(closure_span) => {
+                self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name });
+            }
             Normal | AnonConst => {
-                struct_span_err!(self.sess, span, E0268, "`{}` outside of a loop", name)
-                    .span_label(span, format!("cannot `{}` outside of a loop", name))
-                    .emit();
+                self.sess.emit_err(OutsideLoop { span, name });
             }
         }
     }
@@ -251,37 +204,13 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
         label: &Destination,
         cf_type: &str,
     ) -> bool {
-        if !span.is_desugaring(DesugaringKind::QuestionMark) && self.cx == LabeledBlock {
-            if label.label.is_none() {
-                struct_span_err!(
-                    self.sess,
-                    span,
-                    E0695,
-                    "unlabeled `{}` inside of a labeled block",
-                    cf_type
-                )
-                .span_label(
-                    span,
-                    format!(
-                        "`{}` statements that would diverge to or through \
-                                a labeled block need to bear a label",
-                        cf_type
-                    ),
-                )
-                .emit();
-                return true;
-            }
+        if !span.is_desugaring(DesugaringKind::QuestionMark)
+            && self.cx == LabeledBlock
+            && label.label.is_none()
+        {
+            self.sess.emit_err(UnlabeledInLabeledBlock { span, cf_type });
+            return true;
         }
         false
     }
-    fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) {
-        struct_span_err!(
-            self.sess,
-            span,
-            E0590,
-            "`break` or `continue` with no label in the condition of a `while` loop"
-        )
-        .span_label(span, format!("unlabeled `{}` in the condition of a `while` loop", cf_type))
-        .emit();
-    }
 }
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index 2690be66c21e5..acc54e7e11006 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -1,7 +1,6 @@
 //! Checks validity of naked functions.
 
 use rustc_ast::InlineAsmOptions;
-use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
@@ -14,6 +13,12 @@ use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
+use crate::errors::{
+    CannotInlineNakedFunction, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions,
+    NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed,
+    UndefinedNakedFunctionAbi,
+};
+
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers { check_mod_naked_functions, ..*providers };
 }
@@ -56,7 +61,7 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
 fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let attrs = tcx.get_attrs(def_id.to_def_id(), sym::inline);
     for attr in attrs {
-        tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit();
+        tcx.sess.emit_err(CannotInlineNakedFunction { span: attr.span });
     }
 }
 
@@ -65,12 +70,11 @@ fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) {
     if abi == Abi::Rust {
         let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
         let span = tcx.def_span(def_id);
-        tcx.struct_span_lint_hir(
+        tcx.emit_spanned_lint(
             UNDEFINED_NAKED_FUNCTION_ABI,
             hir_id,
             span,
-            "Rust ABI is unsupported in naked functions",
-            |lint| lint,
+            UndefinedNakedFunctionAbi,
         );
     }
 }
@@ -82,12 +86,7 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
             hir::PatKind::Wild
             | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {}
             _ => {
-                tcx.sess
-                    .struct_span_err(
-                        param.pat.span,
-                        "patterns not allowed in naked function parameters",
-                    )
-                    .emit();
+                tcx.sess.emit_err(NoPatterns { span: param.pat.span });
             }
         }
     }
@@ -117,14 +116,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
         )) = expr.kind
         {
             if self.params.contains(var_hir_id) {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        expr.span,
-                        "referencing function parameters is not allowed in naked functions",
-                    )
-                    .help("follow the calling convention in asm block to use parameters")
-                    .emit();
+                self.tcx.sess.emit_err(ParamsNotAllowed { span: expr.span });
                 return;
             }
         }
@@ -139,26 +131,21 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<
     if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] {
         // Ok.
     } else {
-        let mut diag = struct_span_err!(
-            tcx.sess,
-            tcx.def_span(def_id),
-            E0787,
-            "naked functions must contain a single asm block"
-        );
-
         let mut must_show_error = false;
         let mut has_asm = false;
         let mut has_err = false;
+        let mut multiple_asms = vec![];
+        let mut non_asms = vec![];
         for &(kind, span) in &this.items {
             match kind {
                 ItemKind::Asm if has_asm => {
                     must_show_error = true;
-                    diag.span_label(span, "multiple asm blocks are unsupported in naked functions");
+                    multiple_asms.push(span);
                 }
                 ItemKind::Asm => has_asm = true,
                 ItemKind::NonAsm => {
                     must_show_error = true;
-                    diag.span_label(span, "non-asm is unsupported in naked functions");
+                    non_asms.push(span);
                 }
                 ItemKind::Err => has_err = true,
             }
@@ -168,9 +155,11 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<
         // errors, then don't show an additional error. This allows for appending/prepending
         // `compile_error!("...")` statements and reduces error noise.
         if must_show_error || !has_err {
-            diag.emit();
-        } else {
-            diag.cancel();
+            tcx.sess.emit_err(NakedFunctionsAsmBlock {
+                span: tcx.def_span(def_id),
+                multiple_asms,
+                non_asms,
+            });
         }
     }
 }
@@ -251,13 +240,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
             })
             .collect();
         if !unsupported_operands.is_empty() {
-            struct_span_err!(
-                self.tcx.sess,
-                unsupported_operands,
-                E0787,
-                "only `const` and `sym` operands are supported in naked functions",
-            )
-            .emit();
+            self.tcx.sess.emit_err(NakedFunctionsOperands { unsupported_operands });
         }
 
         let unsupported_options: Vec<&'static str> = [
@@ -273,14 +256,10 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
         .collect();
 
         if !unsupported_options.is_empty() {
-            struct_span_err!(
-                self.tcx.sess,
+            self.tcx.sess.emit_err(NakedFunctionsAsmOptions {
                 span,
-                E0787,
-                "asm options unsupported in naked functions: {}",
-                unsupported_options.join(", ")
-            )
-            .emit();
+                unsupported_options: unsupported_options.join(", "),
+            });
         }
 
         if !asm.options.contains(InlineAsmOptions::NORETURN) {
@@ -290,20 +269,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
                 .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1)
                 .shrink_to_hi();
 
-            struct_span_err!(
-                self.tcx.sess,
-                span,
-                E0787,
-                "asm in naked functions must use `noreturn` option"
-            )
-            .span_suggestion(
-                last_span,
-                "consider specifying that the asm block is responsible \
-                for returning from the function",
-                ", options(noreturn)",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+            self.tcx.sess.emit_err(NakedFunctionsMustUseNoreturn { span, last_span });
         }
     }
 }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 34fa80228df89..cfd6acd8d7cd0 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -1,13 +1,18 @@
 //! A pass that annotates every item and method with its stability level,
 //! propagating default levels lexically from parent to children ast nodes.
 
-use crate::errors;
+use crate::errors::{
+    self, CannotStabilizeDeprecated, DeprecatedAttribute, DuplicateFeatureErr,
+    FeatureOnlyOnNightly, ImpliedFeatureNotExist, InvalidDeprecationVersion, InvalidStability,
+    MissingConstErr, MissingConstStabAttr, MissingStabilityAttr, TraitImplConstStable,
+    UnknownFeature, UselessStability,
+};
 use rustc_attr::{
     self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable,
     UnstableReason, VERSION_PLACEHOLDER,
 };
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -20,7 +25,6 @@ use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
 use rustc_middle::ty::{query::Providers, TyCtxt};
 use rustc_session::lint;
 use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
-use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
@@ -179,7 +183,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 if !self.in_trait_impl
                     || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id()))
                 {
-                    missing_const_err(&self.tcx.sess, fn_sig.span, const_span);
+                    self.tcx
+                        .sess
+                        .emit_err(MissingConstErr { fn_sig_span: fn_sig.span, const_span });
                 }
             }
         }
@@ -197,14 +203,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
 
         if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr {
             if stab.is_none() {
-                struct_span_err!(
-                    self.tcx.sess,
-                    *span,
-                    E0549,
-                    "deprecated attribute must be paired with \
-                    either stable or unstable attribute"
-                )
-                .emit();
+                self.tcx.sess.emit_err(DeprecatedAttribute { span: *span });
             }
         }
 
@@ -220,10 +219,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             if kind == AnnotationKind::Prohibited
                 || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
             {
-                self.tcx.sess.struct_span_err(span,"this stability annotation is useless")
-                    .span_label(span, "useless stability annotation")
-                    .span_label(item_sp, "the stability attribute annotates this item")
-                    .emit();
+                self.tcx.sess.emit_err(UselessStability { span, item_sp });
             }
 
             debug!("annotate: found {:?}", stab);
@@ -239,19 +235,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 {
                     match stab_v.parse::<u64>() {
                         Err(_) => {
-                            self.tcx.sess.struct_span_err(span, "invalid stability version found")
-                                .span_label(span, "invalid stability version")
-                                .span_label(item_sp, "the stability attribute annotates this item")
-                                .emit();
+                            self.tcx.sess.emit_err(InvalidStability { span, item_sp });
                             break;
                         }
                         Ok(stab_vp) => match dep_v.parse::<u64>() {
                             Ok(dep_vp) => match dep_vp.cmp(&stab_vp) {
                                 Ordering::Less => {
-                                    self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated")
-                                        .span_label(span, "invalid version")
-                                        .span_label(item_sp, "the stability attribute annotates this item")
-                                        .emit();
+                                    self.tcx
+                                        .sess
+                                        .emit_err(CannotStabilizeDeprecated { span, item_sp });
                                     break;
                                 }
                                 Ordering::Equal => continue,
@@ -259,10 +251,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                             },
                             Err(_) => {
                                 if dep_v != "TBD" {
-                                    self.tcx.sess.struct_span_err(span, "invalid deprecation version found")
-                                        .span_label(span, "invalid deprecation version")
-                                        .span_label(item_sp, "the stability attribute annotates this item")
-                                        .emit();
+                                    self.tcx
+                                        .sess
+                                        .emit_err(InvalidDeprecationVersion { span, item_sp });
                                 }
                                 break;
                             }
@@ -271,7 +262,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 }
             }
 
-            if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab {
+            if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } =
+                stab
+            {
                 self.index.implications.insert(implied_by, feature);
             }
 
@@ -531,7 +524,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
         let stab = self.tcx.stability().local_stability(def_id);
         if !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(def_id) {
             let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
-            self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr));
+            self.tcx.sess.emit_err(MissingStabilityAttr { span, descr });
         }
     }
 
@@ -551,7 +544,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
 
         if is_const && is_stable && missing_const_stability_attribute && is_reachable {
             let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
-            self.tcx.sess.span_err(span, &format!("{descr} has missing const stability attribute"));
+            self.tcx.sess.emit_err(MissingConstStabAttr { span, descr });
         }
     }
 }
@@ -764,11 +757,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                         && *constness == hir::Constness::Const
                         && const_stab.map_or(false, |(stab, _)| stab.is_const_stable())
                     {
-                        self.tcx
-                            .sess
-                            .struct_span_err(item.span, "trait implementations cannot be const stable yet")
-                            .note("see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information")
-                            .emit();
+                        self.tcx.sess.emit_err(TraitImplConstStable { span: item.span });
                     }
                 }
 
@@ -929,7 +918,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
         }
         if !lang_features.insert(feature) {
             // Warn if the user enables a lang feature multiple times.
-            duplicate_feature_err(tcx.sess, span, feature);
+            tcx.sess.emit_err(DuplicateFeatureErr { span, feature });
         }
     }
 
@@ -937,18 +926,14 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     let mut remaining_lib_features = FxIndexMap::default();
     for (feature, span) in declared_lib_features {
         if !tcx.sess.opts.unstable_features.is_nightly_build() {
-            struct_span_err!(
-                tcx.sess,
-                *span,
-                E0554,
-                "`#![feature]` may not be used on the {} release channel",
-                env!("CFG_RELEASE_CHANNEL")
-            )
-            .emit();
+            tcx.sess.emit_err(FeatureOnlyOnNightly {
+                span: *span,
+                release_channel: env!("CFG_RELEASE_CHANNEL"),
+            });
         }
         if remaining_lib_features.contains_key(&feature) {
             // Warn if the user enables a lib feature multiple times.
-            duplicate_feature_err(tcx.sess, *span, *feature);
+            tcx.sess.emit_err(DuplicateFeatureErr { span: *span, feature: *feature });
         }
         remaining_lib_features.insert(feature, *span);
     }
@@ -1049,23 +1034,18 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     }
 
     for (feature, span) in remaining_lib_features {
-        struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
+        tcx.sess.emit_err(UnknownFeature { span, feature: *feature });
     }
 
     for (implied_by, feature) in remaining_implications {
         let local_defined_features = tcx.lib_features(());
-        let span = local_defined_features
+        let span = *local_defined_features
             .stable
             .get(&feature)
             .map(|(_, span)| span)
             .or_else(|| local_defined_features.unstable.get(&feature))
             .expect("feature that implied another does not exist");
-        tcx.sess
-            .struct_span_err(
-                *span,
-                format!("feature `{implied_by}` implying `{feature}` does not exist"),
-            )
-            .emit();
+        tcx.sess.emit_err(ImpliedFeatureNotExist { span, feature, implied_by });
     }
 
     // FIXME(#44232): the `used_features` table no longer exists, so we
@@ -1088,21 +1068,20 @@ fn unnecessary_partially_stable_feature_lint(
              by the feature `{implies}`"
         ),
         |lint| {
-            lint
-        .span_suggestion(
-            span,
-            &format!(
+            lint.span_suggestion(
+                span,
+                &format!(
                 "if you are using features which are still unstable, change to using `{implies}`"
             ),
-            implies,
-            Applicability::MaybeIncorrect,
-        )
-        .span_suggestion(
-            tcx.sess.source_map().span_extend_to_line(span),
-            "if you are using features which are now stable, remove this line",
-            "",
-            Applicability::MaybeIncorrect,
-        )
+                implies,
+                Applicability::MaybeIncorrect,
+            )
+            .span_suggestion(
+                tcx.sess.source_map().span_extend_to_line(span),
+                "if you are using features which are now stable, remove this line",
+                "",
+                Applicability::MaybeIncorrect,
+            )
         },
     );
 }
@@ -1120,20 +1099,3 @@ fn unnecessary_stable_feature_lint(
         lint
     });
 }
-
-fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {
-    struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature)
-        .emit();
-}
-
-fn missing_const_err(session: &Session, fn_sig_span: Span, const_span: Span) {
-    const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \
-         and `#[rustc_const_stable]` require \
-         the function or method to be `const`";
-
-    session
-        .struct_span_err(fn_sig_span, ERROR_MSG)
-        .span_help(fn_sig_span, "make the function or method const")
-        .span_label(const_span, "attribute specified here")
-        .emit();
-}
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index c48b4ecf87a3a..92024989a75e2 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -1,13 +1,17 @@
 //! Validity checking for weak lang items
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
 use rustc_hir::lang_items::{self, LangItem};
 use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS;
 use rustc_middle::middle::lang_items::required;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::CrateType;
 
+use crate::errors::{
+    AllocFuncRequired, MissingAllocErrorHandler, MissingLangItem, MissingPanicHandler,
+    UnknownExternLangItem,
+};
+
 /// Checks the crate for usage of weak lang items, returning a vector of all the
 /// language items required by this crate, but not defined yet.
 pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItems) {
@@ -31,14 +35,7 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem
                 }
             } else {
                 let span = tcx.def_span(id.def_id);
-                struct_span_err!(
-                    tcx.sess,
-                    span,
-                    E0264,
-                    "unknown external lang item: `{}`",
-                    lang_item
-                )
-                .emit();
+                tcx.sess.emit_err(UnknownExternLangItem { span, lang_item });
             }
         }
     }
@@ -71,20 +68,14 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
     for (name, &item) in WEAK_ITEMS_REFS.iter() {
         if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() {
             if item == LangItem::PanicImpl {
-                tcx.sess.err("`#[panic_handler]` function required, but not found");
+                tcx.sess.emit_err(MissingPanicHandler);
             } else if item == LangItem::Oom {
                 if !tcx.features().default_alloc_error_handler {
-                    tcx.sess.err("`#[alloc_error_handler]` function required, but not found");
-                    tcx.sess.note_without_error("use `#![feature(default_alloc_error_handler)]` for a default error handler");
+                    tcx.sess.emit_err(AllocFuncRequired);
+                    tcx.sess.emit_note(MissingAllocErrorHandler);
                 }
             } else {
-                tcx
-                    .sess
-                    .diagnostic()
-                    .struct_err(&format!("language item required, but not found: `{}`", name))
-                    .note(&format!("this can occur when a binary crate with `#![no_std]` is compiled for a target where `{}` is defined in the standard library", name))
-                    .help(&format!("you may be able to compile for a target that doesn't need `{}`, specify a target with `--target` or in `.cargo/config`", name))
-                    .emit();
+                tcx.sess.emit_err(MissingLangItem { name: *name });
             }
         }
     }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 83aae28640299..c4644d4f076e2 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -139,8 +139,7 @@ impl<'a> Resolver<'a> {
                     &candidates,
                     if instead { Instead::Yes } else { Instead::No },
                     found_use,
-                    IsPattern::No,
-                    IsImport::No,
+                    DiagnosticMode::Normal,
                     path,
                 );
                 err.emit();
@@ -699,8 +698,7 @@ impl<'a> Resolver<'a> {
                         &import_suggestions,
                         Instead::No,
                         FoundUse::Yes,
-                        IsPattern::Yes,
-                        IsImport::No,
+                        DiagnosticMode::Pattern,
                         vec![],
                     );
                 }
@@ -1496,8 +1494,7 @@ impl<'a> Resolver<'a> {
             &import_suggestions,
             Instead::No,
             FoundUse::Yes,
-            IsPattern::No,
-            IsImport::No,
+            DiagnosticMode::Normal,
             vec![],
         );
 
@@ -2458,18 +2455,13 @@ enum FoundUse {
     No,
 }
 
-/// Whether a binding is part of a pattern or an expression. Used for diagnostics.
-enum IsPattern {
+/// Whether a binding is part of a pattern or a use statement. Used for diagnostics.
+enum DiagnosticMode {
+    Normal,
     /// The binding is part of a pattern
-    Yes,
-    /// The binding is part of an expression
-    No,
-}
-
-/// Whether a binding is part of a use statement. Used for diagnostics.
-enum IsImport {
-    Yes,
-    No,
+    Pattern,
+    /// The binding is part of a use statement
+    Import,
 }
 
 pub(crate) fn import_candidates(
@@ -2488,8 +2480,7 @@ pub(crate) fn import_candidates(
         candidates,
         Instead::Yes,
         FoundUse::Yes,
-        IsPattern::No,
-        IsImport::Yes,
+        DiagnosticMode::Import,
         vec![],
     );
 }
@@ -2506,8 +2497,7 @@ fn show_candidates(
     candidates: &[ImportSuggestion],
     instead: Instead,
     found_use: FoundUse,
-    is_pattern: IsPattern,
-    is_import: IsImport,
+    mode: DiagnosticMode,
     path: Vec<Segment>,
 ) {
     if candidates.is_empty() {
@@ -2542,7 +2532,7 @@ fn show_candidates(
         };
 
         let instead = if let Instead::Yes = instead { " instead" } else { "" };
-        let mut msg = if let IsPattern::Yes = is_pattern {
+        let mut msg = if let DiagnosticMode::Pattern = mode {
             format!(
                 "if you meant to match on {}{}{}, use the full path in the pattern",
                 kind, instead, name
@@ -2555,19 +2545,24 @@ fn show_candidates(
             err.note(note);
         }
 
-        if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) {
-            err.span_suggestions(
-                span,
-                &msg,
-                accessible_path_strings.into_iter().map(|a| a.0),
-                Applicability::MaybeIncorrect,
-            );
-        } else if let Some(span) = use_placement_span {
+        if let Some(span) = use_placement_span {
+            let add_use = match mode {
+                DiagnosticMode::Pattern => {
+                    err.span_suggestions(
+                        span,
+                        &msg,
+                        accessible_path_strings.into_iter().map(|a| a.0),
+                        Applicability::MaybeIncorrect,
+                    );
+                    return;
+                }
+                DiagnosticMode::Import => "",
+                DiagnosticMode::Normal => "use ",
+            };
             for candidate in &mut accessible_path_strings {
                 // produce an additional newline to separate the new use statement
                 // from the directly following item.
                 let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
-                let add_use = if let IsImport::Yes = is_import { "" } else { "use " };
                 candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline);
             }
 
@@ -2598,11 +2593,14 @@ fn show_candidates(
 
             err.note(&msg);
         }
-    } else if matches!(is_import, IsImport::No) {
+    } else if !matches!(mode, DiagnosticMode::Import) {
         assert!(!inaccessible_path_strings.is_empty());
 
-        let prefix =
-            if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" };
+        let prefix = if let DiagnosticMode::Pattern = mode {
+            "you might have meant to match on "
+        } else {
+            ""
+        };
         if inaccessible_path_strings.len() == 1 {
             let (name, descr, def_id, note) = &inaccessible_path_strings[0];
             let msg = format!(
@@ -2610,7 +2608,7 @@ fn show_candidates(
                 prefix,
                 descr,
                 name,
-                if let IsPattern::Yes = is_pattern { ", which" } else { "" }
+                if let DiagnosticMode::Pattern = mode { ", which" } else { "" }
             );
 
             if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 2c3d8d5283b57..a199947ebed05 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -12,7 +12,7 @@ use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
 use rustc_errors::{
     fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
-    EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, StashKey,
+    EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
@@ -354,6 +354,17 @@ impl ParseSess {
         self.create_warning(warning).emit()
     }
 
+    pub fn create_note<'a>(
+        &'a self,
+        note: impl IntoDiagnostic<'a, Noted>,
+    ) -> DiagnosticBuilder<'a, Noted> {
+        note.into_diagnostic(&self.span_diagnostic)
+    }
+
+    pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted {
+        self.create_note(note).emit()
+    }
+
     pub fn create_fatal<'a>(
         &'a self,
         fatal: impl IntoDiagnostic<'a, !>,
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 5926cdc9dad9a..beb22ab3eb951 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -28,7 +28,7 @@ use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
 use rustc_errors::{
     error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
-    ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan,
+    ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -489,6 +489,15 @@ impl Session {
     pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) {
         self.parse_sess.emit_warning(warning)
     }
+    pub fn create_note<'a>(
+        &'a self,
+        note: impl IntoDiagnostic<'a, Noted>,
+    ) -> DiagnosticBuilder<'a, Noted> {
+        self.parse_sess.create_note(note)
+    }
+    pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted {
+        self.parse_sess.emit_note(note)
+    }
     pub fn create_fatal<'a>(
         &'a self,
         fatal: impl IntoDiagnostic<'a, !>,
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 30efbf6617598..cd9d229640571 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -85,9 +85,13 @@ fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness {
 ///     - a type parameter or projection whose Sizedness can't be known
 ///     - a tuple of type parameters or projections, if there are multiple
 ///       such.
-///     - an Error, if a type contained itself. The representability
-///       check should catch this case.
-fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstraint<'_> {
+///     - an Error, if a type is infinitely sized
+fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] {
+    if let Some(def_id) = def_id.as_local() {
+        if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
+            return tcx.intern_type_list(&[tcx.ty_error()]);
+        }
+    }
     let def = tcx.adt_def(def_id);
 
     let result = tcx.mk_type_list(
@@ -99,7 +103,7 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain
 
     debug!("adt_sized_constraint: {:?} => {:?}", def, result);
 
-    ty::AdtSizedConstraint(result)
+    result
 }
 
 /// See `ParamEnv` struct definition for details.
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index d6aeee299e30d..ff3b7bc2c9047 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -2279,9 +2279,8 @@ macro_rules! int_impl {
         ///
         /// # Panics
         ///
-        /// When the number is negative, zero, or if the base is not at least 2; it
-        /// panics in debug mode and the return value is 0 in release
-        /// mode.
+        /// This function will panic if `self` is less than or equal to zero,
+        /// or if `base` is less then 2.
         ///
         /// # Examples
         ///
@@ -2294,27 +2293,16 @@ macro_rules! int_impl {
                       without modifying the original"]
         #[inline]
         #[track_caller]
-        #[rustc_inherit_overflow_checks]
-        #[allow(arithmetic_overflow)]
         pub const fn ilog(self, base: Self) -> u32 {
-            match self.checked_ilog(base) {
-                Some(n) => n,
-                None => {
-                    // In debug builds, trigger a panic on None.
-                    // This should optimize completely out in release builds.
-                    let _ = Self::MAX + 1;
-
-                    0
-                },
-            }
+            assert!(base >= 2, "base of integer logarithm must be at least 2");
+            self.checked_ilog(base).expect("argument of integer logarithm must be positive")
         }
 
         /// Returns the base 2 logarithm of the number, rounded down.
         ///
         /// # Panics
         ///
-        /// When the number is negative or zero it panics in debug mode and the return value
-        /// is 0 in release mode.
+        /// This function will panic if `self` is less than or equal to zero.
         ///
         /// # Examples
         ///
@@ -2327,27 +2315,15 @@ macro_rules! int_impl {
                       without modifying the original"]
         #[inline]
         #[track_caller]
-        #[rustc_inherit_overflow_checks]
-        #[allow(arithmetic_overflow)]
         pub const fn ilog2(self) -> u32 {
-            match self.checked_ilog2() {
-                Some(n) => n,
-                None => {
-                    // In debug builds, trigger a panic on None.
-                    // This should optimize completely out in release builds.
-                    let _ = Self::MAX + 1;
-
-                    0
-                },
-            }
+            self.checked_ilog2().expect("argument of integer logarithm must be positive")
         }
 
         /// Returns the base 10 logarithm of the number, rounded down.
         ///
         /// # Panics
         ///
-        /// When the number is negative or zero it panics in debug mode and the return value
-        /// is 0 in release mode.
+        /// This function will panic if `self` is less than or equal to zero.
         ///
         /// # Example
         ///
@@ -2360,19 +2336,8 @@ macro_rules! int_impl {
                       without modifying the original"]
         #[inline]
         #[track_caller]
-        #[rustc_inherit_overflow_checks]
-        #[allow(arithmetic_overflow)]
         pub const fn ilog10(self) -> u32 {
-            match self.checked_ilog10() {
-                Some(n) => n,
-                None => {
-                    // In debug builds, trigger a panic on None.
-                    // This should optimize completely out in release builds.
-                    let _ = Self::MAX + 1;
-
-                    0
-                },
-            }
+            self.checked_ilog10().expect("argument of integer logarithm must be positive")
         }
 
         /// Returns the logarithm of the number with respect to an arbitrary base,
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 46b0ca2303406..d921ff9ba1026 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -692,8 +692,7 @@ macro_rules! uint_impl {
         ///
         /// # Panics
         ///
-        /// When the number is zero, or if the base is not at least 2;
-        /// it panics in debug mode and the return value is 0 in release mode.
+        /// This function will panic if `self` is zero, or if `base` is less then 2.
         ///
         /// # Examples
         ///
@@ -706,27 +705,16 @@ macro_rules! uint_impl {
                       without modifying the original"]
         #[inline]
         #[track_caller]
-        #[rustc_inherit_overflow_checks]
-        #[allow(arithmetic_overflow)]
         pub const fn ilog(self, base: Self) -> u32 {
-            match self.checked_ilog(base) {
-                Some(n) => n,
-                None => {
-                    // In debug builds, trigger a panic on None.
-                    // This should optimize completely out in release builds.
-                    let _ = Self::MAX + 1;
-
-                    0
-                },
-            }
+            assert!(base >= 2, "base of integer logarithm must be at least 2");
+            self.checked_ilog(base).expect("argument of integer logarithm must be positive")
         }
 
         /// Returns the base 2 logarithm of the number, rounded down.
         ///
         /// # Panics
         ///
-        /// When the number is zero it panics in debug mode and
-        /// the return value is 0 in release mode.
+        /// This function will panic if `self` is zero.
         ///
         /// # Examples
         ///
@@ -739,27 +727,15 @@ macro_rules! uint_impl {
                       without modifying the original"]
         #[inline]
         #[track_caller]
-        #[rustc_inherit_overflow_checks]
-        #[allow(arithmetic_overflow)]
         pub const fn ilog2(self) -> u32 {
-            match self.checked_ilog2() {
-                Some(n) => n,
-                None => {
-                    // In debug builds, trigger a panic on None.
-                    // This should optimize completely out in release builds.
-                    let _ = Self::MAX + 1;
-
-                    0
-                },
-            }
+            self.checked_ilog2().expect("argument of integer logarithm must be positive")
         }
 
         /// Returns the base 10 logarithm of the number, rounded down.
         ///
         /// # Panics
         ///
-        /// When the number is zero it panics in debug mode and the
-        /// return value is 0 in release mode.
+        /// This function will panic if `self` is zero.
         ///
         /// # Example
         ///
@@ -772,19 +748,8 @@ macro_rules! uint_impl {
                       without modifying the original"]
         #[inline]
         #[track_caller]
-        #[rustc_inherit_overflow_checks]
-        #[allow(arithmetic_overflow)]
         pub const fn ilog10(self) -> u32 {
-            match self.checked_ilog10() {
-                Some(n) => n,
-                None => {
-                    // In debug builds, trigger a panic on None.
-                    // This should optimize completely out in release builds.
-                    let _ = Self::MAX + 1;
-
-                    0
-                },
-            }
+            self.checked_ilog10().expect("argument of integer logarithm must be positive")
         }
 
         /// Returns the logarithm of the number with respect to an arbitrary base,
diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs
index be203fb5c04ff..a1edb1a518632 100644
--- a/library/core/tests/num/int_log.rs
+++ b/library/core/tests/num/int_log.rs
@@ -164,3 +164,33 @@ fn ilog10_u64() {
 fn ilog10_u128() {
     ilog10_loop! { u128, 38 }
 }
+
+#[test]
+#[should_panic(expected = "argument of integer logarithm must be positive")]
+fn ilog2_of_0_panic() {
+    let _ = 0u32.ilog2();
+}
+
+#[test]
+#[should_panic(expected = "argument of integer logarithm must be positive")]
+fn ilog10_of_0_panic() {
+    let _ = 0u32.ilog10();
+}
+
+#[test]
+#[should_panic(expected = "argument of integer logarithm must be positive")]
+fn ilog3_of_0_panic() {
+    let _ = 0u32.ilog(3);
+}
+
+#[test]
+#[should_panic(expected = "base of integer logarithm must be at least 2")]
+fn ilog0_of_1_panic() {
+    let _ = 1u32.ilog(0);
+}
+
+#[test]
+#[should_panic(expected = "base of integer logarithm must be at least 2")]
+fn ilog1_of_1_panic() {
+    let _ = 1u32.ilog(1);
+}
diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs
index b1a84095f13fa..4800eeda022bb 100644
--- a/library/std/src/io/readbuf.rs
+++ b/library/std/src/io/readbuf.rs
@@ -3,10 +3,10 @@
 #[cfg(test)]
 mod tests;
 
-use crate::cmp;
 use crate::fmt::{self, Debug, Formatter};
 use crate::io::{Result, Write};
 use crate::mem::{self, MaybeUninit};
+use crate::{cmp, ptr};
 
 /// A borrowed byte buffer which is incrementally filled and initialized.
 ///
@@ -250,8 +250,11 @@ impl<'a> BorrowedCursor<'a> {
     /// Initializes all bytes in the cursor.
     #[inline]
     pub fn ensure_init(&mut self) -> &mut Self {
-        for byte in self.uninit_mut() {
-            byte.write(0);
+        let uninit = self.uninit_mut();
+        // SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation
+        // since it is comes from a slice reference.
+        unsafe {
+            ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len());
         }
         self.buf.init = self.buf.capacity();
 
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 415774d7255d7..8b144f1463579 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -704,6 +704,7 @@ impl<'a> Builder<'a> {
                 doc::Miri,
                 doc::EmbeddedBook,
                 doc::EditionGuide,
+                doc::StyleGuide,
             ),
             Kind::Dist => describe!(
                 dist::Docs,
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index 819af6587484d..7bdd226cb692e 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -82,6 +82,7 @@ book!(
     Reference, "src/doc/reference", "reference", submodule;
     RustByExample, "src/doc/rust-by-example", "rust-by-example", submodule;
     RustdocBook, "src/doc/rustdoc", "rustdoc";
+    StyleGuide, "src/doc/style-guide", "style-guide";
 );
 
 fn open(builder: &Builder<'_>, path: impl AsRef<Path>) {
diff --git a/src/doc/index.md b/src/doc/index.md
index 744c7f709a6db..bf08960f338a3 100644
--- a/src/doc/index.md
+++ b/src/doc/index.md
@@ -113,6 +113,12 @@ resources useful.
 [The Reference](reference/index.html) is not a formal spec, but is more detailed and
 comprehensive than the book.
 
+## 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 Rustonomicon
 
 [The Rustonomicon](nomicon/index.html) is your guidebook to the dark arts of unsafe
diff --git a/src/doc/style-guide/book.toml b/src/doc/style-guide/book.toml
new file mode 100644
index 0000000000000..056aec8cdd4f6
--- /dev/null
+++ b/src/doc/style-guide/book.toml
@@ -0,0 +1,8 @@
+[book]
+title = "The Rust Style Guide"
+author = "The Rust Style Team"
+multilingual = false
+src = "src"
+
+[output.html]
+git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/"
diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md
new file mode 100644
index 0000000000000..adb73a7eef6e0
--- /dev/null
+++ b/src/doc/style-guide/src/README.md
@@ -0,0 +1,190 @@
+# Rust Style Guide
+
+## Motivation - why use a formatting tool?
+
+Formatting code is a mostly mechanical task which takes both time and mental
+effort. By using an automatic formatting tool, a programmer is relieved of
+this task and can concentrate on more important things.
+
+Furthermore, by sticking to an established style guide (such as this one),
+programmers don't need to formulate ad hoc style rules, nor do they need to
+debate with other programmers what style rules should be used, saving time,
+communication overhead, and mental energy.
+
+Humans comprehend information through pattern matching. By ensuring that all
+Rust code has similar formatting, less mental effort is required to comprehend a
+new project, lowering the barrier to entry for new developers.
+
+Thus, there are productivity benefits to using a formatting tool (such as
+rustfmt), and even larger benefits by using a community-consistent formatting,
+typically by using a formatting tool's default settings.
+
+
+## Formatting conventions
+
+### Indentation and line width
+
+* Use spaces, not tabs.
+* Each level of indentation must be four spaces (that is, all indentation
+  outside of string literals and comments must be a multiple of four).
+* The maximum width for a line is 100 characters.
+* A tool should be configurable for all three of these variables.
+
+
+### Blank lines
+
+Separate items and statements by either zero or one blank lines (i.e., one or
+two newlines). E.g,
+
+```rust
+fn foo() {
+    let x = ...;
+
+    let y = ...;
+    let z = ...;
+}
+
+fn bar() {}
+fn baz() {}
+```
+
+Formatting tools should make the bounds on blank lines configurable: there
+should be separate minimum and maximum numbers of newlines between both
+statements and (top-level) items (i.e., four options). As described above, the
+defaults for both statements and items should be minimum: 1, maximum: 2.
+
+
+### [Module-level items](items.md)
+### [Statements](statements.md)
+### [Expressions](expressions.md)
+### [Types](types.md)
+
+
+### Comments
+
+The following guidelines for comments are recommendations only, a mechanical
+formatter might skip formatting of comments.
+
+Prefer line comments (`//`) to block comments (`/* ... */`).
+
+When using line comments there should be a single space after the opening sigil.
+
+When using single-line block comments there should be a single space after the
+opening sigil and before the closing sigil. Multi-line block comments should
+have a newline after the opening sigil and before the closing sigil.
+
+Prefer to put a comment on its own line. Where a comment follows code, there
+should be a single space before it. Where a block comment is inline, there
+should be surrounding whitespace as if it were an identifier or keyword. There
+should be no trailing whitespace after a comment or at the end of any line in a
+multi-line comment. Examples:
+
+```rust
+// A comment on an item.
+struct Foo { ... }
+
+fn foo() {} // A comment after an item.
+
+pub fn foo(/* a comment before an argument */ x: T) {...}
+```
+
+Comments should usually be complete sentences. Start with a capital letter, end
+with a period (`.`). An inline block comment may be treated as a note without
+punctuation.
+
+Source lines which are entirely a comment should be limited to 80 characters
+in length (including comment sigils, but excluding indentation) or the maximum
+width of the line (including comment sigils and indentation), whichever is
+smaller:
+
+```rust
+// This comment goes up to the ................................. 80 char margin.
+
+{
+    // This comment is .............................................. 80 chars wide.
+}
+
+{
+    {
+        {
+            {
+                {
+                    {
+                        // This comment is limited by the ......................... 100 char margin.
+                    }
+                }
+            }
+        }
+    }
+}
+```
+
+#### Doc comments
+
+Prefer line comments (`///`) to block comments (`/** ... */`).
+
+Prefer outer doc comments (`///` or `/** ... */`), only use inner doc comments
+(`//!` and `/*! ... */`) to write module-level or crate-level documentation.
+
+Doc comments should come before attributes.
+
+### Attributes
+
+Put each attribute on its own line, indented to the level of the item.
+In the case of inner attributes (`#!`), indent it to the level of the inside of
+the item. Prefer outer attributes, where possible.
+
+For attributes with argument lists, format like functions.
+
+```rust
+#[repr(C)]
+#[foo(foo, bar)]
+struct CRepr {
+    #![repr(C)]
+    x: f32,
+    y: f32,
+}
+```
+
+For attributes with an equal sign, there should be a single space before and
+after the `=`, e.g., `#[foo = 42]`.
+
+There must only be a single `derive` attribute. Note for tool authors: if
+combining multiple `derive` attributes into a single attribute, the ordering of
+the derived names should be preserved. E.g., `#[derive(bar)] #[derive(foo)]
+struct Baz;` should be formatted to `#[derive(bar, foo)] struct Baz;`.
+
+### *small* items
+
+In many places in this guide we specify that a formatter may format an item
+differently if it is *small*, for example struct literals:
+
+```rust
+// Normal formatting
+Foo {
+    f1: an_expression,
+    f2: another_expression(),
+}
+
+// *small* formatting
+Foo { f1, f2 }
+```
+
+We leave it to individual tools to decide on exactly what *small* means. In
+particular, tools are free to use different definitions in different
+circumstances.
+
+Some suitable heuristics are the size of the item (in characters) or the
+complexity of an item (for example, that all components must be simple names,
+not more complex sub-expressions). For more discussion on suitable heuristics,
+see [this issue](https://github.com/rust-lang-nursery/fmt-rfcs/issues/47).
+
+Tools should give the user an option to ignore such heuristics and always use
+the normal formatting.
+
+
+## [Non-formatting conventions](advice.md)
+
+## [Cargo.toml conventions](cargo.md)
+
+## [Principles used for deciding these guidelines](principles.md)
diff --git a/src/doc/style-guide/src/SUMMARY.md b/src/doc/style-guide/src/SUMMARY.md
new file mode 100644
index 0000000000000..004692fa6a22b
--- /dev/null
+++ b/src/doc/style-guide/src/SUMMARY.md
@@ -0,0 +1,11 @@
+# Summary
+
+[Introduction](README.md)
+
+- [Module-level items](items.md)
+- [Statements](statements.md)
+- [Expressions](expressions.md)
+- [Types](types.md)
+- [Non-formatting conventions](advice.md)
+- [`Cargo.toml` conventions](cargo.md)
+- [Principles used for deciding these guidelines](principles.md)
diff --git a/src/doc/style-guide/src/advice.md b/src/doc/style-guide/src/advice.md
new file mode 100644
index 0000000000000..ab4b92b0a2478
--- /dev/null
+++ b/src/doc/style-guide/src/advice.md
@@ -0,0 +1,34 @@
+# Other style advice
+
+## Expressions
+
+Prefer to use Rust's expression oriented nature where possible;
+
+```rust
+// use
+let x = if y { 1 } else { 0 };
+// not
+let x;
+if y {
+    x = 1;
+} else {
+    x = 0;
+}
+```
+
+## Names
+
+ * Types shall be `UpperCamelCase`,
+ * Enum variants shall be `UpperCamelCase`,
+ * Struct fields shall be `snake_case`,
+ * Function and method names shall be `snake_case`,
+ * Local variables shall be `snake_case`,
+ * Macro names shall be `snake_case`,
+ * Constants (`const`s and immutable `static`s) shall be `SCREAMING_SNAKE_CASE`.
+ * When a name is forbidden because it is a reserved word (e.g., `crate`), use a
+   trailing underscore to make the name legal (e.g., `crate_`), or use raw
+   identifiers if possible.
+
+### Modules
+
+Avoid `#[path]` annotations where possible.
diff --git a/src/doc/style-guide/src/cargo.md b/src/doc/style-guide/src/cargo.md
new file mode 100644
index 0000000000000..f4993ba06a888
--- /dev/null
+++ b/src/doc/style-guide/src/cargo.md
@@ -0,0 +1,78 @@
+# Cargo.toml conventions
+
+## Formatting conventions
+
+Use the same line width and indentation as Rust code.
+
+Put a blank line between the last key-value pair in a section and the header of
+the next section. Do not place a blank line between section headers and the
+key-value pairs in that section, or between key-value pairs in a section.
+
+Sort key names alphabetically within each section, with the exception of the
+`[package]` section. Put the `[package]` section at the top of the file; put
+the `name` and `version` keys in that order at the top of that section,
+followed by the remaining keys other than `description` in alphabetical order,
+followed by the `description` at the end of that section.
+
+Don't use quotes around any standard key names; use bare keys. Only use quoted
+keys for non-standard keys whose names require them, and avoid introducing such
+key names when possible.  See the [TOML
+specification](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md#table)
+for details.
+
+Put a single space both before and after the `=` between a key and value. Do
+not indent any key names; start all key names at the start of a line.
+
+Use multi-line strings (rather than newline escape sequences) for any string
+values that include multiple lines, such as the crate description.
+
+For array values, such as a list of authors, put the entire list on the same
+line as the key, if it fits. Otherwise, use block indentation: put a newline
+after the opening square bracket, indent each item by one indentation level,
+put a comma after each item (including the last), and put the closing square
+bracket at the start of a line by itself after the last item.
+
+```rust
+authors = [
+    "A Uthor <a.uthor@example.org>",
+    "Another Author <author@example.net>",
+]
+```
+
+For table values, such as a crate dependency with a path, write the entire
+table using curly braces and commas on the same line as the key if it fits. If
+the entire table does not fit on the same line as the key, separate it out into
+a separate section with key-value pairs:
+
+```toml
+[dependencies]
+crate1 = { path = "crate1", version = "1.2.3" }
+
+[dependencies.extremely_long_crate_name_goes_here]
+path = "extremely_long_path_name_goes_right_here"
+version = "4.5.6"
+```
+
+## Metadata conventions
+
+The authors list should consist of strings that each contain an author name
+followed by an email address in angle brackets: `Full Name <email@address>`.
+It should not contain bare email addresses, or names without email addresses.
+(The authors list may also include a mailing list address without an associated
+name.)
+
+The license field must contain a valid [SPDX
+expression](https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60),
+using valid [SPDX license names](https://spdx.org/licenses/). (As an exception,
+by widespread convention, the license field may use `/` in place of ` OR `; for
+example, `MIT/Apache-2.0`.)
+
+The homepage field, if present, must consist of a single URL, including the
+scheme (e.g. `https://example.org/`, not just `example.org`.)
+
+Within the description field, wrap text at 80 columns. Don't start the
+description field with the name of the crate (e.g. "cratename is a ..."); just
+describe the crate itself. If providing a multi-sentence description, the first
+sentence should go on a line by itself and summarize the crate, like the
+subject of an email or commit message; subsequent sentences can then describe
+the crate in more detail.
diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md
new file mode 100644
index 0000000000000..c7d0446dded19
--- /dev/null
+++ b/src/doc/style-guide/src/expressions.md
@@ -0,0 +1,850 @@
+## Expressions
+
+### Blocks
+
+A block expression should have a newline after the initial `{` and before the
+terminal `}`. Any qualifier before the block (e.g., `unsafe`) should always be
+on the same line as the opening brace, and separated with a single space. The
+contents of the block should be block indented:
+
+```rust
+fn block_as_stmt() {
+    a_call();
+
+    {
+        a_call_inside_a_block();
+
+        // a comment in a block
+        the_value
+    }
+}
+
+fn block_as_expr() {
+    let foo = {
+        a_call_inside_a_block();
+
+        // a comment in a block
+        the_value
+    };
+}
+
+fn unsafe_block_as_stmt() {
+    a_call();
+
+    unsafe {
+        a_call_inside_a_block();
+
+        // a comment in a block
+        the_value
+    }
+}
+```
+
+If a block has an attribute, it should be on its own line:
+
+```rust
+fn block_as_stmt() {
+    #[an_attribute]
+    {
+        #![an_inner_attribute]
+
+        // a comment in a block
+        the_value
+    }
+}
+```
+
+Avoid writing comments on the same line as the braces.
+
+An empty block should be written as `{}`.
+
+A block may be written on a single line if:
+
+* it is either used in expression position (not statement position) or is an
+  unsafe block in statement position
+* contains a single-line expression and no statements
+* contains no comments
+
+A single line block should have spaces after the opening brace and before the
+closing brace.
+
+Examples:
+
+```rust
+fn main() {
+    // Single line
+    let _ = { a_call() };
+    let _ = unsafe { a_call() };
+
+    // Not allowed on one line
+    // Statement position.
+    {
+        a_call()
+    }
+
+    // Contains a statement
+    let _ = {
+        a_call();
+    };
+    unsafe {
+        a_call();
+    }
+
+    // Contains a comment
+    let _ = {
+        // A comment
+    };
+    let _ = {
+        // A comment
+        a_call()
+    };
+
+    // Multiple lines
+    let _ = {
+        a_call();
+        another_call()
+    };
+    let _ = {
+        a_call(
+            an_argument,
+            another_arg,
+        )
+    };
+}
+```
+
+
+### Closures
+
+Don't put any extra spaces before the first `|` (unless the closure is prefixed
+by `move`); put a space between the second `|` and the expression of the
+closure. Between the `|`s, you should use function definition syntax, however,
+elide types where possible.
+
+Use closures without the enclosing `{}`, if possible. Add the `{}` when you have
+a return type, when there are statements, there are comments in the body, or the
+body expression spans multiple lines and is a control-flow expression. If using
+braces, follow the rules above for blocks. Examples:
+
+```rust
+|arg1, arg2| expr
+
+move |arg1: i32, arg2: i32| -> i32 {
+    expr1;
+    expr2
+}
+
+|| Foo {
+    field1,
+    field2: 0,
+}
+
+|| {
+    if true {
+        blah
+    } else {
+        boo
+    }
+}
+
+|x| unsafe {
+    expr
+}
+```
+
+
+### Struct literals
+
+If a struct literal is *small* it may be formatted on a single line. If not,
+each field should be on it's own, block-indented line. There should be a
+trailing comma in the multi-line form only. There should be a space after the
+colon only.
+
+There should be a space before the opening brace. In the single-line form there
+should be spaces after the opening brace and before the closing brace.
+
+```rust
+Foo { field1, field2: 0 }
+let f = Foo {
+    field1,
+    field2: an_expr,
+};
+```
+
+Functional record update syntax is treated like a field, but it must never have
+a trailing comma. There should be no space after `..`.
+
+let f = Foo {
+    field1,
+    ..an_expr
+};
+
+
+### Tuple literals
+
+Use a single-line form where possible. There should not be spaces around the
+parentheses. Where a single-line form is not possible, each element of the tuple
+should be on its own block-indented line and there should be a trailing comma.
+
+```rust
+(a, b, c)
+
+let x = (
+    a_long_expr,
+    another_very_long_expr,
+);
+```
+
+
+### Tuple struct literals
+
+There should be no space between the identifier and the opening parenthesis.
+Otherwise, follow the rules for tuple literals, e.g., `Foo(a, b)`.
+
+
+### Enum literals
+
+Follow the formatting rules for the various struct literals. Prefer using the
+name of the enum as a qualifying name, unless the enum is in the prelude. E.g.,
+
+```rust
+Foo::Bar(a, b)
+Foo::Baz {
+    field1,
+    field2: 1001,
+}
+Ok(an_expr)
+```
+
+
+### Array literals
+
+For simple array literals, avoid line breaking, no spaces around square
+brackets, contents of the array should be separated by commas and spaces. If
+using the repeating initialiser, there should be a space after the semicolon
+only. Apply the same rules if using the `vec!` or similar macros (always use
+square brackets here). Examples:
+
+```rust
+fn main() {
+    [1, 2, 3];
+    vec![a, b, c, d];
+    let a = [42; 10];
+}
+```
+
+If a line must be broken, prefer breaking only after the `;`, if possible.
+Otherwise, follow the rules below for function calls. In any case, the contents
+of the initialiser should be block indented and there should be line breaks
+after the opening bracket and before the closing bracket:
+
+```rust
+fn main() {
+    [
+        a_long_expression();
+        1234567890
+    ]
+    let x = [
+        an_expression,
+        another_expression,
+        a_third_expression,
+    ];
+}
+```
+
+
+### Array accesses, indexing, and slicing.
+
+No spaces around the square brackets, avoid breaking lines if possible, never
+break a line between the target expression and the opening bracket. If the
+indexing expression covers multiple lines, then it should be block indented and
+there should be newlines after the opening brackets and before the closing
+bracket. However, this should be avoided where possible.
+
+Examples:
+
+```rust
+fn main() {
+    foo[42];
+    &foo[..10];
+    bar[0..100];
+    foo[4 + 5 / bar];
+    a_long_target[
+        a_long_indexing_expression
+    ];
+}
+```
+
+### Unary operations
+
+Do not include a space between a unary op and its operand (i.e., `!x`, not
+`! x`). However, there must be a space after `&mut`. Avoid line-breaking
+between a unary operator and its operand.
+
+### Binary operations
+
+Do include spaces around binary ops (i.e., `x + 1`, not `x+1`) (including `=`
+and other assignment operators such as `+=` or `*=`).
+
+For comparison operators, because for `T op U`, `&T op &U` is also implemented:
+if you have `t: &T`, and `u: U`, prefer `*t op u` to `t op &u`. In general,
+within expressions, prefer dereferencing to taking references.
+
+Use parentheses liberally, do not necessarily elide them due to precedence.
+Tools should not automatically insert or remove parentheses. Do not use spaces
+to indicate precedence.
+
+If line-breaking, put the operator on a new line and block indent. Put each
+sub-expression on its own line. E.g.,
+
+```rust
+foo_bar
+    + bar
+    + baz
+    + qux
+    + whatever
+```
+
+Prefer line-breaking at an assignment operator (either `=` or `+=`, etc.) rather
+than at other binary operators.
+
+### Control flow
+
+Do not include extraneous parentheses for `if` and `while` expressions.
+
+```rust
+if true {
+}
+```
+
+is better than
+
+```rust
+if (true) {
+}
+```
+
+Do include extraneous parentheses if it makes an arithmetic or logic expression
+easier to understand (`(x * 15) + (y * 20)` is fine)
+
+### Function calls
+
+Do not put a space between the function name, and the opening parenthesis.
+
+Do not put a space between an argument, and the comma which follows.
+
+Do put a space between an argument, and the comma which precedes it.
+
+Prefer not to break a line in the callee expression.
+
+#### Single-line calls
+
+Do not put a space between the function name and open paren, between the open
+paren and the first argument, or between the last argument and the close paren.
+
+Do not put a comma after the last argument.
+
+```rust
+foo(x, y, z)
+```
+
+#### Multi-line calls
+
+If the function call is not *small*, it would otherwise over-run the max width,
+or any argument or the callee is multi-line, then the call should be formatted
+across multiple lines. In this case, each argument should be on it's own block-
+indented line, there should be a newline after the opening parenthesis and
+before the closing parenthesis, and there should be a trailing comma. E.g.,
+
+```rust
+a_function_call(
+    arg1,
+    a_nested_call(a, b),
+)
+```
+
+
+### Method calls
+
+Follow the function rules for calling.
+
+Do not put any spaces around the `.`.
+
+```rust
+x.foo().bar().baz(x, y, z);
+```
+
+
+### Macro uses
+
+Macros which can be parsed like other constructs should be formatted like those
+constructs. For example, a macro use `foo!(a, b, c)` can be parsed like a
+function call (ignoring the `!`), therefore it should be formatted following the
+rules for function calls.
+
+#### Special case macros
+
+Macros which take a format string and where all other arguments are *small* may
+be formatted with arguments before and after the format string on a single line
+and the format string on its own line, rather than putting each argument on its
+own line. For example,
+
+```rust
+println!(
+    "Hello {} and {}",
+    name1, name2,
+);
+
+assert_eq!(
+    x, y,
+    "x and y were not equal, see {}",
+    reason,
+);
+```
+
+
+### Casts (`as`)
+
+Put spaces before and after `as`:
+
+```rust
+let cstr = "Hi\0" as *const str as *const [u8] as *const std::os::raw::c_char;
+```
+
+
+### Chains of fields and method calls
+
+A chain is a sequence of field accesses and/or method calls. A chain may also
+include the try operator ('?'). E.g., `a.b.c().d` or `foo?.bar().baz?`.
+
+Prefer formatting on one line if possible, and the chain is *small*. If
+formatting on multiple lines, each field access or method call in the chain
+should be on its own line with the line-break before the `.` and after any `?`.
+Each line should be block-indented. E.g.,
+
+```rust
+let foo = bar
+    .baz?
+    .qux();
+```
+
+If the length of the last line of the first element plus its indentation is
+less than or equal to the indentation of the second line (and there is space),
+then combine the first and second lines, e.g.,
+
+```rust
+x.baz?
+    .qux()
+
+let foo = x
+    .baz?
+    .qux();
+
+foo(
+    expr1,
+    expr2,
+).baz?
+    .qux();
+```
+
+#### Multi-line elements
+
+If any element in a chain is formatted across multiple lines, then that element
+and any later elements must be on their own line. Earlier elements may be kept
+on a single line. E.g.,
+
+```rust
+a.b.c()?.d
+    .foo(
+        an_expr,
+        another_expr,
+    )
+    .bar
+    .baz
+```
+
+Note there is block indent due to the chain and the function call in the above
+example.
+
+Prefer formatting the whole chain in multi-line style and each element on one
+line, rather than putting some elements on multiple lines and some on a single
+line, e.g.,
+
+```rust
+// Better
+self.pre_comment
+    .as_ref()
+    .map_or(false, |comment| comment.starts_with("//"))
+
+// Worse
+self.pre_comment.as_ref().map_or(
+    false,
+    |comment| comment.starts_with("//"),
+)
+```
+
+### Control flow expressions
+
+This section covers `if`, `if let`, `loop`, `while`, `while let`, and `for`
+expressions.
+
+The keyword, any initial clauses, and the opening brace of the block should be
+on a single line. The usual rules for [block formatting](#blocks) should be
+applied to the block.
+
+If there is an `else` component, then the closing brace, `else`, any following
+clause, and the opening brace should all be on the same line. There should be a
+single space before and after the `else` keyword. For example:
+
+```rust
+if ... {
+    ...
+} else {
+    ...
+}
+
+if let ... {
+    ...
+} else if ... {
+    ...
+} else {
+    ...
+}
+```
+
+If the control line needs to be broken, then prefer to break before the `=` in
+`* let` expressions and before `in` in a `for` expression; the following line
+should be block indented. If the control line is broken for any reason, then the
+opening brace should be on its own line and not indented. Examples:
+
+```rust
+while let Some(foo)
+    = a_long_expression
+{
+    ...
+}
+
+for foo
+    in a_long_expression
+{
+    ...
+}
+
+if a_long_expression
+    && another_long_expression
+    || a_third_long_expression
+{
+    ...
+}
+```
+
+Where the initial clause is multi-lined and ends with one or more closing
+parentheses, square brackets, or braces, and there is nothing else on that line,
+and that line is not indented beyond the indent on the first line of the control
+flow expression, then the opening brace of the block should be put on the same
+line with a preceding space. For example:
+
+```rust
+if !self.config.file_lines().intersects(
+    &self.codemap.lookup_line_range(
+        stmt.span,
+    ),
+) {  // Opening brace on same line as initial clause.
+    ...
+}
+```
+
+
+#### Single line `if else`
+
+Formatters may place an `if else` or `if let else` on a single line if it occurs
+in expression context (i.e., is not a standalone statement), it contains a
+single `else` clause, and is *small*. For example:
+
+```rust
+let y = if x { 0 } else { 1 };
+
+// Examples that must be multi-line.
+let y = if something_very_long {
+    not_small
+} else {
+    also_not_small
+};
+
+if x {
+    0
+} else {
+    1
+}
+```
+
+
+### Match
+
+Prefer not to line-break inside the discriminant expression. There must always
+be a line break after the opening brace and before the closing brace. The match
+arms must be block indented once:
+
+```rust
+match foo {
+    // arms
+}
+
+let x = match foo.bar.baz() {
+    // arms
+};
+```
+
+Use a trailing comma for a match arm if and only if not using a block.
+
+Never start a match arm pattern with `|`, e.g.,
+
+```rust
+match foo {
+    // Don't do this.
+    | foo => bar,
+    // Or this.
+    | a_very_long_pattern
+    | another_pattern
+    | yet_another_pattern
+    | a_forth_pattern => {
+        ...
+    }
+}
+```
+
+Prefer
+
+
+```rust
+match foo {
+    foo => bar,
+    a_very_long_pattern
+    | another_pattern
+    | yet_another_pattern
+    | a_forth_pattern => {
+        ...
+    }
+}
+```
+
+Avoid splitting the left-hand side (before the `=>`) of a match arm where
+possible. If the right-hand side of the match arm is kept on the same line,
+never use a block (unless the block is empty).
+
+If the right-hand side consists of multiple statements or has line comments or
+the start of the line cannot be fit on the same line as the left-hand side, use
+a block.
+
+The body of a block arm should be block indented once.
+
+Examples:
+
+```rust
+match foo {
+    foo => bar,
+    a_very_long_patten | another_pattern if an_expression() => {
+        no_room_for_this_expression()
+    }
+    foo => {
+        // A comment.
+        an_expression()
+    }
+    foo => {
+        let a = statement();
+        an_expression()
+    }
+    bar => {}
+    // Trailing comma on last item.
+    foo => bar,
+}
+```
+
+If the body is a single expression with no line comments and not a control flow
+expression, then it may be started on the same line as the right-hand side. If
+not, then it must be in a block. Example,
+
+```rust
+match foo {
+    // A combinable expression.
+    foo => a_function_call(another_call(
+        argument1,
+        argument2,
+    )),
+    // A non-combinable expression
+    bar => {
+        a_function_call(
+            another_call(
+                argument1,
+                argument2,
+            ),
+            another_argument,
+        )
+    }
+}
+```
+
+#### Line-breaking
+
+Where it is possible to use a block form on the right-hand side and avoid
+breaking the left-hand side, do that. E.g.
+
+```rust
+    // Assuming the following line does done fit in the max width
+    a_very_long_pattern | another_pattern => ALongStructName {
+        ...
+    },
+    // Prefer this
+    a_very_long_pattern | another_pattern => {
+        ALongStructName {
+            ...
+        }
+    }
+    // To splitting the pattern.
+```
+
+Never break after `=>` without using the block form of the body.
+
+If the left-hand side must be split and there is an `if` clause, break before
+the `if` and block indent. In this case, always use a block body and start the
+body on a new line:
+
+```rust
+    a_very_long_pattern | another_pattern
+        if expr =>
+    {
+        ...
+    }
+```
+
+If required to break the pattern, put each clause of the pattern on its own
+line with no additional indent, breaking before the `|`. If there is an `if`
+clause, then you must use the above form:
+
+```rust
+    a_very_long_pattern
+    | another_pattern
+    | yet_another_pattern
+    | a_forth_pattern => {
+        ...
+    }
+    a_very_long_pattern
+    | another_pattern
+    | yet_another_pattern
+    | a_forth_pattern
+        if expr =>
+    {
+        ...
+    }
+```
+
+If the pattern is multi-line, and the last line is less wide than the indent, do
+not put the `if` clause on a newline. E.g.,
+
+```rust
+    Token::Dimension {
+         value,
+         ref unit,
+         ..
+    } if num_context.is_ok(context.parsing_mode, value) => {
+        ...
+    }
+```
+
+If every clause in a pattern is *small*, but does not fit on one line, then the
+pattern may be formatted across multiple lines with as many clauses per line as
+possible. Again break before a `|`:
+
+```rust
+    foo | bar | baz
+    | qux => {
+        ...
+    }
+```
+
+We define a pattern clause to be *small* if it matches the following grammar:
+
+```
+[small, ntp]:
+    - single token
+    - `&[single-line, ntp]`
+
+[small]:
+    - `[small, ntp]`
+    - unary tuple constructor `([small, ntp])`
+    - `&[small]`
+```
+
+E.g., `&&Some(foo)` matches, `Foo(4, Bar)` does not.
+
+
+### Combinable expressions
+
+Where a function call has a single argument, and that argument is formatted
+across multiple-lines, the outer call may be formatted as if it were a single-
+line call. The same combining behaviour may be applied to any similar
+expressions which have multi-line, block-indented lists of sub-expressions
+delimited by parentheses (e.g., macros or tuple struct literals). E.g.,
+
+```rust
+foo(bar(
+    an_expr,
+    another_expr,
+))
+
+let x = foo(Bar {
+    field: whatever,
+});
+
+foo(|param| {
+    action();
+    foo(param)
+})
+```
+
+Such behaviour should extend recursively, however, tools may choose to limit the
+depth of nesting.
+
+Only where the multi-line sub-expression is a closure with an explicit block,
+this combining behaviour may be used where there are other arguments, as long as
+all the arguments and the first line of the closure fit on the first line, the
+closure is the last argument, and there is only one closure argument:
+
+```rust
+foo(first_arg, x, |param| {
+    action();
+    foo(param)
+})
+```
+
+
+### Ranges
+
+Do not put spaces in ranges, e.g., `0..10`, `x..=y`, `..x.len()`, `foo..`.
+
+When writing a range with both upper and lower bounds, if the line must be
+broken, break before the range operator and block indent the second line:
+
+```rust
+a_long_expression
+    ..another_long_expression
+```
+
+For the sake of indicating precedence, we recommend that if either bound is a
+compound expression, then use parentheses around it, e.g., `..(x + 1)`,
+`(x.f)..(x.f.len())`, or `0..(x - 10)`.
+
+
+### Hexadecimal literals
+
+Hexadecimal literals may use upper- or lower-case letters, but they must not be
+mixed within the same literal. Projects should use the same case for all
+literals, but we do not make a recommendation for either lower- or upper-case.
+Tools should have an option to convert mixed case literals to upper-case, and
+may have an option to convert all literals to either lower- or upper-case.
+
+
+## Patterns
+
+Patterns should be formatted like their corresponding expressions. See the
+section on `match` for additional formatting for patterns in match arms.
diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md
new file mode 100644
index 0000000000000..2835975355fca
--- /dev/null
+++ b/src/doc/style-guide/src/items.md
@@ -0,0 +1,565 @@
+## Items
+
+`extern crate` statements must be first in a file. They must be ordered
+alphabetically.
+
+`use` statements, and module *declarations* (`mod foo;`, not `mod { ... }`)
+must come before other items. We recommend that imports come before module
+declarations; if imports and modules are separated, then they should be ordered
+alphabetically. When sorting, `self` and `super` must come before any other
+names. Module declarations should not be moved if they are annotated with
+`#[macro_use]`, since that may be semantics changing.
+
+Tools should make the above ordering optional.
+
+
+### Function definitions
+
+In Rust, one finds functions by searching for `fn [function-name]`; It's
+important that you style your code so that it's very searchable in this way.
+
+The proper ordering and spacing is:
+
+```rust
+[pub] [unsafe] [extern ["ABI"]] fn foo(arg1: i32, arg2: i32) -> i32 {
+    ...
+}
+```
+
+Avoid comments within the signature itself.
+
+If the function signature does not fit on one line, then break after the opening
+parenthesis and before the closing parenthesis and put each argument on its own
+block-indented line. For example,
+
+```rust
+fn foo(
+    arg1: i32,
+    arg2: i32,
+) -> i32 {
+    ...
+}
+```
+
+Note the trailing comma on the last argument.
+
+
+### Tuples and tuple structs
+
+Write the type list as you would a parameter list to a function.
+
+Build a tuple or tuple struct as you would call a function.
+
+#### Single-line
+
+```rust
+struct Bar(Type1, Type2);
+
+let x = Bar(11, 22);
+let y = (11, 22, 33);
+```
+
+### Enums
+
+In the declaration, put each variant on its own line, block indented.
+
+Format each variant accordingly as either a struct, tuple struct, or identifier,
+which doesn't require special formatting (but without the `struct` keyword.
+
+```rust
+enum FooBar {
+    First(u32),
+    Second,
+    Error {
+        err: Box<Error>,
+        line: u32,
+    },
+}
+```
+
+If a struct variant is [*small*](index.html#small-items), it may be formatted on
+one line. In this case, do not use a trailing comma for the field list, but do
+put spaces around each brace:
+
+```rust
+enum FooBar {
+    Error { err: Box<Error>, line: u32 },
+}
+```
+
+In an enum with multiple struct variants, if any struct variant is written on
+multiple lines, then the multi-line formatting should be used for all struct
+variants. However, such a situation might be an indication that you should
+factor out the fields of the variant into their own struct.
+
+
+### Structs and Unions
+
+Struct names follow on the same line as the `struct` keyword, with the opening
+brace on the same line when it fits within the right margin. All struct fields
+are indented once and end with a trailing comma. The closing brace is not
+indented and appears on its own line.
+
+```rust
+struct Foo {
+    a: A,
+    b: B,
+}
+```
+
+If and only if the type of a field does not fit within the right margin, it is
+pulled down to its own line and indented again.
+
+```rust
+struct Foo {
+    a: A,
+    long_name:
+        LongType,
+}
+```
+
+Prefer using a unit struct (e.g., `struct Foo;`) to an empty struct (e.g.,
+`struct Foo();` or `struct Foo {}`, these only exist to simplify code
+generation), but if you must use an empty struct, keep it on one line with no
+space between the braces: `struct Foo;` or `struct Foo {}`.
+
+The same guidelines are used for untagged union declarations.
+
+```rust
+union Foo {
+    a: A,
+    b: B,
+    long_name:
+        LongType,
+}
+```
+
+
+### Tuple structs
+
+Put the whole struct on one line if possible. Types in the parentheses should be
+separated by a comma and space with no trailing comma. No spaces around the
+parentheses or semi-colon:
+
+```rust
+pub struct Foo(String, u8);
+```
+
+Prefer unit structs to empty tuple structs (these only exist to simplify code
+generation), e.g., `struct Foo;` rather than `struct Foo();`.
+
+For more than a few fields, prefer a proper struct with named fields. Given
+this, a tuple struct should always fit on one line. If it does not, block format
+the fields with a field on each line and a trailing comma:
+
+```rust
+pub struct Foo(
+    String,
+    u8,
+);
+```
+
+
+### Traits
+
+Trait items should be block-indented. If there are no items, the trait may be
+formatted on a single line. Otherwise there should be line-breaks after the
+opening brace and before the closing brace:
+
+```rust
+trait Foo {}
+
+pub trait Bar {
+    ...
+}
+```
+
+If the trait has bounds, there should be a space after the colon but not before
+and before and after each `+`, e.g.,
+
+```rust
+trait Foo: Debug + Bar {}
+```
+
+Prefer not to line-break in the bounds if possible (consider using a `where`
+clause). Prefer to break between bounds than to break any individual bound. If
+you must break the bounds, put each bound (including the first) on its own
+block-indented line, break before the `+` and put the opening brace on its own
+line:
+
+```rust
+pub trait IndexRanges:
+    Index<Range<usize>, Output=Self>
+    + Index<RangeTo<usize>, Output=Self>
+    + Index<RangeFrom<usize>, Output=Self>
+    + Index<RangeFull, Output=Self>
+{
+    ...
+}
+```
+
+
+### Impls
+
+Impl items should be block indented. If there are no items, the impl may be
+formatted on a single line. Otherwise there should be line-breaks after the
+opening brace and before the closing brace:
+
+```rust
+impl Foo {}
+
+impl Bar for Foo {
+    ...
+}
+```
+
+Avoid line-breaking in the signature if possible. If a line break is required in
+a non-inherent impl, break immediately before `for`, block indent the concrete type
+and put the opening brace on its own line:
+
+```rust
+impl Bar
+    for Foo
+{
+    ...
+}
+```
+
+
+### Extern crate
+
+`extern crate foo;`
+
+Use spaces around keywords, no spaces around the semi-colon.
+
+
+### Modules
+
+```rust
+mod foo {
+}
+```
+
+```rust
+mod foo;
+```
+
+Use spaces around keywords and before the opening brace, no spaces around the
+semi-colon.
+
+### macro\_rules!
+
+Use `{}` for the full definition of the macro.
+
+```rust
+macro_rules! foo {
+}
+```
+
+
+### Generics
+
+Prefer to put a generics clause on one line. Break other parts of an item
+declaration rather than line-breaking a generics clause. If a generics clause is
+large enough to require line-breaking, you should prefer to use a `where` clause
+instead.
+
+Do not put spaces before or after `<` nor before `>`. Only put a space after `>`
+if it is followed by a word or opening brace, not an opening parenthesis. There
+should be a space after each comma and no trailing comma.
+
+```rust
+fn foo<T: Display, U: Debug>(x: Vec<T>, y: Vec<U>) ...
+
+impl<T: Display, U: Debug> SomeType<T, U> { ...
+```
+
+If the generics clause must be formatted across multiple lines, each parameter
+should have its own block-indented line, there should be newlines after the
+opening bracket and before the closing bracket, and the should be a trailing
+comma.
+
+```rust
+fn foo<
+    T: Display,
+    U: Debug,
+>(x: Vec<T>, y: Vec<U>) ...
+```
+
+If an associated type is bound in a generic type, then there should be spaces on
+either side of the `=`:
+
+```rust
+<T: Example<Item = u32>>
+```
+
+Prefer to use single-letter names for generic parameters.
+
+
+### `where` clauses
+
+These rules apply for `where` clauses on any item.
+
+A `where` clause may immediately follow a closing bracket of any kind.
+Otherwise, it must start a new line, with no indent. Each component of a `where`
+clause must be on its own line and be block indented. There should be a trailing
+comma, unless the clause is terminated with a semicolon. If the `where` clause
+is followed by a block (or assignment), the block should be started on a new
+line. Examples:
+
+```rust
+fn function<T, U>(args)
+where
+    T: Bound,
+    U: AnotherBound,
+{
+    body
+}
+
+fn foo<T>(
+    args
+) -> ReturnType
+where
+    T: Bound,
+{
+    body
+}
+
+fn foo<T, U>(
+    args,
+) where
+    T: Bound,
+    U: AnotherBound,
+{
+    body
+}
+
+fn foo<T, U>(
+    args
+) -> ReturnType
+where
+    T: Bound,
+    U: AnotherBound;  // Note, no trailing comma.
+
+// Note that where clauses on `type` aliases are not enforced and should not
+// be used.
+type Foo<T>
+where
+    T: Bound
+= Bar<T>;
+```
+
+If a `where` clause is very short, we recommend using an inline bound on the
+type parameter.
+
+
+If a component of a `where` clause is long, it may be broken before `+` and
+further block indented. Each bound should go on its own line. E.g.,
+
+```rust
+impl<T: ?Sized, Idx> IndexRanges<Idx> for T
+where
+    T: Index<Range<Idx>, Output = Self::Output>
+        + Index<RangeTo<Idx>, Output = Self::Output>
+        + Index<RangeFrom<Idx>, Output = Self::Output>
+        + Index<RangeInclusive<Idx>, Output = Self::Output>
+        + Index<RangeToInclusive<Idx>, Output = Self::Output> + Index<RangeFull>
+```
+
+#### Option - `where_single_line`
+
+`where_single_line` is `false` by default. If `true`, then a where clause with
+exactly one component may be formatted on a single line if the rest of the
+item's signature is also kept on one line. In this case, there is no need for a
+trailing comma and if followed by a block, no need for a newline before the
+block. E.g.,
+
+```rust
+// May be single-lined.
+fn foo<T>(args) -> ReturnType
+where T: Bound {
+    body
+}
+
+// Must be multi-lined.
+fn foo<T>(
+    args
+) -> ReturnType
+where
+    T: Bound,
+{
+    body
+}
+```
+
+
+### Type aliases
+
+Type aliases should generally be kept on one line. If necessary to break the
+line, do so after the `=`; the right-hand-side should be block indented:
+
+```rust
+pub type Foo = Bar<T>;
+
+// If multi-line is required
+type VeryLongType<T, U: SomeBound> =
+    AnEvenLongerType<T, U, Foo<T>>;
+```
+
+Where possible avoid `where` clauses and keep type constraints inline. Where
+that is not possible split the line before and after the `where` clause (and
+split the `where` clause as normal), e.g.,
+
+```rust
+type VeryLongType<T, U>
+where
+    T: U::AnAssociatedType,
+    U: SomeBound,
+= AnEvenLongerType<T, U, Foo<T>>;
+```
+
+
+### Associated types
+
+Associated types should follow the guidelines above for type aliases. Where an
+associated type has a bound, there should be a space after the colon but not
+before:
+
+```rust
+pub type Foo: Bar;
+```
+
+
+### extern items
+
+When writing extern items (such as `extern "C" fn`), always be explicit about
+the ABI. For example, write `extern "C" fn foo ...`, not `extern fn foo ...`, or
+`extern "C" { ... }`.
+
+
+### Imports (`use` statements)
+
+If an import can be formatted on one line, do so. There should be no spaces
+around braces.
+
+```rust
+use a::b::c;
+use a::b::d::*;
+use a::b::{foo, bar, baz};
+```
+
+
+#### Large list imports
+
+Prefer to use multiple imports rather than a multi-line import. However, tools
+should not split imports by default (they may offer this as an option).
+
+If an import does require multiple lines (either because a list of single names
+does not fit within the max width, or because of the rules for nested imports
+below), then break after the opening brace and before the closing brace, use a
+trailing comma, and block indent the names.
+
+
+```rust
+// Prefer
+foo::{long, list, of, imports};
+foo::{more, imports};
+
+// If necessary
+foo::{
+    long, list, of, imports, more,
+    imports,  // Note trailing comma
+};
+```
+
+
+#### Ordering of imports
+
+A *group* of imports is a set of imports on the same or sequential lines. One or
+more blank lines or other items (e.g., a function) separate groups of imports.
+
+Within a group of imports, imports must be sorted ascii-betically. Groups of
+imports must not be merged or re-ordered.
+
+
+E.g., input:
+
+```rust
+use d;
+use c;
+
+use b;
+use a;
+```
+
+output:
+
+```rust
+use c;
+use d;
+
+use a;
+use b;
+```
+
+Because of `macro_use`, attributes must also start a new group and prevent
+re-ordering.
+
+Note that tools which only have access to syntax (such as Rustfmt) cannot tell
+which imports are from an external crate or the std lib, etc.
+
+
+#### Ordering list import
+
+Names in a list import must be sorted ascii-betically, but with `self` and
+`super` first, and groups and glob imports last. This applies recursively. For
+example, `a::*` comes before `b::a` but `a::b` comes before `a::*`. E.g.,
+`use foo::bar::{a, b::c, b::d, b::d::{x, y, z}, b::{self, r, s}};`.
+
+
+#### Normalisation
+
+Tools must make the following normalisations:
+
+* `use a::self;` -> `use a;`
+* `use a::{};` -> (nothing)
+* `use a::{b};` -> `use a::b;`
+
+And must apply these recursively.
+
+Tools must not otherwise merge or un-merge import lists or adjust glob imports
+(without an explicit option).
+
+
+#### Nested imports
+
+If there are any nested imports in a list import, then use the multi-line form,
+even if the import fits on one line. Each nested import must be on its own line,
+but non-nested imports must be grouped on as few lines as possible.
+
+For example,
+
+```rust
+use a::b::{
+    x, y, z,
+    u::{...},
+    w::{...},
+};
+```
+
+
+#### Merging/un-merging imports
+
+An example:
+
+```rust
+// Un-merged
+use a::b;
+use a::c::d;
+
+// Merged
+use a::{b, c::d};
+```
+
+Tools must not merge or un-merge imports by default. They may offer merging or
+un-merging as an option.
diff --git a/src/doc/style-guide/src/principles.md b/src/doc/style-guide/src/principles.md
new file mode 100644
index 0000000000000..b02b3c0471f28
--- /dev/null
+++ b/src/doc/style-guide/src/principles.md
@@ -0,0 +1,51 @@
+# Guiding principles and rationale
+
+When deciding on style guidelines, the style team tried to be guided by the
+following principles (in rough priority order):
+
+* readability
+    - scan-ability
+    - avoiding misleading formatting
+    - accessibility - readable and editable by users using the the widest
+      variety of hardware, including non-visual accessibility interfaces
+    - readability of code when quoted in rustc error messages
+
+* aesthetics
+    - sense of 'beauty'
+    - consistent with other languages/tools
+
+* specifics
+    - compatibility with version control practices - preserving diffs,
+      merge-friendliness, etc.
+    - preventing right-ward drift
+    - minimising vertical space
+
+* application
+    - ease of manual application
+    - ease of implementation (in Rustfmt, and in other tools/editors/code generators)
+    - internal consistency
+    - simplicity of formatting rules
+
+
+## Overarching guidelines
+
+Prefer block indent over visual indent. E.g.,
+
+```rust
+// Block indent
+a_function_call(
+    foo,
+    bar,
+);
+
+// Visual indent
+a_function_call(foo,
+                bar);
+```
+
+This makes for smaller diffs (e.g., if `a_function_call` is renamed in the above
+example) and less rightward drift.
+
+Lists should have a trailing comma when followed by a newline, see the block
+indent example above. This choice makes moving code (e.g., by copy and paste)
+easier and makes smaller diffs.
diff --git a/src/doc/style-guide/src/statements.md b/src/doc/style-guide/src/statements.md
new file mode 100644
index 0000000000000..29b48bb1ee0b7
--- /dev/null
+++ b/src/doc/style-guide/src/statements.md
@@ -0,0 +1,150 @@
+### Let statements
+
+There should be spaces after the `:` and on both sides of the `=` (if they are
+present). No space before the semi-colon.
+
+```rust
+// A comment.
+let pattern: Type = expr;
+
+let pattern;
+let pattern: Type;
+let pattern = expr;
+```
+
+If possible the declaration should be formatted on a single line. If this is not
+possible, then try splitting after the `=`, if the declaration can fit on two
+lines. The expression should be block indented.
+
+```rust
+let pattern: Type =
+    expr;
+```
+
+If the first line does not fit on a single line, then split after the colon,
+using block indentation. If the type covers multiple lines, even after line-
+breaking after the `:`, then the first line may be placed on the same line as
+the `:`, subject to the [combining rules](https://github.com/rust-lang-nursery/fmt-rfcs/issues/61) (WIP).
+
+
+```rust
+let pattern:
+    Type =
+    expr;
+```
+
+e.g,
+
+```rust
+let Foo {
+    f: abcd,
+    g: qwer,
+}: Foo<Bar> =
+    Foo { f, g };
+
+let (abcd,
+    defg):
+    Baz =
+{ ... }
+```
+
+If the expression covers multiple lines, if the first line of the expression
+fits in the remaining space, it stays on the same line as the `=`, the rest of the
+expression is not indented. If the first line does not fit, then it should start
+on the next lines, and should be block indented. If the expression is a block
+and the type or pattern cover multiple lines, then the opening brace should be
+on a new line and not indented (this provides separation for the interior of the
+block from the type), otherwise the opening brace follows the `=`.
+
+Examples:
+
+```rust
+let foo = Foo {
+    f: abcd,
+    g: qwer,
+};
+
+let foo =
+    ALongName {
+        f: abcd,
+        g: qwer,
+    };
+
+let foo: Type = {
+    an_expression();
+    ...
+};
+
+let foo:
+    ALongType =
+{
+    an_expression();
+    ...
+};
+
+let Foo {
+    f: abcd,
+    g: qwer,
+}: Foo<Bar> = Foo {
+    f: blimblimblim,
+    g: blamblamblam,
+};
+
+let Foo {
+    f: abcd,
+    g: qwer,
+}: Foo<Bar> = foo(
+    blimblimblim,
+    blamblamblam,
+);
+```
+
+
+### Macros in statement position
+
+A macro use in statement position should use parentheses or square brackets as
+delimiters and should be terminated with a semi-colon. There should be no spaces
+between the name, `!`, the delimiters, or the `;`.
+
+```rust
+// A comment.
+a_macro!(...);
+```
+
+
+### Expressions in statement position
+
+There should be no space between the expression and the semi-colon.
+
+```
+<expr>;
+```
+
+All expressions in statement position should be terminated with a semi-colon,
+unless they end with a block or are used as the value for a block.
+
+E.g.,
+
+```rust
+{
+    an_expression();
+    expr_as_value()
+}
+
+return foo();
+
+loop {
+    break;
+}
+```
+
+Use a semi-colon where an expression has void type, even if it could be
+propagated. E.g.,
+
+```rust
+fn foo() { ... }
+
+fn bar() {
+    foo();
+}
+```
diff --git a/src/doc/style-guide/src/types.md b/src/doc/style-guide/src/types.md
new file mode 100644
index 0000000000000..25861ddabb8d0
--- /dev/null
+++ b/src/doc/style-guide/src/types.md
@@ -0,0 +1,58 @@
+## Types and Bounds
+
+### Single line formatting
+
+* `[T]` no spaces
+* `[T; expr]`, e.g., `[u32; 42]`, `[Vec<Foo>; 10 * 2 + foo()]` (space after colon, no spaces around square brackets)
+* `*const T`, `*mut T` (no space after `*`, space before type)
+* `&'a T`, `&T`, `&'a mut T`, `&mut T` (no space after `&`, single spaces separating other words)
+* `unsafe extern "C" fn<'a, 'b, 'c>(T, U, V) -> W` or `fn()` (single spaces around keyowrds and sigils, and after commas, no trailing commas, no spaces around brackets)
+* `!` should be treated like any other type name, `Name`
+* `(A, B, C, D)` (spaces after commas, no spaces around parens, no trailing comma unless it is a one-tuple)
+* `<Baz<T> as SomeTrait>::Foo::Bar` or `Foo::Bar` or `::Foo::Bar` (no spaces around `::` or angle brackets, single spaces around `as`)
+* `Foo::Bar<T, U, V>` (spaces after commas, no trailing comma, no spaces around angle brackets)
+* `T + T + T` (single spaces between types, and `+`).
+* `impl T + T + T` (single spaces between keyword, types, and `+`).
+
+Parentheses used in types should not be surrounded by whitespace, e.g., `(Foo)`
+
+
+### Line breaks
+
+Avoid breaking lines in types where possible. Prefer breaking at outermost scope, e.g., prefer
+
+```rust
+Foo<
+    Bar,
+    Baz<Type1, Type2>,
+>
+```
+
+to
+
+```rust
+Foo<Bar, Baz<
+    Type1,
+    Type2,
+>>
+```
+
+`[T; expr]` may be broken after the `;` if necessary.
+
+Function types may be broken following the rules for function declarations.
+
+Generic types may be broken following the rules for generics.
+
+Types with `+` may be broken after any `+` using block indent and breaking before the `+`. When breaking such a type, all `+`s should be line broken, e.g.,
+
+```rust
+impl Clone
+    + Copy
+    + Debug
+
+Box<
+    Clone
+    + Copy
+    + Debug
+>
+```
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 661aed71298d9..f5b0d15d733aa 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -218,55 +218,55 @@ pre.rust a,
 .sidebar h2 a,
 .sidebar h3 a,
 .mobile-topbar h2 a,
-h1.fqn a,
+h1 a,
 .search-results a,
 .module-item .stab,
 .import-item .stab,
 .result-name .primitive > i, .result-name .keyword > i,
-.content .method .where,
-.content .fn .where,
-.content .where.fmt-newline {
+.method .where,
+.fn .where,
+.where.fmt-newline {
 	color: var(--main-color);
 }
 
-.content span.enum, .content a.enum,
-.content span.struct, .content a.struct,
-.content span.union, .content a.union,
-.content span.primitive, .content a.primitive,
-.content span.type, .content a.type,
-.content span.foreigntype, .content a.foreigntype {
+span.enum, a.enum,
+span.struct, a.struct,
+span.union, a.union,
+span.primitive, a.primitive,
+span.type, a.type,
+span.foreigntype, a.foreigntype {
 	color: var(--type-link-color);
 }
 
-.content span.trait, .content a.trait,
-.content span.traitalias, .content a.traitalias {
+span.trait, a.trait,
+span.traitalias, a.traitalias {
 	color: var(--trait-link-color);
 }
 
-.content span.associatedtype, .content a.associatedtype,
-.content span.constant, .content a.constant,
-.content span.static, .content a.static {
+span.associatedtype, a.associatedtype,
+span.constant, a.constant,
+span.static, a.static {
 	color: var(--assoc-item-link-color);
 }
 
-.content span.fn, .content a.fn,
-.content .fnname,
-.content span.method, .content a.method,
-.content span.tymethod, .content a.tymethod {
+span.fn, a.fn,
+.fnname,
+span.method, a.method,
+span.tymethod, a.tymethod {
 	color: var(--function-link-color);
 }
 
-.content span.attr, .content a.attr,
-.content span.derive, .content a.derive,
-.content span.macro, .content a.macro {
+span.attr, a.attr,
+span.derive, a.derive,
+span.macro, a.macro {
 	color: var(--macro-link-color);
 }
 
-.content span.mod, .content a.mod, .block a.current.mod {
+span.mod, a.mod {
 	color: var(--mod-link-color);
 }
 
-.content span.keyword, .content a.keyword {
+span.keyword, a.keyword {
 	color: var(--keyword-link-color);
 }
 
@@ -685,9 +685,9 @@ pre, .rustdoc.source .example-wrap {
 }
 
 /* Shift "where ..." part of method or fn definition down a line */
-.content .method .where,
-.content .fn .where,
-.content .where.fmt-newline {
+.method .where,
+.fn .where,
+.where.fmt-newline {
 	display: block;
 	font-size: 0.875rem;
 }
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index 0975d497cb23c..fc7713b98857b 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -56,7 +56,7 @@ input:focus + .slider {
 h1, h2, h3, h4 {
 	color: white;
 }
-h1.fqn  a {
+h1 a {
 	color: #fff;
 }
 h4 {
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 0180c0ead8d39..dc5b8acdf53a8 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -451,7 +451,6 @@ function loadCss(cssFileName) {
                 const name = item[0];
                 const desc = item[1]; // can be null
 
-                let klass = shortty;
                 let path;
                 if (shortty === "mod") {
                     path = name + "/index.html";
@@ -459,13 +458,12 @@ function loadCss(cssFileName) {
                     path = shortty + "." + name + ".html";
                 }
                 const current_page = document.location.href.split("/").pop();
-                if (path === current_page) {
-                    klass += " current";
-                }
                 const link = document.createElement("a");
                 link.href = path;
                 link.title = desc;
-                link.className = klass;
+                if (path === current_page) {
+                    link.className = "current";
+                }
                 link.textContent = name;
                 const li = document.createElement("li");
                 li.appendChild(link);
diff --git a/src/test/rustdoc-gui/sidebar-links-color.goml b/src/test/rustdoc-gui/sidebar-links-color.goml
index 3f719c4c4dc34..18a1a3fadea55 100644
--- a/src/test/rustdoc-gui/sidebar-links-color.goml
+++ b/src/test/rustdoc-gui/sidebar-links-color.goml
@@ -13,72 +13,72 @@ reload:
 
 // Struct
 assert-css: (
-    ".sidebar a.struct:not(.current)",
+    ".sidebar .block.struct a:not(.current)",
     {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.struct:not(.current)"
+move-cursor-to: ".sidebar .block.struct a:not(.current)"
 assert-css: (
-    ".sidebar a.struct:hover",
+    ".sidebar .block.struct a:hover",
     {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"},
 )
 // Enum
 assert-css: (
-    ".sidebar a.enum",
+    ".sidebar .block.enum a",
     {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.enum"
+move-cursor-to: ".sidebar .block.enum a"
 assert-css: (
-    ".sidebar a.enum:hover",
+    ".sidebar .block.enum a:hover",
     {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"},
 )
 // Union
 assert-css: (
-    ".sidebar a.union",
+    ".sidebar .block.union a",
     {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.union"
+move-cursor-to: ".sidebar .block.union a"
 assert-css: (
-    ".sidebar a.union:hover",
+    ".sidebar .block.union a:hover",
     {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"},
 )
 // Trait
 assert-css: (
-    ".sidebar a.trait",
+    ".sidebar .block.trait a",
     {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.trait"
+move-cursor-to: ".sidebar .block.trait a"
 assert-css: (
-    ".sidebar a.trait:hover",
+    ".sidebar .block.trait a:hover",
     {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"},
 )
 // Function
 assert-css: (
-    ".sidebar a.fn",
+    ".sidebar .block.fn a",
     {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.fn"
+move-cursor-to: ".sidebar .block.fn a"
 assert-css: (
-    ".sidebar a.fn:hover",
+    ".sidebar .block.fn a:hover",
     {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"},
 )
 // Type definition
 assert-css: (
-    ".sidebar a.type",
+    ".sidebar .block.type a",
     {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.type"
+move-cursor-to: ".sidebar .block.type a"
 assert-css: (
-    ".sidebar a.type:hover",
+    ".sidebar .block.type a:hover",
     {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"},
 )
 // Keyword
 assert-css: (
-    ".sidebar a.keyword",
+    ".sidebar .block.keyword a",
     {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.keyword"
+move-cursor-to: ".sidebar .block.keyword a"
 assert-css: (
-    ".sidebar a.keyword:hover",
+    ".sidebar .block.keyword a:hover",
     {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"},
 )
 
@@ -88,72 +88,72 @@ reload:
 
 // Struct
 assert-css: (
-    ".sidebar a.struct:not(.current)",
+    ".sidebar .block.struct a:not(.current)",
     {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.struct:not(.current)"
+move-cursor-to: ".sidebar .block.struct a:not(.current)"
 assert-css: (
-    ".sidebar a.struct:hover",
+    ".sidebar .block.struct a:hover",
     {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"},
 )
 // Enum
 assert-css: (
-    ".sidebar a.enum",
+    ".sidebar .block.enum a",
     {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.enum"
+move-cursor-to: ".sidebar .block.enum a"
 assert-css: (
-    ".sidebar a.enum:hover",
+    ".sidebar .block.enum a:hover",
     {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"},
 )
 // Union
 assert-css: (
-    ".sidebar a.union",
+    ".sidebar .block.union a",
     {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.union"
+move-cursor-to: ".sidebar .block.union a"
 assert-css: (
-    ".sidebar a.union:hover",
+    ".sidebar .block.union a:hover",
     {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"},
 )
 // Trait
 assert-css: (
-    ".sidebar a.trait",
+    ".sidebar .block.trait a",
     {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.trait"
+move-cursor-to: ".sidebar .block.trait a"
 assert-css: (
-    ".sidebar a.trait:hover",
+    ".sidebar .block.trait a:hover",
     {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"},
 )
 // Function
 assert-css: (
-    ".sidebar a.fn",
+    ".sidebar .block.fn a",
     {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.fn"
+move-cursor-to: ".sidebar .block.fn a"
 assert-css: (
-    ".sidebar a.fn:hover",
+    ".sidebar .block.fn a:hover",
     {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"},
 )
 // Type definition
 assert-css: (
-    ".sidebar a.type",
+    ".sidebar .block.type a",
     {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.type"
+move-cursor-to: ".sidebar .block.type a"
 assert-css: (
-    ".sidebar a.type:hover",
+    ".sidebar .block.type a:hover",
     {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"},
 )
 // Keyword
 assert-css: (
-    ".sidebar a.keyword",
+    ".sidebar .block.keyword a",
     {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.keyword"
+move-cursor-to: ".sidebar .block.keyword a"
 assert-css: (
-    ".sidebar a.keyword:hover",
+    ".sidebar .block.keyword a:hover",
     {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"},
 )
 
@@ -163,71 +163,71 @@ reload:
 
 // Struct
 assert-css: (
-    ".sidebar a.struct:not(.current)",
+    ".sidebar .block.struct a:not(.current)",
     {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.struct:not(.current)"
+move-cursor-to: ".sidebar .block.struct a:not(.current)"
 assert-css: (
-    ".sidebar a.struct:hover",
+    ".sidebar .block.struct a:hover",
     {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"},
 )
 // Enum
 assert-css: (
-    ".sidebar a.enum",
+    ".sidebar .block.enum a",
     {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.enum"
+move-cursor-to: ".sidebar .block.enum a"
 assert-css: (
-    ".sidebar a.enum:hover",
+    ".sidebar .block.enum a:hover",
     {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"},
 )
 // Union
 assert-css: (
-    ".sidebar a.union",
+    ".sidebar .block.union a",
     {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.union"
+move-cursor-to: ".sidebar .block.union a"
 assert-css: (
-    ".sidebar a.union:hover",
+    ".sidebar .block.union a:hover",
     {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"},
 )
 // Trait
 assert-css: (
-    ".sidebar a.trait",
+    ".sidebar .block.trait a",
     {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.trait"
+move-cursor-to: ".sidebar .block.trait a"
 assert-css: (
-    ".sidebar a.trait:hover",
+    ".sidebar .block.trait a:hover",
     {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"},
 )
 // Function
 assert-css: (
-    ".sidebar a.fn",
+    ".sidebar .block.fn a",
     {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.fn"
+move-cursor-to: ".sidebar .block.fn a"
 assert-css: (
-    ".sidebar a.fn:hover",
+    ".sidebar .block.fn a:hover",
     {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"},
 )
 // Type definition
 assert-css: (
-    ".sidebar a.type",
+    ".sidebar .block.type a",
     {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.type"
+move-cursor-to: ".sidebar .block.type a"
 assert-css: (
-    ".sidebar a.type:hover",
+    ".sidebar .block.type a:hover",
     {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"},
 )
 // Keyword
 assert-css: (
-    ".sidebar a.keyword",
+    ".sidebar .block.keyword a",
     {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"},
 )
-move-cursor-to: ".sidebar a.keyword"
+move-cursor-to: ".sidebar .block.keyword a"
 assert-css: (
-    ".sidebar a.keyword:hover",
+    ".sidebar .block.keyword a:hover",
     {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"},
 )
diff --git a/src/test/rustdoc-gui/sidebar-macro-reexport.goml b/src/test/rustdoc-gui/sidebar-macro-reexport.goml
index 01282f2ffeb7a..b5c1b6a4390b6 100644
--- a/src/test/rustdoc-gui/sidebar-macro-reexport.goml
+++ b/src/test/rustdoc-gui/sidebar-macro-reexport.goml
@@ -1,5 +1,5 @@
 // This test ensures that the reexport of a macro doesn't make the original macro
 // displayed twice in the sidebar.
 goto: "file://" + |DOC_PATH| + "/test_docs/macro.repro.html"
-wait-for: ".sidebar-elems .macro .macro"
+wait-for: ".sidebar-elems .block.macro a"
 assert-count: ("//*[@class='sidebar-elems']//*[@class='block macro']//a[text()='repro']", 1)
diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs
index 2319de5568366..4b1e04234c870 100644
--- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs
+++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs
@@ -1,11 +1,12 @@
+// check-pass
 // normalize-stderr-test: "`.*`" -> "`DEF_ID`"
 // normalize-stdout-test: "`.*`" -> "`DEF_ID`"
 // edition:2018
 
 pub async fn f() -> impl std::fmt::Debug {
+    // rustdoc doesn't care that this is infinitely sized
     #[derive(Debug)]
     enum E {
-    //~^ ERROR recursive type `f::{closure#0}::E` has infinite size
         This(E),
         Unit,
     }
diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr
deleted file mode 100644
index e6ab67d59ce58..0000000000000
--- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-error[E0072]: recursive type `DEF_ID` has infinite size
-  --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5
-   |
-LL |     enum E {
-   |     ^^^^^^
-LL |
-LL |         This(E),
-   |              - recursive without indirection
-   |
-help: insert some indirection (e.g., a `DEF_ID`) to break the cycle
-   |
-LL |         This(Box<E>),
-   |              ++++ +
-
-error: aborting due to previous error
-
-For more information about this error, try `DEF_ID`.
diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs
index b3a7ee563130e..ac79582fb3f0d 100644
--- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs
+++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs
@@ -1,6 +1,8 @@
+// check-pass
+
 fn f() -> impl Sized {
+    // rustdoc doesn't care that this is infinitely sized
     enum E {
-    //~^ ERROR recursive type `f::E` has infinite size
         V(E),
     }
     unimplemented!()
diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr
deleted file mode 100644
index 165ff67837244..0000000000000
--- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-error[E0072]: recursive type `f::E` has infinite size
-  --> $DIR/infinite-recursive-type-impl-trait.rs:2:5
-   |
-LL |     enum E {
-   |     ^^^^^^
-LL |
-LL |         V(E),
-   |           - recursive without indirection
-   |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
-   |
-LL |         V(Box<E>),
-   |           ++++ +
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/associated-types/issue-85103.rs b/src/test/ui/associated-types/issue-85103.rs
index c5e13856178de..9c6a419e9f72d 100644
--- a/src/test/ui/associated-types/issue-85103.rs
+++ b/src/test/ui/associated-types/issue-85103.rs
@@ -4,6 +4,6 @@ use std::borrow::Cow;
 
 #[rustc_layout(debug)]
 type Edges<'a, E> = Cow<'a, [E]>;
-//~^ ERROR layout error: NormalizationFailure
+//~^ 6:1: 6:18: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized
 
 fn main() {}
diff --git a/src/test/ui/associated-types/issue-85103.stderr b/src/test/ui/associated-types/issue-85103.stderr
index bddd1dce8e647..17f7148074cf3 100644
--- a/src/test/ui/associated-types/issue-85103.stderr
+++ b/src/test/ui/associated-types/issue-85103.stderr
@@ -1,4 +1,4 @@
-error: layout error: NormalizationFailure(<[E] as std::borrow::ToOwned>::Owned, Type(<[E] as std::borrow::ToOwned>::Owned))
+error: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized
   --> $DIR/issue-85103.rs:6:1
    |
 LL | type Edges<'a, E> = Cow<'a, [E]>;
diff --git a/src/test/ui/consts/issue-102117.rs b/src/test/ui/consts/issue-102117.rs
new file mode 100644
index 0000000000000..b77342c4135e1
--- /dev/null
+++ b/src/test/ui/consts/issue-102117.rs
@@ -0,0 +1,30 @@
+#![feature(inline_const, const_type_id)]
+
+use std::alloc::Layout;
+use std::any::TypeId;
+use std::mem::transmute;
+use std::ptr::drop_in_place;
+
+pub struct VTable {
+    layout: Layout,
+    type_id: TypeId,
+    drop_in_place: unsafe fn(*mut ()),
+}
+
+impl VTable {
+    pub fn new<T>() -> &'static Self {
+        const {
+          //~^ ERROR the parameter type `T` may not live long enough
+          //~| ERROR the parameter type `T` may not live long enough
+            &VTable {
+                layout: Layout::new::<T>(),
+                type_id: TypeId::of::<T>(),
+                drop_in_place: unsafe {
+                    transmute::<unsafe fn(*mut T), unsafe fn(*mut ())>(drop_in_place::<T>)
+                },
+            }
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/consts/issue-102117.stderr b/src/test/ui/consts/issue-102117.stderr
new file mode 100644
index 0000000000000..eb4b329bd8134
--- /dev/null
+++ b/src/test/ui/consts/issue-102117.stderr
@@ -0,0 +1,37 @@
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/issue-102117.rs:16:9
+   |
+LL | /         const {
+LL | |
+LL | |
+LL | |             &VTable {
+...  |
+LL | |             }
+LL | |         }
+   | |_________^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     pub fn new<T: 'static>() -> &'static Self {
+   |                 +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/issue-102117.rs:16:9
+   |
+LL | /         const {
+LL | |
+LL | |
+LL | |             &VTable {
+...  |
+LL | |             }
+LL | |         }
+   | |_________^ ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL |     pub fn new<T: 'static>() -> &'static Self {
+   |                 +++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0310`.
diff --git a/src/test/ui/issues/issue-72554.rs b/src/test/ui/issues/issue-72554.rs
index 7287639c61dde..54f7e9ac592eb 100644
--- a/src/test/ui/issues/issue-72554.rs
+++ b/src/test/ui/issues/issue-72554.rs
@@ -3,7 +3,6 @@ use std::collections::BTreeSet;
 #[derive(Hash)]
 pub enum ElemDerived {
     //~^ ERROR recursive type `ElemDerived` has infinite size
-    //~| ERROR cycle detected when computing drop-check constraints for `ElemDerived`
     A(ElemDerived)
 }
 
diff --git a/src/test/ui/issues/issue-72554.stderr b/src/test/ui/issues/issue-72554.stderr
index bc85cd7b18d55..d12be539f7c17 100644
--- a/src/test/ui/issues/issue-72554.stderr
+++ b/src/test/ui/issues/issue-72554.stderr
@@ -3,7 +3,7 @@ error[E0072]: recursive type `ElemDerived` has infinite size
    |
 LL | pub enum ElemDerived {
    | ^^^^^^^^^^^^^^^^^^^^
-...
+LL |
 LL |     A(ElemDerived)
    |       ----------- recursive without indirection
    |
@@ -12,20 +12,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
 LL |     A(Box<ElemDerived>)
    |       ++++           +
 
-error[E0391]: cycle detected when computing drop-check constraints for `ElemDerived`
-  --> $DIR/issue-72554.rs:4:1
-   |
-LL | pub enum ElemDerived {
-   | ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: ...which immediately requires computing drop-check constraints for `ElemDerived` again
-note: cycle used when computing drop-check constraints for `Elem`
-  --> $DIR/issue-72554.rs:11:1
-   |
-LL | pub enum Elem {
-   | ^^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0072, E0391.
-For more information about an error, try `rustc --explain E0072`.
+For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/variance/variance-regions-unused-indirect.rs b/src/test/ui/variance/variance-regions-unused-indirect.rs
index 1514e39563e1f..6c2c24ddbc728 100644
--- a/src/test/ui/variance/variance-regions-unused-indirect.rs
+++ b/src/test/ui/variance/variance-regions-unused-indirect.rs
@@ -1,6 +1,7 @@
 // Test that disallow lifetime parameters that are unused.
 
 enum Foo<'a> { //~ ERROR parameter `'a` is never used
+    //~^ ERROR recursive types `Foo` and `Bar` have infinite size
     Foo1(Bar<'a>)
 }
 
diff --git a/src/test/ui/variance/variance-regions-unused-indirect.stderr b/src/test/ui/variance/variance-regions-unused-indirect.stderr
index 93710cc133aa8..14fdd849294b1 100644
--- a/src/test/ui/variance/variance-regions-unused-indirect.stderr
+++ b/src/test/ui/variance/variance-regions-unused-indirect.stderr
@@ -1,3 +1,26 @@
+error[E0072]: recursive types `Foo` and `Bar` have infinite size
+  --> $DIR/variance-regions-unused-indirect.rs:3:1
+   |
+LL | enum Foo<'a> {
+   | ^^^^^^^^^^^^
+LL |
+LL |     Foo1(Bar<'a>)
+   |          ------- recursive without indirection
+...
+LL | enum Bar<'a> {
+   | ^^^^^^^^^^^^
+LL |     Bar1(Foo<'a>)
+   |          ------- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
+   |
+LL ~     Foo1(Box<Bar<'a>>)
+LL | }
+LL | 
+LL | enum Bar<'a> {
+LL ~     Bar1(Box<Foo<'a>>)
+   |
+
 error[E0392]: parameter `'a` is never used
   --> $DIR/variance-regions-unused-indirect.rs:3:10
    |
@@ -7,13 +30,14 @@ LL | enum Foo<'a> {
    = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
 
 error[E0392]: parameter `'a` is never used
-  --> $DIR/variance-regions-unused-indirect.rs:7:10
+  --> $DIR/variance-regions-unused-indirect.rs:8:10
    |
 LL | enum Bar<'a> {
    |          ^^ unused parameter
    |
    = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0392`.
+Some errors have detailed explanations: E0072, E0392.
+For more information about an error, try `rustc --explain E0072`.
diff --git a/triagebot.toml b/triagebot.toml
index d358e59c24525..181fb1de93055 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -189,6 +189,11 @@ trigger_files = [
     "src/tools/bump-stage0",
 ]
 
+[autolabel."T-style"]
+trigger_files = [
+    "src/doc/style-guide",
+]
+
 [autolabel."A-translation"]
 trigger_files = [
     "compiler/rustc_error_messages",