about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThe Miri Conjob Bot <miri@cron.bot>2023-12-19 05:00:53 +0000
committerThe Miri Conjob Bot <miri@cron.bot>2023-12-19 05:00:53 +0000
commit0f70ced1280124e2078ec5838ac173c13f400e2e (patch)
tree5abaccd88ccb2f5852113cbbb76418387134d508
parent245f2ad429011498b13d090438a605fba0655c58 (diff)
parentdf5b604945276994ad662bd0fd34face0ad609eb (diff)
downloadrust-0f70ced1280124e2078ec5838ac173c13f400e2e.tar.gz
rust-0f70ced1280124e2078ec5838ac173c13f400e2e.zip
Merge from rustc
-rw-r--r--Cargo.lock8
-rw-r--r--RELEASES.md7
-rw-r--r--compiler/rustc_abi/src/layout.rs20
-rw-r--r--compiler/rustc_ast/src/ast.rs22
-rw-r--r--compiler/rustc_ast/src/util/classify.rs35
-rw-r--r--compiler/rustc_ast/src/util/literal.rs74
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs56
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs54
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs7
-rw-r--r--compiler/rustc_ast_passes/messages.ftl3
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs109
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs5
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs4
-rw-r--r--compiler/rustc_ast_passes/src/show_span.rs12
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs4
-rw-r--r--compiler/rustc_attr/src/builtin.rs2
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs10
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs10
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs30
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/nll.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/alloc_error_handler.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs43
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs15
-rw-r--r--compiler/rustc_builtin_macros/src/global_allocator.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs24
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs28
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs12
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/pretty_clif.rs5
-rw-r--r--compiler/rustc_codegen_gcc/src/back/lto.rs22
-rw-r--r--compiler/rustc_codegen_gcc/src/back/write.rs10
-rw-r--r--compiler/rustc_codegen_gcc/src/common.rs11
-rw-r--r--compiler/rustc_codegen_gcc/src/context.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs7
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs87
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs76
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs14
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs27
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs23
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs19
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs24
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs16
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs24
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs43
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs36
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs89
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/write.rs8
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs2
-rw-r--r--compiler/rustc_const_eval/src/errors.rs62
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs4
-rw-r--r--compiler/rustc_data_structures/src/sorted_map.rs2
-rw-r--r--compiler/rustc_driver_impl/src/args.rs6
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs123
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs9
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs118
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs24
-rw-r--r--compiler/rustc_errors/src/emitter.rs15
-rw-r--r--compiler/rustc_errors/src/json/tests.rs6
-rw-r--r--compiler/rustc_errors/src/lib.rs34
-rw-r--r--compiler/rustc_expand/src/base.rs32
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs11
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs42
-rw-r--r--compiler/rustc_expand/src/mbe/metavar_expr.rs21
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs15
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs4
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs6
-rw-r--r--compiler/rustc_expand/src/tests.rs12
-rw-r--r--compiler/rustc_hir/src/hir.rs15
-rw-r--r--compiler/rustc_hir/src/intravisit.rs4
-rw-r--r--compiler/rustc_hir/src/target.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/bounds.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/lint.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs169
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/object_safety.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs379
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/check/entry.rs49
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs51
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs63
-rw-r--r--compiler/rustc_hir_analysis/src/check_unused.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs44
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs6
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs33
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs61
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs37
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude2021.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs68
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs16
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs16
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs26
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs42
-rw-r--r--compiler/rustc_infer/src/infer/outlives/components.rs4
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs7
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs5
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs (renamed from compiler/rustc_infer/src/infer/combine.rs)30
-rw-r--r--compiler/rustc_infer/src/infer/relate/equate.rs (renamed from compiler/rustc_infer/src/infer/equate.rs)8
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs (renamed from compiler/rustc_infer/src/infer/generalize.rs)12
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs (renamed from compiler/rustc_infer/src/infer/glb.rs)11
-rw-r--r--compiler/rustc_infer/src/infer/relate/higher_ranked.rs (renamed from compiler/rustc_infer/src/infer/higher_ranked/mod.rs)2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lattice.rs (renamed from compiler/rustc_infer/src/infer/lattice.rs)6
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs (renamed from compiler/rustc_infer/src/infer/lub.rs)7
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs12
-rw-r--r--compiler/rustc_infer/src/infer/relate/nll.rs (renamed from compiler/rustc_infer/src/infer/nll_relate/mod.rs)12
-rw-r--r--compiler/rustc_infer/src/infer/relate/sub.rs (renamed from compiler/rustc_infer/src/infer/sub.rs)4
-rw-r--r--compiler/rustc_interface/src/interface.rs52
-rw-r--r--compiler/rustc_interface/src/passes.rs6
-rw-r--r--compiler/rustc_interface/src/queries.rs2
-rw-r--r--compiler/rustc_interface/src/tests.rs70
-rw-r--r--compiler/rustc_interface/src/util.rs26
-rw-r--r--compiler/rustc_lexer/src/unescape.rs21
-rw-r--r--compiler/rustc_lint/src/builtin.rs2
-rw-r--r--compiler/rustc_lint/src/context.rs39
-rw-r--r--compiler/rustc_lint/src/early.rs8
-rw-r--r--compiler/rustc_lint/src/expect.rs2
-rw-r--r--compiler/rustc_lint/src/levels.rs13
-rw-r--r--compiler/rustc_lint/src/lib.rs5
-rw-r--r--compiler/rustc_lint/src/lints.rs56
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs3
-rw-r--r--compiler/rustc_lint/src/reference_casting.rs138
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs42
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp3
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp8
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs23
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs12
-rw-r--r--compiler/rustc_metadata/src/creader.rs2
-rw-r--r--compiler/rustc_metadata/src/errors.rs12
-rw-r--r--compiler/rustc_metadata/src/fs.rs2
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs5
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs4
-rw-r--r--compiler/rustc_middle/src/lint.rs42
-rw-r--r--compiler/rustc_middle/src/middle/lang_items.rs11
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs3
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs2
-rw-r--r--compiler/rustc_middle/src/mir/type_foldable.rs1
-rw-r--r--compiler/rustc_middle/src/traits/select.rs8
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs73
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/int.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs27
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs8
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs8
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs14
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs1
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs20
-rw-r--r--compiler/rustc_middle/src/ty/util.rs7
-rw-r--r--compiler/rustc_middle/src/util/bug.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/custom/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs8
-rw-r--r--compiler/rustc_mir_build/src/errors.rs8
-rw-r--r--compiler/rustc_mir_build/src/lib.rs1
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs5
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs6
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs154
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs12
-rw-r--r--compiler/rustc_mir_transform/src/cross_crate_inline.rs19
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs2
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs34
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs8
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs20
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs4
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs12
-rw-r--r--compiler/rustc_next_trait_solver/src/canonicalizer.rs8
-rw-r--r--compiler/rustc_parse/src/errors.rs8
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs20
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs4
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs90
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs2
-rw-r--r--compiler/rustc_parse/src/lib.rs10
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs4
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs5
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs26
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs24
-rw-r--r--compiler/rustc_parse/src/parser/item.rs16
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs2
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs11
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs4
-rw-r--r--compiler/rustc_parse/src/parser/path.rs4
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs8
-rw-r--r--compiler/rustc_passes/src/errors.rs20
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs2
-rw-r--r--compiler/rustc_passes/src/lang_items.rs378
-rw-r--r--compiler/rustc_passes/src/weak_lang_items.rs30
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs2
-rw-r--r--compiler/rustc_privacy/src/lib.rs63
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs4
-rw-r--r--compiler/rustc_query_system/src/query/job.rs10
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs28
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs4
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs17
-rw-r--r--compiler/rustc_resolve/src/macros.rs5
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs3
-rw-r--r--compiler/rustc_session/src/config.rs261
-rw-r--r--compiler/rustc_session/src/errors.rs4
-rw-r--r--compiler/rustc_session/src/options.rs14
-rw-r--r--compiler/rustc_session/src/parse.rs37
-rw-r--r--compiler/rustc_session/src/search_paths.rs6
-rw-r--r--compiler/rustc_session/src/session.rs153
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs14
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mir.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mod.rs4
-rw-r--r--compiler/rustc_span/src/caching_source_map_view.rs6
-rw-r--r--compiler/rustc_symbol_mangling/src/errors.rs4
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs79
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs27
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs20
-rw-r--r--compiler/rustc_type_ir/src/debug.rs6
-rw-r--r--compiler/rustc_type_ir/src/infcx.rs16
-rw-r--r--compiler/rustc_type_ir/src/interner.rs7
-rw-r--r--compiler/rustc_type_ir/src/lib.rs42
-rw-r--r--compiler/rustc_type_ir/src/macros.rs2
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs46
-rw-r--r--compiler/stable_mir/src/compiler_interface.rs3
-rw-r--r--compiler/stable_mir/src/mir/body.rs69
-rw-r--r--compiler/stable_mir/src/mir/mono.rs7
-rw-r--r--compiler/stable_mir/src/mir/pretty.rs9
-rw-r--r--compiler/stable_mir/src/mir/visit.rs2
-rw-r--r--compiler/stable_mir/src/ty.rs20
-rw-r--r--config.example.toml4
-rw-r--r--library/alloc/src/ffi/c_str.rs6
-rw-r--r--library/core/src/any.rs39
-rw-r--r--library/core/src/ffi/c_str.rs4
-rw-r--r--library/core/src/net/ip_addr.rs2
-rw-r--r--library/core/src/ptr/mod.rs7
-rw-r--r--library/core/src/slice/ascii.rs3
-rw-r--r--library/core/src/str/mod.rs79
-rw-r--r--library/proc_macro/src/bridge/mod.rs1
-rw-r--r--library/proc_macro/src/lib.rs3
-rw-r--r--library/std/Cargo.toml5
-rw-r--r--library/std/src/env.rs50
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/macros.rs6
-rw-r--r--library/std/src/panicking.rs4
-rw-r--r--library/std/src/process.rs2
-rw-r--r--library/std/src/rt.rs3
-rw-r--r--library/std/src/sys/personality/mod.rs2
-rw-r--r--library/std/src/sys/windows/fs.rs14
-rw-r--r--src/bootstrap/Cargo.lock7
-rw-r--r--src/bootstrap/Cargo.toml1
-rw-r--r--src/bootstrap/bootstrap.py16
-rw-r--r--src/bootstrap/src/bin/rustc.rs52
-rw-r--r--src/bootstrap/src/core/build_steps/clean.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs16
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs3
-rw-r--r--src/bootstrap/src/core/builder.rs140
-rw-r--r--src/bootstrap/src/core/download.rs39
-rw-r--r--src/bootstrap/src/lib.rs3
-rw-r--r--src/bootstrap/src/tests/builder.rs16
-rw-r--r--src/bootstrap/src/tests/helpers.rs59
-rw-r--r--src/bootstrap/src/tests/setup.rs3
-rw-r--r--src/bootstrap/src/utils/dylib.rs13
-rw-r--r--src/bootstrap/src/utils/helpers.rs22
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile6
-rw-r--r--src/doc/rustc/src/codegen-options/index.md5
-rw-r--r--src/doc/unstable-book/src/compiler-flags/codegen-options.md56
-rw-r--r--src/librustdoc/clean/mod.rs94
-rw-r--r--src/librustdoc/config.rs92
-rw-r--r--src/librustdoc/core.rs29
-rw-r--r--src/librustdoc/doctest.rs18
-rw-r--r--src/librustdoc/externalfiles.rs22
-rw-r--r--src/librustdoc/html/markdown.rs13
-rw-r--r--src/librustdoc/html/render/print_item.rs13
-rw-r--r--src/librustdoc/html/render/write_shared.rs14
-rw-r--r--src/librustdoc/html/static/css/noscript.css6
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css171
-rw-r--r--src/librustdoc/html/static/js/main.js268
-rw-r--r--src/librustdoc/html/static/js/search.js74
-rw-r--r--src/librustdoc/html/static/js/settings.js19
-rw-r--r--src/librustdoc/html/static/js/src-script.js42
-rw-r--r--src/librustdoc/html/static/js/storage.js28
-rw-r--r--src/librustdoc/html/templates/page.html10
-rw-r--r--src/librustdoc/lib.rs48
-rw-r--r--src/librustdoc/lint.rs4
-rw-r--r--src/librustdoc/passes/check_custom_code_classes.rs7
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs4
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs2
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs2
-rw-r--r--src/librustdoc/passes/lint/bare_urls.rs2
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs8
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs4
-rw-r--r--src/librustdoc/passes/lint/redundant_explicit_links.rs4
-rw-r--r--src/librustdoc/passes/lint/unescaped_backticks.rs2
-rw-r--r--src/librustdoc/scrape_examples.rs15
-rw-r--r--src/librustdoc/theme.rs6
-rw-r--r--src/librustdoc/visit_ast.rs11
-rw-r--r--src/stage0.json48
-rw-r--r--src/tools/bump-stage0/src/main.rs2
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml2
-rw-r--r--src/tools/clippy/CHANGELOG.md5
-rw-r--r--src/tools/clippy/clippy_dev/src/lint.rs10
-rw-r--r--src/tools/clippy/clippy_dev/src/serve.rs4
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/as_conversions.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs137
-rw-r--r--src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs139
-rw-r--r--src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs55
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/markdown.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/ineffective_open_options.rs95
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs125
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs47
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_string_new.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_if.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/renamed_lints.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs114
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/single_call_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/uninhabited_references.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs157
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs17
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs80
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs55
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs6
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/src/driver.rs6
-rw-r--r--src/tools/clippy/src/main.rs2
-rw-r--r--src/tools/clippy/tests/headers.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr2
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr2
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr2
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr2
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs2
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed2
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs2
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_conditions.fixed (renamed from src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed)26
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_conditions.rs (renamed from src/tools/clippy/tests/ui/blocks_in_if_conditions.rs)26
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_conditions.stderr (renamed from src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr)33
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs (renamed from src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs)21
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr (renamed from src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr)21
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed3
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs3
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr24
-rw-r--r--src/tools/clippy/tests/ui/doc_unsafe.rs2
-rw-r--r--src/tools/clippy/tests/ui/ineffective_open_options.fixed41
-rw-r--r--src/tools/clippy/tests/ui/ineffective_open_options.rs41
-rw-r--r--src/tools/clippy/tests/ui/ineffective_open_options.stderr17
-rw-r--r--src/tools/clippy/tests/ui/infallible_destructuring_match.fixed2
-rw-r--r--src/tools/clippy/tests/ui/infallible_destructuring_match.rs2
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.rs366
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.stderr259
-rw-r--r--src/tools/clippy/tests/ui/manual_filter.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_filter.rs2
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed15
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs15
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr54
-rw-r--r--src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed15
-rw-r--r--src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs15
-rw-r--r--src/tools/clippy/tests/ui/needless_if.fixed2
-rw-r--r--src/tools/clippy/tests/ui/needless_if.rs2
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.fixed2
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.rs2
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs13
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr62
-rw-r--r--src/tools/clippy/tests/ui/no_effect.rs31
-rw-r--r--src/tools/clippy/tests/ui/no_effect.stderr58
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.fixed115
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.rs115
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.stderr146
-rw-r--r--src/tools/clippy/tests/ui/regex.rs4
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed7
-rw-r--r--src/tools/clippy/tests/ui/rename.rs3
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr122
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed38
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs38
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr40
-rw-r--r--src/tools/clippy/tests/ui/uninhabited_references.rs22
-rw-r--r--src/tools/clippy/tests/ui/uninhabited_references.stderr39
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed19
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs19
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.stderr38
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr176
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs2
-rw-r--r--src/tools/clippy/tests/ui/vec.fixed34
-rw-r--r--src/tools/clippy/tests/ui/vec.rs34
-rw-r--r--src/tools/clippy/tests/ui/vec.stderr8
-rw-r--r--src/tools/clippy/triagebot.toml1
-rw-r--r--src/tools/error_index_generator/main.rs2
-rw-r--r--src/tools/miri/src/bin/miri.rs20
-rw-r--r--src/tools/miri/src/diagnostics.rs6
-rw-r--r--src/tools/rust-analyzer/.github/workflows/metrics.yaml9
-rw-r--r--src/tools/rust-analyzer/Cargo.lock11
-rw-r--r--src/tools/rust-analyzer/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/span.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs34
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs72
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs96
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/eager.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs39
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs41
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/span.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs63
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs72
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs38
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs202
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt7
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt29
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs21
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs11
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs16
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs236
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs19
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs54
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs13
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/utils.rs42
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/lib/line-index/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/xtask/src/flags.rs3
-rw-r--r--src/tools/rust-analyzer/xtask/src/metrics.rs17
-rw-r--r--src/tools/rustdoc-js/tester.js6
-rw-r--r--src/tools/rustfmt/src/parse/macros/cfg_if.rs2
-rw-r--r--src/tools/rustfmt/src/parse/macros/lazy_static.rs6
-rw-r--r--src/tools/rustfmt/src/parse/macros/mod.rs6
-rw-r--r--src/tools/rustfmt/src/parse/session.rs20
-rw-r--r--tests/codegen/debug-accessibility/crate-enum.rs26
-rw-r--r--tests/codegen/debug-accessibility/crate-struct.rs23
-rw-r--r--tests/codegen/debug-accessibility/private-enum.rs21
-rw-r--r--tests/codegen/debug-accessibility/private-struct.rs17
-rw-r--r--tests/codegen/debug-accessibility/public-enum.rs21
-rw-r--r--tests/codegen/debug-accessibility/public-struct.rs17
-rw-r--r--tests/codegen/debug-accessibility/struct-fields.rs30
-rw-r--r--tests/codegen/debug-accessibility/super-enum.rs27
-rw-r--r--tests/codegen/debug-accessibility/super-struct.rs23
-rw-r--r--tests/codegen/debug-accessibility/tuple-fields.rs24
-rw-r--r--tests/coverage/assert_not.cov-map16
-rw-r--r--tests/coverage/assert_not.coverage12
-rw-r--r--tests/coverage/assert_not.rs11
-rw-r--r--tests/coverage/async.cov-map152
-rw-r--r--tests/coverage/async.coverage43
-rw-r--r--tests/coverage/async.rs26
-rw-r--r--tests/coverage/async2.cov-map96
-rw-r--r--tests/coverage/async2.coverage80
-rw-r--r--tests/coverage/async2.rs24
-rw-r--r--tests/coverage/closure_macro_async.cov-map16
-rw-r--r--tests/coverage/closure_macro_async.coverage26
-rw-r--r--tests/coverage/closure_macro_async.rs26
-rw-r--r--tests/coverage/no_spans_if_not.cov-map8
-rw-r--r--tests/coverage/no_spans_if_not.coverage30
-rw-r--r--tests/coverage/no_spans_if_not.rs29
-rw-r--r--tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff104
-rw-r--r--tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff104
-rw-r--r--tests/mir-opt/gvn.rs18
-rw-r--r--tests/rustdoc-gui/globals.goml24
-rw-r--r--tests/rustdoc-gui/hide-mobile-topbar.goml20
-rw-r--r--tests/rustdoc-gui/links-color.goml4
-rw-r--r--tests/rustdoc-gui/sidebar-links-color.goml4
-rw-r--r--tests/rustdoc-gui/sidebar-resize-setting.goml23
-rw-r--r--tests/rustdoc-gui/sidebar-resize-window.goml37
-rw-r--r--tests/rustdoc-gui/sidebar-resize.goml28
-rw-r--r--tests/rustdoc-gui/sidebar-source-code.goml3
-rw-r--r--tests/rustdoc-gui/sidebar.goml4
-rw-r--r--tests/rustdoc-gui/src/theme_css/custom-theme.css2
-rw-r--r--tests/rustdoc-js/substring.js22
-rw-r--r--tests/rustdoc-js/substring.rs2
-rw-r--r--tests/rustdoc/reexport-cfg.rs30
-rw-r--r--tests/rustdoc/reexport-stability-tags-deprecated-and-portability.rs4
-rw-r--r--tests/rustdoc/reexport-stability-tags-unstable-and-portability.rs4
-rw-r--r--tests/ui-fulldeps/internal-lints/diagnostics.rs20
-rw-r--r--tests/ui-fulldeps/internal-lints/diagnostics.stderr24
-rw-r--r--tests/ui-fulldeps/stable-mir/check_allocation.rs22
-rw-r--r--tests/ui-fulldeps/stable-mir/check_defs.rs2
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-118144.rs16
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-118144.stderr11
-rw-r--r--tests/ui/coherence/occurs-check/associated-type.next.stderr16
-rw-r--r--tests/ui/coherence/occurs-check/associated-type.old.stderr16
-rw-r--r--tests/ui/coroutine/async-gen-yield-ty-is-unit.rs17
-rw-r--r--tests/ui/coroutine/return-types-diverge.rs20
-rw-r--r--tests/ui/coroutine/return-types.rs21
-rw-r--r--tests/ui/coroutine/return-types.stderr31
-rw-r--r--tests/ui/duplicate_entry_error.stderr7
-rw-r--r--tests/ui/entry-point/issue-118772.rs3
-rw-r--r--tests/ui/entry-point/issue-118772.stderr12
-rw-r--r--tests/ui/error-codes/E0152.stderr2
-rw-r--r--tests/ui/error-codes/E0264.stderr2
-rw-r--r--tests/ui/hygiene/cross-crate-define-and-use.rs1
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs5
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr43
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs5
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr45
-rw-r--r--tests/ui/intrinsics/const-eval-select-bad.stderr32
-rw-r--r--tests/ui/lang-items/lang-item-generic-requirements.rs3
-rw-r--r--tests/ui/lang-items/lang-item-generic-requirements.stderr37
-rw-r--r--tests/ui/lint/ptr_null_checks.rs2
-rw-r--r--tests/ui/lint/ptr_null_checks.stderr54
-rw-r--r--tests/ui/lint/reference_casting.rs9
-rw-r--r--tests/ui/lint/reference_casting.stderr102
-rw-r--r--tests/ui/macros/issue-118786.rs16
-rw-r--r--tests/ui/macros/issue-118786.stderr47
-rw-r--r--tests/ui/panic-handler/panic-handler-duplicate.stderr12
-rw-r--r--tests/ui/panic-handler/panic-handler-std.stderr6
-rw-r--r--tests/ui/parser/bad-let-else-statement.rs164
-rw-r--r--tests/ui/parser/bad-let-else-statement.stderr232
-rw-r--r--tests/ui/parser/defaultness-invalid-places-fail-semantic.rs (renamed from tests/ui/parser/trait-item-with-defaultness-fail-semantic.rs)4
-rw-r--r--tests/ui/parser/defaultness-invalid-places-fail-semantic.stderr (renamed from tests/ui/parser/trait-item-with-defaultness-fail-semantic.stderr)40
-rw-r--r--tests/ui/proc-macro/auxiliary/env.rs28
-rw-r--r--tests/ui/proc-macro/env.rs17
-rw-r--r--tests/ui/regions/resolve-re-error-ice.rs6
-rw-r--r--tests/ui/regions/resolve-re-error-ice.stderr42
-rw-r--r--tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr30
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs20
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs3
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr14
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs7
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.stderr12
-rw-r--r--tests/ui/structs-enums/type-sizes.rs1
-rw-r--r--tests/ui/traits/issue-102989.rs1
-rw-r--r--tests/ui/traits/issue-102989.stderr22
-rw-r--r--tests/ui/traits/issue-117794.rs10
-rw-r--r--tests/ui/traits/issue-117794.stderr9
-rw-r--r--tests/ui/traits/next-solver/issue-118950-root-region.rs22
-rw-r--r--tests/ui/traits/next-solver/issue-118950-root-region.stderr36
-rw-r--r--triagebot.toml20
622 files changed, 10570 insertions, 5335 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 98c030b3996..dbe5b2ec6b7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -598,7 +598,7 @@ dependencies = [
  "itertools",
  "quine-mc_cluskey",
  "regex",
- "regex-syntax 0.7.2",
+ "regex-syntax 0.8.2",
  "rustc-semver",
  "semver",
  "serde",
@@ -3196,6 +3196,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
 
 [[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
 name = "remote-test-client"
 version = "0.1.0"
 
diff --git a/RELEASES.md b/RELEASES.md
index 9eb8755dad9..a18b5264bbd 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,10 @@
+Version 1.74.1 (2023-12-07)
+===========================
+
+- [Resolved spurious STATUS_ACCESS_VIOLATIONs in LLVM](https://github.com/rust-lang/rust/pull/118464)
+- [Clarify guarantees for std::mem::discriminant](https://github.com/rust-lang/rust/pull/118006)
+- [Fix some subtyping-related regressions](https://github.com/rust-lang/rust/pull/116415)
+
 Version 1.74.0 (2023-11-16)
 ==========================
 
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 050366b081f..5252472261f 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -382,7 +382,7 @@ pub trait LayoutCalculator {
                             *offset += this_offset;
                         }
                     }
-                    _ => {
+                    FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
                         panic!("Layout of fields should be Arbitrary for variants")
                     }
                 }
@@ -600,7 +600,9 @@ pub trait LayoutCalculator {
                             variant.size = new_ity_size;
                         }
                     }
-                    _ => panic!(),
+                    FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
+                        panic!("encountered a non-arbitrary layout during enum layout")
+                    }
                 }
             }
         }
@@ -628,7 +630,7 @@ pub trait LayoutCalculator {
             let mut common_prim_initialized_in_all_variants = true;
             for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
                 let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
-                    panic!();
+                    panic!("encountered a non-arbitrary layout during enum layout");
                 };
                 // We skip *all* ZST here and later check if we are good in terms of alignment.
                 // This lets us handle some cases involving aligned ZST.
@@ -681,7 +683,7 @@ pub trait LayoutCalculator {
                         assert_eq!(memory_index.raw, [0, 1]);
                         offsets
                     }
-                    _ => panic!(),
+                    _ => panic!("encountered a non-arbitrary layout during enum layout"),
                 };
                 if pair_offsets[FieldIdx::new(0)] == Size::ZERO
                     && pair_offsets[FieldIdx::new(1)] == *offset
@@ -758,7 +760,9 @@ pub trait LayoutCalculator {
             Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
                 Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
             }
-            _ => panic!(),
+            Variants::Single { .. } => {
+                panic!("encountered a single-variant enum during multi-variant layout")
+            }
         };
         Some(best_layout.layout)
     }
@@ -1154,7 +1158,11 @@ fn univariant<
                                 assert_eq!(memory_index.raw, [0, 1]);
                                 offsets
                             }
-                            _ => panic!(),
+                            FieldsShape::Primitive
+                            | FieldsShape::Array { .. }
+                            | FieldsShape::Union(..) => {
+                                panic!("encountered a non-arbitrary layout during enum layout")
+                            }
                         };
                         if offsets[i] == pair_offsets[FieldIdx::new(0)]
                             && offsets[j] == pair_offsets[FieldIdx::new(1)]
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 5755ae8a8bc..9d543563c0f 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2845,6 +2845,28 @@ impl Item {
     pub fn span_with_attributes(&self) -> Span {
         self.attrs.iter().fold(self.span, |acc, attr| acc.to(attr.span))
     }
+
+    pub fn opt_generics(&self) -> Option<&Generics> {
+        match &self.kind {
+            ItemKind::ExternCrate(_)
+            | ItemKind::Use(_)
+            | ItemKind::Mod(_, _)
+            | ItemKind::ForeignMod(_)
+            | ItemKind::GlobalAsm(_)
+            | ItemKind::MacCall(_)
+            | ItemKind::MacroDef(_) => None,
+            ItemKind::Static(_) => None,
+            ItemKind::Const(i) => Some(&i.generics),
+            ItemKind::Fn(i) => Some(&i.generics),
+            ItemKind::TyAlias(i) => Some(&i.generics),
+            ItemKind::TraitAlias(generics, _)
+            | ItemKind::Enum(_, generics)
+            | ItemKind::Struct(_, generics)
+            | ItemKind::Union(_, generics) => Some(&generics),
+            ItemKind::Trait(i) => Some(&i.generics),
+            ItemKind::Impl(i) => Some(&i.generics),
+        }
+    }
 }
 
 /// `extern` qualifier on a function item or function type.
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 821fca6656c..4dece079783 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -40,15 +40,44 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
             | Range(_, Some(e), _)
             | Ret(Some(e))
             | Unary(_, e)
-            | Yield(Some(e)) => {
+            | Yield(Some(e))
+            | Yeet(Some(e))
+            | Become(e) => {
                 expr = e;
             }
             Closure(closure) => {
                 expr = &closure.body;
             }
             Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
-            | TryBlock(..) | While(..) => break Some(expr),
-            _ => break None,
+            | TryBlock(..) | While(..) | ConstBlock(_) => break Some(expr),
+
+            // FIXME: These can end in `}`, but changing these would break stable code.
+            InlineAsm(_) | OffsetOf(_, _) | MacCall(_) | IncludedBytes(_) | FormatArgs(_) => {
+                break None;
+            }
+
+            Break(_, None)
+            | Range(_, None, _)
+            | Ret(None)
+            | Yield(None)
+            | Array(_)
+            | Call(_, _)
+            | MethodCall(_)
+            | Tup(_)
+            | Lit(_)
+            | Cast(_, _)
+            | Type(_, _)
+            | Await(_, _)
+            | Field(_, _)
+            | Index(_, _, _)
+            | Underscore
+            | Path(_, _)
+            | Continue(_)
+            | Repeat(_, _)
+            | Paren(_)
+            | Try(_)
+            | Yeet(None)
+            | Err => break None,
         }
     }
 }
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index 50eb92125b9..92b9adf1db7 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -77,6 +77,8 @@ impl LitKind {
                 // new symbol because the string in the LitKind is different to the
                 // string in the token.
                 let s = symbol.as_str();
+                // Vanilla strings are so common we optimize for the common case where no chars
+                // requiring special behaviour are present.
                 let symbol = if s.contains(['\\', '\r']) {
                     let mut buf = String::with_capacity(s.len());
                     let mut error = Ok(());
@@ -104,27 +106,20 @@ impl LitKind {
                 LitKind::Str(symbol, ast::StrStyle::Cooked)
             }
             token::StrRaw(n) => {
-                // Ditto.
-                let s = symbol.as_str();
-                let symbol =
-                    if s.contains('\r') {
-                        let mut buf = String::with_capacity(s.len());
-                        let mut error = Ok(());
-                        unescape_literal(s, Mode::RawStr, &mut |_, unescaped_char| {
-                            match unescaped_char {
-                                Ok(c) => buf.push(c),
-                                Err(err) => {
-                                    if err.is_fatal() {
-                                        error = Err(LitError::LexerError);
-                                    }
-                                }
+                // Raw strings have no escapes, so we only need to check for invalid chars, and we
+                // can reuse the symbol on success.
+                let mut error = Ok(());
+                unescape_literal(symbol.as_str(), Mode::RawStr, &mut |_, unescaped_char| {
+                    match unescaped_char {
+                        Ok(_) => {}
+                        Err(err) => {
+                            if err.is_fatal() {
+                                error = Err(LitError::LexerError);
                             }
-                        });
-                        error?;
-                        Symbol::intern(&buf)
-                    } else {
-                        symbol
-                    };
+                        }
+                    }
+                });
+                error?;
                 LitKind::Str(symbol, ast::StrStyle::Raw(n))
             }
             token::ByteStr => {
@@ -143,25 +138,19 @@ impl LitKind {
                 LitKind::ByteStr(buf.into(), StrStyle::Cooked)
             }
             token::ByteStrRaw(n) => {
+                // Raw strings have no escapes, so we only need to check for invalid chars, and we
+                // can convert the symbol directly to a `Lrc<u8>` on success.
                 let s = symbol.as_str();
-                let bytes = if s.contains('\r') {
-                    let mut buf = Vec::with_capacity(s.len());
-                    let mut error = Ok(());
-                    unescape_literal(s, Mode::RawByteStr, &mut |_, c| match c {
-                        Ok(c) => buf.push(byte_from_char(c)),
-                        Err(err) => {
-                            if err.is_fatal() {
-                                error = Err(LitError::LexerError);
-                            }
+                let mut error = Ok(());
+                unescape_literal(s, Mode::RawByteStr, &mut |_, c| match c {
+                    Ok(_) => {}
+                    Err(err) => {
+                        if err.is_fatal() {
+                            error = Err(LitError::LexerError);
                         }
-                    });
-                    error?;
-                    buf
-                } else {
-                    symbol.to_string().into_bytes()
-                };
-
-                LitKind::ByteStr(bytes.into(), StrStyle::Raw(n))
+                    }
+                });
+                LitKind::ByteStr(s.to_owned().into_bytes().into(), StrStyle::Raw(n))
             }
             token::CStr => {
                 let s = symbol.as_str();
@@ -172,7 +161,6 @@ impl LitKind {
                         error = Err(LitError::NulInCStr(span));
                     }
                     Ok(CStrUnit::Byte(b)) => buf.push(b),
-                    Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
                     Ok(CStrUnit::Char(c)) => {
                         buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
                     }
@@ -187,18 +175,15 @@ impl LitKind {
                 LitKind::CStr(buf.into(), StrStyle::Cooked)
             }
             token::CStrRaw(n) => {
+                // Raw strings have no escapes, so we only need to check for invalid chars, and we
+                // can convert the symbol directly to a `Lrc<u8>` on success.
                 let s = symbol.as_str();
-                let mut buf = Vec::with_capacity(s.len());
                 let mut error = Ok(());
                 unescape_c_string(s, Mode::RawCStr, &mut |span, c| match c {
                     Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
                         error = Err(LitError::NulInCStr(span));
                     }
-                    Ok(CStrUnit::Byte(b)) => buf.push(b),
-                    Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
-                    Ok(CStrUnit::Char(c)) => {
-                        buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
-                    }
+                    Ok(_) => {}
                     Err(err) => {
                         if err.is_fatal() {
                             error = Err(LitError::LexerError);
@@ -206,6 +191,7 @@ impl LitKind {
                     }
                 });
                 error?;
+                let mut buf = s.to_owned().into_bytes();
                 buf.push(0);
                 LitKind::CStr(buf.into(), StrStyle::Raw(n))
             }
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 7111daa2ea9..4c81983c242 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -336,7 +336,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         hir::InlineAsmOperand::Const { .. }
                         | hir::InlineAsmOperand::SymFn { .. }
                         | hir::InlineAsmOperand::SymStatic { .. } => {
-                            unreachable!()
+                            unreachable!("{op:?} is not a register operand");
                         }
                     };
 
@@ -380,7 +380,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                     {
                                         reg_sym.as_str()
                                     } else {
-                                        unreachable!();
+                                        unreachable!("{op:?} is not a register operand");
                                     }
                                 };
 
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 0bbdcb7f09e..704f124dbcb 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -917,12 +917,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let poll_expr = {
             let awaitee = self.expr_ident(span, awaitee_ident, awaitee_pat_hid);
             let ref_mut_awaitee = self.expr_mut_addr_of(span, awaitee);
-            let task_context = if let Some(task_context_hid) = self.task_context {
-                self.expr_ident_mut(span, task_context_ident, task_context_hid)
-            } else {
-                // Use of `await` outside of an async context, we cannot use `task_context` here.
-                self.expr_err(span, self.tcx.sess.span_delayed_bug(span, "no task_context hir id"))
+
+            let Some(task_context_hid) = self.task_context else {
+                unreachable!("use of `await` outside of an async context.");
             };
+
+            let task_context = self.expr_ident_mut(span, task_context_ident, task_context_hid);
+
             let new_unchecked = self.expr_call_lang_item_fn_mut(
                 span,
                 hir::LangItem::PinNewUnchecked,
@@ -991,16 +992,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
             );
             let yield_expr = self.arena.alloc(yield_expr);
 
-            if let Some(task_context_hid) = self.task_context {
-                let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
-                let assign =
-                    self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)));
-                self.stmt_expr(span, assign)
-            } else {
-                // Use of `await` outside of an async context. Return `yield_expr` so that we can
-                // proceed with type checking.
-                self.stmt(span, hir::StmtKind::Semi(yield_expr))
-            }
+            let Some(task_context_hid) = self.task_context else {
+                unreachable!("use of `await` outside of an async context.");
+            };
+
+            let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
+            let assign =
+                self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)));
+            self.stmt_expr(span, assign)
         };
 
         let loop_block = self.block_all(span, arena_vec![self; inner_match_stmt, yield_stmt], None);
@@ -1260,9 +1259,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         );
 
         // `a = lhs1; b = lhs2;`.
-        let stmts = self
-            .arena
-            .alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));
+        let stmts = self.arena.alloc_from_iter(std::iter::once(destructure_let).chain(assignments));
 
         // Wrap everything in a block.
         hir::ExprKind::Block(self.block_all(whole_span, stmts, None), None)
@@ -1637,19 +1634,32 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
         };
 
-        let mut yielded =
+        let yielded =
             opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
 
         if is_async_gen {
-            // yield async_gen_ready($expr);
-            yielded = self.expr_call_lang_item_fn(
+            // `yield $expr` is transformed into `task_context = yield async_gen_ready($expr)`.
+            // This ensures that we store our resumed `ResumeContext` correctly, and also that
+            // the apparent value of the `yield` expression is `()`.
+            let wrapped_yielded = self.expr_call_lang_item_fn(
                 span,
                 hir::LangItem::AsyncGenReady,
                 std::slice::from_ref(yielded),
             );
-        }
+            let yield_expr = self.arena.alloc(
+                self.expr(span, hir::ExprKind::Yield(wrapped_yielded, hir::YieldSource::Yield)),
+            );
 
-        hir::ExprKind::Yield(yielded, hir::YieldSource::Yield)
+            let Some(task_context_hid) = self.task_context else {
+                unreachable!("use of `await` outside of an async context.");
+            };
+            let task_context_ident = Ident::with_dummy_span(sym::_task_context);
+            let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
+
+            hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span))
+        } else {
+            hir::ExprKind::Yield(yielded, hir::YieldSource::Yield)
+        }
     }
 
     /// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 7c05724f64c..83452c22280 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -421,8 +421,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
             ItemKind::MacroDef(MacroDef { body, macro_rules }) => {
                 let body = P(self.lower_delim_args(body));
-                let DefKind::Macro(macro_kind) = self.tcx.def_kind(self.local_def_id(id)) else {
-                    unreachable!()
+                let def_id = self.local_def_id(id);
+                let def_kind = self.tcx.def_kind(def_id);
+                let DefKind::Macro(macro_kind) = def_kind else {
+                    unreachable!(
+                        "expected DefKind::Macro for macro item, found {}",
+                        def_kind.descr(def_id.to_def_id())
+                    );
                 };
                 let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
                 hir::ItemKind::Macro(macro_def, macro_kind)
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index d9663d50c59..96ed3eee02e 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -59,10 +59,8 @@ use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
 use rustc_index::{Idx, IndexSlice, IndexVec};
-use rustc_middle::{
-    span_bug,
-    ty::{ResolverAstLowering, TyCtxt},
-};
+use rustc_middle::span_bug;
+use rustc_middle::ty::{ResolverAstLowering, TyCtxt, Visibility};
 use rustc_session::parse::{add_feature_diagnostics, feature_err};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{DesugaringKind, Span, DUMMY_SP};
@@ -453,6 +451,7 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
     tcx.ensure_with_value().output_filenames(());
     tcx.ensure_with_value().early_lint_checks(());
     tcx.ensure_with_value().debugger_visualizers(LOCAL_CRATE);
+    tcx.ensure_with_value().get_lang_items(());
     let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal();
 
     let ast_index = index_crate(&resolver.node_id_to_def_id, &krate);
@@ -765,6 +764,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         self.resolver.get_import_res(id).present_items()
     }
 
+    fn make_lang_item_path(
+        &mut self,
+        lang_item: hir::LangItem,
+        span: Span,
+        args: Option<&'hir hir::GenericArgs<'hir>>,
+    ) -> &'hir hir::Path<'hir> {
+        let def_id = self.tcx.require_lang_item(lang_item, Some(span));
+        let def_kind = self.tcx.def_kind(def_id);
+        let res = Res::Def(def_kind, def_id);
+        self.arena.alloc(hir::Path {
+            span,
+            res,
+            segments: self.arena.alloc_from_iter([hir::PathSegment {
+                ident: Ident::new(lang_item.name(), span),
+                hir_id: self.next_id(),
+                res,
+                args,
+                infer_args: false,
+            }]),
+        })
+    }
+
     /// Reuses the span but adds information like the kind of the desugaring and features that are
     /// allowed inside this span.
     fn mark_span_with_reason(
@@ -1630,6 +1651,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         );
         debug!(?opaque_ty_def_id);
 
+        // Meaningless, but provided so that all items have visibilities.
+        let parent_mod = self.tcx.parent_module_from_def_id(opaque_ty_def_id).to_def_id();
+        self.tcx.feed_local_def_id(opaque_ty_def_id).visibility(Visibility::Restricted(parent_mod));
+
         // Map from captured (old) lifetime to synthetic (new) lifetime.
         // Used to resolve lifetimes in the bounds of the opaque.
         let mut captured_to_synthesized_mapping = FxHashMap::default();
@@ -1976,18 +2001,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             CoroutineKind::AsyncGen { .. } => (sym::Item, hir::LangItem::AsyncIterator),
         };
 
-        let future_args = self.arena.alloc(hir::GenericArgs {
+        let bound_args = self.arena.alloc(hir::GenericArgs {
             args: &[],
             bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, opaque_ty_span, output_ty)],
             parenthesized: hir::GenericArgsParentheses::No,
             span_ext: DUMMY_SP,
         });
 
-        hir::GenericBound::LangItemTrait(
-            trait_lang_item,
-            opaque_ty_span,
-            self.next_id(),
-            future_args,
+        hir::GenericBound::Trait(
+            hir::PolyTraitRef {
+                bound_generic_params: &[],
+                trait_ref: hir::TraitRef {
+                    path: self.make_lang_item_path(
+                        trait_lang_item,
+                        opaque_ty_span,
+                        Some(bound_args),
+                    ),
+                    hir_ref_id: self.next_id(),
+                },
+                span: opaque_ty_span,
+            },
+            hir::TraitBoundModifier::None,
         )
     }
 
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index efd80af5ef4..5ceeb72f291 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -9,6 +9,7 @@ use rustc_ast::{self as ast, *};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, PartialRes, Res};
 use rustc_hir::GenericArg;
+use rustc_middle::span_bug;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 
@@ -139,7 +140,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         // We should've returned in the for loop above.
 
-        self.tcx.sess.diagnostic().span_bug(
+        self.tcx.sess.dcx().span_bug(
             p.span,
             format!(
                 "lower_qpath: no final extension segment in {}..{}",
@@ -285,7 +286,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let (start, end) = match self.resolver.get_lifetime_res(segment_id) {
             Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),
             None => return,
-            Some(_) => panic!(),
+            Some(res) => {
+                span_bug!(path_span, "expected an elided lifetime to insert. found {res:?}")
+            }
         };
         let expected_lifetimes = end.as_usize() - start.as_usize();
         debug!(expected_lifetimes);
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 28bd6c2111b..790b583134c 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -225,7 +225,8 @@ ast_passes_tilde_const_disallowed = `~const` is not allowed here
     .closure = closures cannot have `~const` trait bounds
     .function = this function is not `const`, so it cannot have `~const` trait bounds
     .trait = this trait is not a `#[const_trait]`, so it cannot have `~const` trait bounds
-    .impl = this impl is not `const`, so it cannot have `~const` trait bounds
+    .trait_impl = this impl is not `const`, so it cannot have `~const` trait bounds
+    .impl = inherent impls cannot have `~const` trait bounds
     .object = trait objects cannot have `~const` trait bounds
     .item = this item cannot have `~const` trait bounds
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 59cf18c2459..e0a7b06c050 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -41,6 +41,7 @@ enum DisallowTildeConstContext<'a> {
     TraitObject,
     Fn(FnKind<'a>),
     Trait(Span),
+    TraitImpl(Span),
     Impl(Span),
     Item,
 }
@@ -220,8 +221,8 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn err_handler(&self) -> &rustc_errors::Handler {
-        self.session.diagnostic()
+    fn dcx(&self) -> &rustc_errors::DiagCtxt {
+        self.session.dcx()
     }
 
     fn check_lifetime(&self, ident: Ident) {
@@ -269,7 +270,7 @@ impl<'a> AstValidator<'a> {
         ) {
             return;
         }
-        self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
+        self.dcx().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
     }
 
     fn deny_anon_struct_or_union(&self, ty: &Ty) {
@@ -278,15 +279,14 @@ impl<'a> AstValidator<'a> {
             TyKind::AnonUnion(..) => "union",
             _ => return,
         };
-        self.err_handler()
-            .emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
+        self.dcx().emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
     }
 
     fn deny_unnamed_field(&self, field: &FieldDef) {
         if let Some(ident) = field.ident
             && ident.name == kw::Underscore
         {
-            self.err_handler()
+            self.dcx()
                 .emit_err(errors::InvalidUnnamedField { span: field.span, ident_span: ident.span });
         }
     }
@@ -392,7 +392,7 @@ impl<'a> AstValidator<'a> {
             [b0] => b0.span(),
             [b0, .., bl] => b0.span().to(bl.span()),
         };
-        self.err_handler().emit_err(errors::BoundInContext { span, ctx });
+        self.dcx().emit_err(errors::BoundInContext { span, ctx });
     }
 
     fn check_foreign_ty_genericless(
@@ -402,7 +402,7 @@ impl<'a> AstValidator<'a> {
         after_where_clause: &TyAliasWhereClause,
     ) {
         let cannot_have = |span, descr, remove_descr| {
-            self.err_handler().emit_err(errors::ExternTypesCannotHave {
+            self.dcx().emit_err(errors::ExternTypesCannotHave {
                 span,
                 descr,
                 remove_descr,
@@ -428,7 +428,7 @@ impl<'a> AstValidator<'a> {
         let Some(body) = body else {
             return;
         };
-        self.err_handler().emit_err(errors::BodyInExtern {
+        self.dcx().emit_err(errors::BodyInExtern {
             span: ident.span,
             body,
             block: self.current_extern_span(),
@@ -441,7 +441,7 @@ impl<'a> AstValidator<'a> {
         let Some(body) = body else {
             return;
         };
-        self.err_handler().emit_err(errors::FnBodyInExtern {
+        self.dcx().emit_err(errors::FnBodyInExtern {
             span: ident.span,
             body: body.span,
             block: self.current_extern_span(),
@@ -455,7 +455,7 @@ impl<'a> AstValidator<'a> {
     /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
     fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) {
         if header.has_qualifiers() {
-            self.err_handler().emit_err(errors::FnQualifierInExtern {
+            self.dcx().emit_err(errors::FnQualifierInExtern {
                 span: ident.span,
                 block: self.current_extern_span(),
                 sugg_span: span.until(ident.span.shrink_to_lo()),
@@ -466,7 +466,7 @@ impl<'a> AstValidator<'a> {
     /// An item in `extern { ... }` cannot use non-ascii identifier.
     fn check_foreign_item_ascii_only(&self, ident: Ident) {
         if !ident.as_str().is_ascii() {
-            self.err_handler().emit_err(errors::ExternItemAscii {
+            self.dcx().emit_err(errors::ExternItemAscii {
                 span: ident.span,
                 block: self.current_extern_span(),
             });
@@ -495,7 +495,7 @@ impl<'a> AstValidator<'a> {
             if let Const::Yes(const_span) = header.constness {
                 let mut spans = variadic_spans.clone();
                 spans.push(const_span);
-                self.err_handler().emit_err(errors::ConstAndCVariadic {
+                self.dcx().emit_err(errors::ConstAndCVariadic {
                     spans,
                     const_span,
                     variadic_spans: variadic_spans.clone(),
@@ -517,14 +517,14 @@ impl<'a> AstValidator<'a> {
             _ => {}
         };
 
-        self.err_handler().emit_err(errors::BadCVariadic { span: variadic_spans });
+        self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
     }
 
     fn check_item_named(&self, ident: Ident, kind: &str) {
         if ident.name != kw::Underscore {
             return;
         }
-        self.err_handler().emit_err(errors::ItemUnderscore { span: ident.span, kind });
+        self.dcx().emit_err(errors::ItemUnderscore { span: ident.span, kind });
     }
 
     fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) {
@@ -615,7 +615,7 @@ impl<'a> AstValidator<'a> {
         let args_len = arg_spans.len();
         let constraint_len = constraint_spans.len();
         // ...and then error:
-        self.err_handler().emit_err(errors::ArgsBeforeConstraint {
+        self.dcx().emit_err(errors::ArgsBeforeConstraint {
             arg_spans: arg_spans.clone(),
             constraints: constraint_spans[0],
             args: *arg_spans.iter().last().unwrap(),
@@ -667,7 +667,7 @@ impl<'a> AstValidator<'a> {
                 }
 
                 if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) {
-                    self.err_handler().emit_err(errors::AtLeastOneTrait { span: ty.span });
+                    self.dcx().emit_err(errors::AtLeastOneTrait { span: ty.span });
                 }
             }
             _ => {}
@@ -697,7 +697,7 @@ impl<'a> AstValidator<'a> {
 /// Checks that generic parameters are in the correct order,
 /// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`)
 fn validate_generic_param_order(
-    handler: &rustc_errors::Handler,
+    dcx: &rustc_errors::DiagCtxt,
     generics: &[GenericParam],
     span: Span,
 ) {
@@ -760,7 +760,7 @@ fn validate_generic_param_order(
         ordered_params += ">";
 
         for (param_ord, (max_param, spans)) in &out_of_order {
-            handler.emit_err(errors::OutOfOrderParams {
+            dcx.emit_err(errors::OutOfOrderParams {
                 spans: spans.clone(),
                 sugg_span: span,
                 param_ord,
@@ -823,7 +823,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         errors::VisibilityNotPermittedNote::TraitImpl,
                     );
                     if let TyKind::Err = self_ty.kind {
-                        this.err_handler().emit_err(errors::ObsoleteAuto { span: item.span });
+                        this.dcx().emit_err(errors::ObsoleteAuto { span: item.span });
                     }
                     if let (&Unsafe::Yes(span), &ImplPolarity::Negative(sp)) = (unsafety, polarity)
                     {
@@ -837,7 +837,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     this.visit_vis(&item.vis);
                     this.visit_ident(item.ident);
                     let disallowed = matches!(constness, Const::No)
-                        .then(|| DisallowTildeConstContext::Impl(item.span));
+                        .then(|| DisallowTildeConstContext::TraitImpl(item.span));
                     this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
                     this.visit_trait_ref(t);
                     this.visit_ty(self_ty);
@@ -871,7 +871,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     errors::VisibilityNotPermittedNote::IndividualImplItems,
                 );
                 if let &Unsafe::Yes(span) = unsafety {
-                    self.err_handler().emit_err(errors::InherentImplCannotUnsafe {
+                    self.dcx().emit_err(errors::InherentImplCannotUnsafe {
                         span: self_ty.span,
                         annotation_span: span,
                         annotation: "unsafe",
@@ -879,18 +879,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     });
                 }
                 if let &ImplPolarity::Negative(span) = polarity {
-                    self.err_handler().emit_err(error(span, "negative", false));
+                    self.dcx().emit_err(error(span, "negative", false));
                 }
                 if let &Defaultness::Default(def_span) = defaultness {
-                    self.err_handler().emit_err(error(def_span, "`default`", true));
+                    self.dcx().emit_err(error(def_span, "`default`", true));
                 }
                 if let &Const::Yes(span) = constness {
-                    self.err_handler().emit_err(error(span, "`const`", true));
+                    self.dcx().emit_err(error(span, "`const`", true));
                 }
 
                 self.visit_vis(&item.vis);
                 self.visit_ident(item.ident);
-                self.with_tilde_const(None, |this| this.visit_generics(generics));
+                self.with_tilde_const(Some(DisallowTildeConstContext::Impl(item.span)), |this| {
+                    this.visit_generics(generics)
+                });
                 self.visit_ty(self_ty);
                 walk_list!(self, visit_assoc_item, items, AssocCtxt::Impl);
                 walk_list!(self, visit_attribute, &item.attrs);
@@ -937,7 +939,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     errors::VisibilityNotPermittedNote::IndividualForeignItems,
                 );
                 if let &Unsafe::Yes(span) = unsafety {
-                    self.err_handler().emit_err(errors::UnsafeItem { span, kind: "extern block" });
+                    self.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" });
                 }
                 if abi.is_none() {
                     self.maybe_lint_missing_abi(item.span, item.id);
@@ -988,7 +990,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
             ItemKind::Mod(unsafety, mod_kind) => {
                 if let &Unsafe::Yes(span) = unsafety {
-                    self.err_handler().emit_err(errors::UnsafeItem { span, kind: "module" });
+                    self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
                 }
                 // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
                 if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
@@ -1011,7 +1013,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             },
             ItemKind::Union(vdata, generics) => {
                 if vdata.fields().is_empty() {
-                    self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
+                    self.dcx().emit_err(errors::FieldlessUnion { span: item.span });
                 }
                 match vdata {
                     VariantData::Struct(fields, ..) => {
@@ -1026,12 +1028,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     _ => {}
                 }
             }
-            ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
+            ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
                 self.check_defaultness(item.span, *defaultness);
-                self.session.emit_err(errors::ConstWithoutBody {
-                    span: item.span,
-                    replace_span: self.ending_semi_or_hi(item.span),
-                });
+                if expr.is_none() {
+                    self.session.emit_err(errors::ConstWithoutBody {
+                        span: item.span,
+                        replace_span: self.ending_semi_or_hi(item.span),
+                    });
+                }
             }
             ItemKind::Static(box StaticItem { expr: None, .. }) => {
                 self.session.emit_err(errors::StaticWithoutBody {
@@ -1053,10 +1057,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
                 if self.features.lazy_type_alias {
                     if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
-                        self.err_handler().emit_err(err);
+                        self.dcx().emit_err(err);
                     }
                 } else if where_clauses.1.0 {
-                    self.err_handler().emit_err(errors::WhereClauseAfterTypeAlias {
+                    self.dcx().emit_err(errors::WhereClauseAfterTypeAlias {
                         span: where_clauses.1.1,
                         help: self.session.is_nightly_build().then_some(()),
                     });
@@ -1141,14 +1145,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
                 GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
                     if let Some(span) = prev_param_default {
-                        self.err_handler().emit_err(errors::GenericDefaultTrailing { span });
+                        self.dcx().emit_err(errors::GenericDefaultTrailing { span });
                         break;
                     }
                 }
             }
         }
 
-        validate_generic_param_order(self.err_handler(), &generics.params, generics.span);
+        validate_generic_param_order(self.dcx(), &generics.params, generics.span);
 
         for predicate in &generics.where_clause.predicates {
             if let WherePredicate::EqPredicate(predicate) = predicate {
@@ -1169,7 +1173,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                             match bound {
                                 GenericBound::Trait(t, _) => {
                                     if !t.bound_generic_params.is_empty() {
-                                        self.err_handler()
+                                        self.dcx()
                                             .emit_err(errors::NestedLifetimes { span: t.span });
                                     }
                                 }
@@ -1195,13 +1199,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         if let GenericBound::Trait(poly, modify) = bound {
             match (ctxt, modify) {
                 (BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
-                    self.err_handler().emit_err(errors::OptionalTraitSupertrait {
+                    self.dcx().emit_err(errors::OptionalTraitSupertrait {
                         span: poly.span,
                         path_str: pprust::path_to_string(&poly.trait_ref.path),
                     });
                 }
                 (BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
-                    self.err_handler().emit_err(errors::OptionalTraitObject { span: poly.span });
+                    self.dcx().emit_err(errors::OptionalTraitObject { span: poly.span });
                 }
                 (_, &TraitBoundModifier::MaybeConst(span))
                     if let Some(reason) = &self.disallow_tilde_const =>
@@ -1216,7 +1220,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         &DisallowTildeConstContext::Trait(span) => {
                             errors::TildeConstReason::Trait { span }
                         }
+                        &DisallowTildeConstContext::TraitImpl(span) => {
+                            errors::TildeConstReason::TraitImpl { span }
+                        }
                         &DisallowTildeConstContext::Impl(span) => {
+                            // FIXME(effects): Consider providing a help message or even a structured
+                            // suggestion for moving such bounds to the assoc const fns if available.
                             errors::TildeConstReason::Impl { span }
                         }
                         DisallowTildeConstContext::TraitObject => {
@@ -1224,16 +1233,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         }
                         DisallowTildeConstContext::Item => errors::TildeConstReason::Item,
                     };
-                    self.err_handler().emit_err(errors::TildeConstDisallowed { span, reason });
+                    self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
                 }
                 (_, TraitBoundModifier::MaybeConstMaybe) => {
-                    self.err_handler().emit_err(errors::OptionalConstExclusive {
+                    self.dcx().emit_err(errors::OptionalConstExclusive {
                         span: bound.span(),
                         modifier: "?",
                     });
                 }
                 (_, TraitBoundModifier::MaybeConstNegative) => {
-                    self.err_handler().emit_err(errors::OptionalConstExclusive {
+                    self.dcx().emit_err(errors::OptionalConstExclusive {
                         span: bound.span(),
                         modifier: "!",
                     });
@@ -1249,7 +1258,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         {
             for arg in &args.args {
                 if let ast::AngleBracketedArg::Constraint(constraint) = arg {
-                    self.err_handler()
+                    self.dcx()
                         .emit_err(errors::ConstraintOnNegativeBound { span: constraint.span });
                 }
             }
@@ -1281,7 +1290,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 | CoroutineKind::AsyncGen { span: aspan, .. } => aspan,
             };
             // FIXME(gen_blocks): Report a different error for `const gen`
-            self.err_handler().emit_err(errors::ConstAndAsync {
+            self.dcx().emit_err(errors::ConstAndAsync {
                 spans: vec![cspan, aspan],
                 cspan,
                 aspan,
@@ -1321,10 +1330,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     }
                 } else {
                     match ctxt {
-                        FnCtxt::Foreign => {
-                            self.err_handler().emit_err(errors::PatternInForeign { span })
-                        }
-                        _ => self.err_handler().emit_err(errors::PatternInBodiless { span }),
+                        FnCtxt::Foreign => self.dcx().emit_err(errors::PatternInForeign { span }),
+                        _ => self.dcx().emit_err(errors::PatternInBodiless { span }),
                     };
                 }
             });
@@ -1523,7 +1530,7 @@ fn deny_equality_constraints(
             }
         }
     }
-    this.err_handler().emit_err(err);
+    this.dcx().emit_err(err);
 }
 
 pub fn check_crate(
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 928bf19759a..4283fc7c07d 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -563,6 +563,11 @@ pub enum TildeConstReason {
         #[primary_span]
         span: Span,
     },
+    #[note(ast_passes_trait_impl)]
+    TraitImpl {
+        #[primary_span]
+        span: Span,
+    },
     #[note(ast_passes_impl)]
     Impl {
         #[primary_span]
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index ac55c6cabd0..142cdd15e64 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -102,7 +102,7 @@ impl<'a> PostExpansionVisitor<'a> {
             }
             Err(abi::AbiDisabled::Unrecognized) => {
                 if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {
-                    self.sess.diagnostic().span_delayed_bug(
+                    self.sess.dcx().span_delayed_bug(
                         span,
                         format!(
                             "unrecognized ABI not caught in lowering: {}",
@@ -654,7 +654,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
             if all_stable {
                 err.sugg = Some(attr.span);
             }
-            sess.diagnostic().emit_err(err);
+            sess.dcx().emit_err(err);
         }
     }
 }
diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs
index 280cf3284c3..9882f1d23ce 100644
--- a/compiler/rustc_ast_passes/src/show_span.rs
+++ b/compiler/rustc_ast_passes/src/show_span.rs
@@ -31,37 +31,37 @@ impl FromStr for Mode {
 }
 
 struct ShowSpanVisitor<'a> {
-    span_diagnostic: &'a rustc_errors::Handler,
+    dcx: &'a rustc_errors::DiagCtxt,
     mode: Mode,
 }
 
 impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
     fn visit_expr(&mut self, e: &'a ast::Expr) {
         if let Mode::Expression = self.mode {
-            self.span_diagnostic.emit_warning(errors::ShowSpan { span: e.span, msg: "expression" });
+            self.dcx.emit_warning(errors::ShowSpan { span: e.span, msg: "expression" });
         }
         visit::walk_expr(self, e);
     }
 
     fn visit_pat(&mut self, p: &'a ast::Pat) {
         if let Mode::Pattern = self.mode {
-            self.span_diagnostic.emit_warning(errors::ShowSpan { span: p.span, msg: "pattern" });
+            self.dcx.emit_warning(errors::ShowSpan { span: p.span, msg: "pattern" });
         }
         visit::walk_pat(self, p);
     }
 
     fn visit_ty(&mut self, t: &'a ast::Ty) {
         if let Mode::Type = self.mode {
-            self.span_diagnostic.emit_warning(errors::ShowSpan { span: t.span, msg: "type" });
+            self.dcx.emit_warning(errors::ShowSpan { span: t.span, msg: "type" });
         }
         visit::walk_ty(self, t);
     }
 }
 
-pub fn run(span_diagnostic: &rustc_errors::Handler, mode: &str, krate: &ast::Crate) {
+pub fn run(dcx: &rustc_errors::DiagCtxt, mode: &str, krate: &ast::Crate) {
     let Ok(mode) = mode.parse() else {
         return;
     };
-    let mut v = ShowSpanVisitor { span_diagnostic, mode };
+    let mut v = ShowSpanVisitor { dcx, mode };
     visit::walk_crate(&mut v, krate);
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 4a2a693862b..d6c15ec35b6 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1597,7 +1597,9 @@ impl<'a> State<'a> {
             }
             match bound {
                 ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
-                _ => panic!(),
+                _ => {
+                    panic!("expected a lifetime bound, found a trait bound")
+                }
             }
         }
     }
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index a6930fe0a17..0959e8d3043 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -945,7 +945,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
     assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
     use ReprAttr::*;
     let mut acc = Vec::new();
-    let diagnostic = sess.diagnostic();
+    let diagnostic = sess.dcx();
 
     if let Some(items) = attr.meta_item_list() {
         for item in items {
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
index ca9bbd28b95..ce8e04defb2 100644
--- a/compiler/rustc_attr/src/session_diagnostics.rs
+++ b/compiler/rustc_attr/src/session_diagnostics.rs
@@ -2,7 +2,7 @@ use std::num::IntErrorKind;
 
 use rustc_ast as ast;
 use rustc_errors::{
-    error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
+    error_code, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
 };
 use rustc_macros::Diagnostic;
 use rustc_span::{Span, Symbol};
@@ -51,9 +51,9 @@ pub(crate) struct UnknownMetaItem<'a> {
 
 // Manual implementation to be able to format `expected` items correctly.
 impl<'a> IntoDiagnostic<'a> for UnknownMetaItem<'_> {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
-        let mut diag = handler.struct_span_err_with_code(
+        let mut diag = dcx.struct_span_err_with_code(
             self.span,
             fluent::attr_unknown_meta_item,
             error_code!(E0541),
@@ -201,8 +201,8 @@ pub(crate) struct UnsupportedLiteral {
 }
 
 impl<'a> IntoDiagnostic<'a> for UnsupportedLiteral {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag = handler.struct_span_err_with_code(
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let mut diag = dcx.struct_span_err_with_code(
             self.span,
             match self.reason {
                 UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 2f366001d4b..db0f4559a6b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1138,7 +1138,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             });
         } else {
             issued_spans.var_subdiag(
-                Some(self.infcx.tcx.sess.diagnostic()),
+                Some(self.infcx.tcx.sess.dcx()),
                 &mut err,
                 Some(issued_borrow.kind),
                 |kind, var_span| {
@@ -1155,7 +1155,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             );
 
             borrow_spans.var_subdiag(
-                Some(self.infcx.tcx.sess.diagnostic()),
+                Some(self.infcx.tcx.sess.dcx()),
                 &mut err,
                 Some(gen_borrow_kind),
                 |kind, var_span| {
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 1616b5e99bf..ee321365470 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -124,7 +124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         let did = did.expect_local();
                         if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
                             diag.eager_subdiagnostic(
-                                self.infcx.tcx.sess.diagnostic(),
+                                self.infcx.tcx.sess.dcx(),
                                 OnClosureNote::InvokedTwice {
                                     place_name: &ty::place_to_string_for_capture(
                                         self.infcx.tcx,
@@ -146,7 +146,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 let did = did.expect_local();
                 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
                     diag.eager_subdiagnostic(
-                        self.infcx.tcx.sess.diagnostic(),
+                        self.infcx.tcx.sess.dcx(),
                         OnClosureNote::MovedTwice {
                             place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
                             span: *span,
@@ -624,7 +624,7 @@ impl UseSpans<'_> {
     /// Add a subdiagnostic to the use of the captured variable, if it exists.
     pub(super) fn var_subdiag(
         self,
-        handler: Option<&rustc_errors::Handler>,
+        dcx: Option<&rustc_errors::DiagCtxt>,
         err: &mut Diagnostic,
         kind: Option<rustc_middle::mir::BorrowKind>,
         f: impl FnOnce(Option<CoroutineKind>, Span) -> CaptureVarCause,
@@ -646,7 +646,7 @@ impl UseSpans<'_> {
                 });
             };
             let diag = f(coroutine_kind, path_span);
-            match handler {
+            match dcx {
                 Some(hd) => err.eager_subdiagnostic(hd, diag),
                 None => err.subdiagnostic(diag),
             };
@@ -1150,7 +1150,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             && self.infcx.can_eq(self.param_env, ty, self_ty)
                         {
                             err.eager_subdiagnostic(
-                                self.infcx.tcx.sess.diagnostic(),
+                                self.infcx.tcx.sess.dcx(),
                                 CaptureReasonSuggest::FreshReborrow {
                                     span: move_span.shrink_to_hi(),
                                 },
diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
index 94981c45582..66275888c50 100644
--- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
@@ -206,7 +206,7 @@ impl OutlivesSuggestionBuilder {
         // If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
         // list of diagnostics.
         let mut diag = if suggested.len() == 1 {
-            mbcx.infcx.tcx.sess.diagnostic().struct_help(match suggested.last().unwrap() {
+            mbcx.infcx.tcx.sess.dcx().struct_help(match suggested.last().unwrap() {
                 SuggestedConstraint::Outlives(a, bs) => {
                     let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
                     format!("add bound `{a}: {}`", bs.join(" + "))
@@ -223,7 +223,7 @@ impl OutlivesSuggestionBuilder {
                 .infcx
                 .tcx
                 .sess
-                .diagnostic()
+                .dcx()
                 .struct_help("the following changes may resolve your lifetime errors");
 
             // Add suggestions.
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 4c3cef2e763..8441dfaa7df 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -788,28 +788,18 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
         };
         let opaque_ty = hir.item(id);
         if let hir::ItemKind::OpaqueTy(hir::OpaqueTy {
-            bounds:
-                [
-                    hir::GenericBound::LangItemTrait(
-                        hir::LangItem::Future,
-                        _,
-                        _,
-                        hir::GenericArgs {
-                            bindings:
-                                [
-                                    hir::TypeBinding {
-                                        ident: Ident { name: sym::Output, .. },
-                                        kind:
-                                            hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) },
-                                        ..
-                                    },
-                                ],
-                            ..
-                        },
-                    ),
-                ],
+            bounds: [hir::GenericBound::Trait(trait_ref, _)],
             ..
         }) = opaque_ty.kind
+            && let Some(segment) = trait_ref.trait_ref.path.segments.last()
+            && let Some(args) = segment.args
+            && let [
+                hir::TypeBinding {
+                    ident: Ident { name: sym::Output, .. },
+                    kind: hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) },
+                    ..
+                },
+            ] = args.bindings
         {
             ty
         } else {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 7e0e598cd9f..43f48f579a3 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -2503,7 +2503,7 @@ mod error {
                 self.errors.buffered.sort_by_key(|diag| diag.sort_span);
 
                 for diag in self.errors.buffered.drain(..) {
-                    self.infcx.tcx.sess.diagnostic().emit_diagnostic(diag);
+                    self.infcx.tcx.sess.dcx().emit_diagnostic(diag);
                 }
             }
 
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index c88d9d81fe1..6781c6a756f 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -280,7 +280,7 @@ pub(super) fn dump_annotation<'tcx>(
 
     let def_span = tcx.def_span(body.source.def_id());
     let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
-        let mut err = tcx.sess.diagnostic().struct_span_note(def_span, "external requirements");
+        let mut err = tcx.sess.dcx().struct_span_note(def_span, "external requirements");
 
         regioncx.annotate(tcx, &mut err);
 
@@ -299,7 +299,7 @@ pub(super) fn dump_annotation<'tcx>(
 
         err
     } else {
-        let mut err = tcx.sess.diagnostic().struct_span_note(def_span, "no external requirements");
+        let mut err = tcx.sess.dcx().struct_span_note(def_span, "no external requirements");
         regioncx.annotate(tcx, &mut err);
 
         err
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 98c21693cf0..5247d5f6981 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -268,7 +268,7 @@ fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) {
     // We sometimes see MIR failures (notably predicate failures) due to
     // the fact that we check rvalue sized predicates here. So use `span_delayed_bug`
     // to avoid reporting bugs in those cases.
-    tcx.sess.diagnostic().span_delayed_bug(span, msg);
+    tcx.sess.dcx().span_delayed_bug(span, msg);
 }
 
 enum FieldAccessError {
diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
index e13d217ef01..dffda8acc8d 100644
--- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
+++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
@@ -31,7 +31,7 @@ pub fn expand(
     {
         (item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
     } else {
-        ecx.sess.diagnostic().emit_err(errors::AllocErrorMustBeFn { span: item.span() });
+        ecx.sess.dcx().emit_err(errors::AllocErrorMustBeFn { span: item.span() });
         return vec![orig_item];
     };
 
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 9e66eaf73b3..6f1acd8e570 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -47,10 +47,10 @@ pub fn parse_asm_args<'a>(
     sp: Span,
     is_global_asm: bool,
 ) -> PResult<'a, AsmArgs> {
-    let diag = &sess.span_diagnostic;
+    let dcx = &sess.dcx;
 
     if p.token == token::Eof {
-        return Err(diag.create_err(errors::AsmRequiresTemplate { span: sp }));
+        return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
     }
 
     let first_template = p.parse_expr()?;
@@ -69,7 +69,7 @@ pub fn parse_asm_args<'a>(
         if !p.eat(&token::Comma) {
             if allow_templates {
                 // After a template string, we always expect *only* a comma...
-                return Err(diag.create_err(errors::AsmExpectedComma { span: p.token.span }));
+                return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
             } else {
                 // ...after that delegate to `expect` to also include the other expected tokens.
                 return Err(p.expect(&token::Comma).err().unwrap());
@@ -110,7 +110,7 @@ pub fn parse_asm_args<'a>(
         let op = if !is_global_asm && p.eat_keyword(kw::In) {
             let reg = parse_reg(p, &mut explicit_reg)?;
             if p.eat_keyword(kw::Underscore) {
-                let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
+                let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
                 return Err(err);
             }
             let expr = p.parse_expr()?;
@@ -126,7 +126,7 @@ pub fn parse_asm_args<'a>(
         } else if !is_global_asm && p.eat_keyword(sym::inout) {
             let reg = parse_reg(p, &mut explicit_reg)?;
             if p.eat_keyword(kw::Underscore) {
-                let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
+                let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
                 return Err(err);
             }
             let expr = p.parse_expr()?;
@@ -140,7 +140,7 @@ pub fn parse_asm_args<'a>(
         } else if !is_global_asm && p.eat_keyword(sym::inlateout) {
             let reg = parse_reg(p, &mut explicit_reg)?;
             if p.eat_keyword(kw::Underscore) {
-                let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
+                let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
                 return Err(err);
             }
             let expr = p.parse_expr()?;
@@ -157,7 +157,7 @@ pub fn parse_asm_args<'a>(
         } else if p.eat_keyword(sym::sym) {
             let expr = p.parse_expr()?;
             let ast::ExprKind::Path(qself, path) = &expr.kind else {
-                let err = diag.create_err(errors::AsmSymNoPath { span: expr.span });
+                let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
                 return Err(err);
             };
             let sym = ast::InlineAsmSym {
@@ -178,7 +178,7 @@ pub fn parse_asm_args<'a>(
                     ) => {}
                 ast::ExprKind::MacCall(..) => {}
                 _ => {
-                    let err = diag.create_err(errors::AsmExpectedOther {
+                    let err = dcx.create_err(errors::AsmExpectedOther {
                         span: template.span,
                         is_global_asm,
                     });
@@ -201,12 +201,12 @@ pub fn parse_asm_args<'a>(
         // of the argument available.
         if explicit_reg {
             if name.is_some() {
-                diag.emit_err(errors::AsmExplicitRegisterName { span });
+                dcx.emit_err(errors::AsmExplicitRegisterName { span });
             }
             args.reg_args.insert(slot);
         } else if let Some(name) = name {
             if let Some(&prev) = args.named_args.get(&name) {
-                diag.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
+                dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
                 continue;
             }
             args.named_args.insert(name, slot);
@@ -215,7 +215,7 @@ pub fn parse_asm_args<'a>(
                 let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
                 let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect();
 
-                diag.emit_err(errors::AsmPositionalAfter { span, named, explicit });
+                dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
             }
         }
     }
@@ -224,19 +224,19 @@ pub fn parse_asm_args<'a>(
         && args.options.contains(ast::InlineAsmOptions::READONLY)
     {
         let spans = args.options_spans.clone();
-        diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
+        dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
     }
     if args.options.contains(ast::InlineAsmOptions::PURE)
         && args.options.contains(ast::InlineAsmOptions::NORETURN)
     {
         let spans = args.options_spans.clone();
-        diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
+        dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
     }
     if args.options.contains(ast::InlineAsmOptions::PURE)
         && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
     {
         let spans = args.options_spans.clone();
-        diag.emit_err(errors::AsmPureCombine { spans });
+        dcx.emit_err(errors::AsmPureCombine { spans });
     }
 
     let mut have_real_output = false;
@@ -263,17 +263,17 @@ pub fn parse_asm_args<'a>(
         }
     }
     if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
-        diag.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
+        dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
     }
     if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
-        let err = diag.create_err(errors::AsmNoReturn { outputs_sp });
+        let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
         // Bail out now since this is likely to confuse MIR
         return Err(err);
     }
 
     if args.clobber_abis.len() > 0 {
         if is_global_asm {
-            let err = diag.create_err(errors::GlobalAsmClobberAbi {
+            let err = dcx.create_err(errors::GlobalAsmClobberAbi {
                 spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
             });
 
@@ -281,7 +281,7 @@ pub fn parse_asm_args<'a>(
             return Err(err);
         }
         if !regclass_outputs.is_empty() {
-            diag.emit_err(errors::AsmClobberNoReg {
+            dcx.emit_err(errors::AsmClobberNoReg {
                 spans: regclass_outputs,
                 clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
             });
@@ -298,7 +298,7 @@ pub fn parse_asm_args<'a>(
 fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
     // Tool-only output
     let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
-    p.sess.span_diagnostic.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
+    p.sess.dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
 }
 
 /// Try to set the provided option in the provided `AsmArgs`.
@@ -370,7 +370,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
     p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
 
     if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
-        return Err(p.sess.span_diagnostic.create_err(errors::NonABI { span: p.token.span }));
+        return Err(p.sess.dcx.create_err(errors::NonABI { span: p.token.span }));
     }
 
     let mut new_abis = Vec::new();
@@ -381,8 +381,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
             }
             Err(opt_lit) => {
                 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
-                let mut err =
-                    p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
+                let mut err = p.sess.dcx.struct_span_err(span, "expected string literal");
                 err.span_label(span, "not a string literal");
                 return Err(err);
             }
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index 3b1fde1f097..2803ddefba2 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -25,9 +25,7 @@ pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String])
         };
         let end_span = parser.token.span;
         if parser.token != token::Eof {
-            parse_sess
-                .span_diagnostic
-                .emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
+            parse_sess.dcx.emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
             continue;
         }
 
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 3d02cd72e54..6ffeb401453 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -1,5 +1,5 @@
 use rustc_errors::{
-    AddToDiagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, MultiSpan,
+    AddToDiagnostic, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
     SingleLabelManySpans,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -448,12 +448,12 @@ pub(crate) struct EnvNotDefinedWithUserMessage {
 // Hand-written implementation to support custom user messages.
 impl<'a> IntoDiagnostic<'a> for EnvNotDefinedWithUserMessage {
     #[track_caller]
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         #[expect(
             rustc::untranslatable_diagnostic,
             reason = "cannot translate user-provided messages"
         )]
-        let mut diag = handler.struct_err(self.msg_from_user.to_string());
+        let mut diag = dcx.struct_err(self.msg_from_user.to_string());
         diag.set_span(self.span);
         diag
     }
@@ -802,17 +802,16 @@ pub(crate) struct AsmClobberNoReg {
 }
 
 impl<'a> IntoDiagnostic<'a> for AsmClobberNoReg {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag =
-            handler.struct_err(crate::fluent_generated::builtin_macros_asm_clobber_no_reg);
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let mut diag = dcx.struct_err(crate::fluent_generated::builtin_macros_asm_clobber_no_reg);
         diag.set_span(self.spans.clone());
         // eager translation as `span_labels` takes `AsRef<str>`
-        let lbl1 = handler.eagerly_translate_to_string(
+        let lbl1 = dcx.eagerly_translate_to_string(
             crate::fluent_generated::builtin_macros_asm_clobber_abi,
             [].into_iter(),
         );
         diag.span_labels(self.clobbers, &lbl1);
-        let lbl2 = handler.eagerly_translate_to_string(
+        let lbl2 = dcx.eagerly_translate_to_string(
             crate::fluent_generated::builtin_macros_asm_clobber_outputs,
             [].into_iter(),
         );
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs
index 6dc75e3ba4c..00c7907cdb4 100644
--- a/compiler/rustc_builtin_macros/src/global_allocator.rs
+++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -34,7 +34,7 @@ pub fn expand(
     {
         (item, true, ecx.with_def_site_ctxt(ty.span))
     } else {
-        ecx.sess.diagnostic().emit_err(errors::AllocMustStatics { span: item.span() });
+        ecx.sess.dcx().emit_err(errors::AllocMustStatics { span: item.span() });
         return vec![orig_item];
     };
 
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index dae1bc5bfe5..4fddaa8ab6c 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -38,7 +38,7 @@ enum ProcMacro {
 struct CollectProcMacros<'a> {
     macros: Vec<ProcMacro>,
     in_root: bool,
-    handler: &'a rustc_errors::Handler,
+    dcx: &'a rustc_errors::DiagCtxt,
     source_map: &'a SourceMap,
     is_proc_macro_crate: bool,
     is_test_crate: bool,
@@ -52,7 +52,7 @@ pub fn inject(
     is_proc_macro_crate: bool,
     has_proc_macro_decls: bool,
     is_test_crate: bool,
-    handler: &rustc_errors::Handler,
+    dcx: &rustc_errors::DiagCtxt,
 ) {
     let ecfg = ExpansionConfig::default("proc_macro".to_string(), features);
     let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
@@ -60,7 +60,7 @@ pub fn inject(
     let mut collect = CollectProcMacros {
         macros: Vec::new(),
         in_root: true,
-        handler,
+        dcx,
         source_map: sess.source_map(),
         is_proc_macro_crate,
         is_test_crate,
@@ -86,13 +86,13 @@ pub fn inject(
 impl<'a> CollectProcMacros<'a> {
     fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
         if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() {
-            self.handler.emit_err(errors::ProcMacro { span: sp });
+            self.dcx.emit_err(errors::ProcMacro { span: sp });
         }
     }
 
     fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
         let Some((trait_name, proc_attrs)) =
-            parse_macro_name_and_helper_attrs(self.handler, attr, "derive")
+            parse_macro_name_and_helper_attrs(self.dcx, attr, "derive")
         else {
             return;
         };
@@ -112,7 +112,7 @@ impl<'a> CollectProcMacros<'a> {
             } else {
                 "functions tagged with `#[proc_macro_derive]` must be `pub`"
             };
-            self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
+            self.dcx.span_err(self.source_map.guess_head_span(item.span), msg);
         }
     }
 
@@ -130,7 +130,7 @@ impl<'a> CollectProcMacros<'a> {
             } else {
                 "functions tagged with `#[proc_macro_attribute]` must be `pub`"
             };
-            self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
+            self.dcx.span_err(self.source_map.guess_head_span(item.span), msg);
         }
     }
 
@@ -148,7 +148,7 @@ impl<'a> CollectProcMacros<'a> {
             } else {
                 "functions tagged with `#[proc_macro]` must be `pub`"
             };
-            self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
+            self.dcx.span_err(self.source_map.guess_head_span(item.span), msg);
         }
     }
 }
@@ -157,7 +157,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
     fn visit_item(&mut self, item: &'a ast::Item) {
         if let ast::ItemKind::MacroDef(..) = item.kind {
             if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
-                self.handler.emit_err(errors::ExportMacroRules {
+                self.dcx.emit_err(errors::ExportMacroRules {
                     span: self.source_map.guess_head_span(item.span),
                 });
             }
@@ -192,7 +192,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
                         )
                     };
 
-                    self.handler
+                    self.dcx
                         .struct_span_err(attr.span, msg)
                         .span_label(prev_attr.span, "previous attribute here")
                         .emit();
@@ -218,7 +218,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
                 pprust::path_to_string(&attr.get_normal_item().path),
             );
 
-            self.handler.span_err(attr.span, msg);
+            self.dcx.span_err(attr.span, msg);
             return;
         }
 
@@ -232,7 +232,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
                 pprust::path_to_string(&attr.get_normal_item().path),
             );
 
-            self.handler.span_err(attr.span, msg);
+            self.dcx.span_err(attr.span, msg);
             return;
         }
 
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index e5b274304e7..ec843a3a0cd 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -389,16 +389,16 @@ pub fn expand_test_or_bench(
 }
 
 fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>) {
-    let diag = cx.sess.diagnostic();
+    let dcx = cx.sess.dcx();
     let msg = "the `#[test]` attribute may only be used on a non-associated function";
     let mut err = match item.map(|i| &i.kind) {
         // These were a warning before #92959 and need to continue being that to avoid breaking
         // stable user code (#94508).
-        Some(ast::ItemKind::MacCall(_)) => diag.struct_span_warn(attr_sp, msg),
+        Some(ast::ItemKind::MacCall(_)) => dcx.struct_span_warn(attr_sp, msg),
         // `.forget_guarantee()` needed to get these two arms to match types. Because of how
         // locally close the `.emit()` call is I'm comfortable with it, but if it can be
         // reworked in the future to not need it, it'd be nice.
-        _ => diag.struct_span_err(attr_sp, msg).forget_guarantee(),
+        _ => dcx.struct_span_err(attr_sp, msg).forget_guarantee(),
     };
     if let Some(item) = item {
         err.span_label(
@@ -466,7 +466,7 @@ fn should_ignore_message(i: &ast::Item) -> Option<Symbol> {
 fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
     match attr::find_by_name(&i.attrs, sym::should_panic) {
         Some(attr) => {
-            let sd = cx.sess.diagnostic();
+            let dcx = cx.sess.dcx();
 
             match attr.meta_item_list() {
                 // Handle #[should_panic(expected = "foo")]
@@ -477,7 +477,7 @@ fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
                         .and_then(|mi| mi.meta_item())
                         .and_then(|mi| mi.value_str());
                     if list.len() != 1 || msg.is_none() {
-                        sd.struct_span_warn(
+                        dcx.struct_span_warn(
                             attr.span,
                             "argument must be of the form: \
                              `expected = \"error message\"`",
@@ -535,30 +535,30 @@ fn check_test_signature(
     f: &ast::Fn,
 ) -> Result<(), ErrorGuaranteed> {
     let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
-    let sd = cx.sess.diagnostic();
+    let dcx = cx.sess.dcx();
 
     if let ast::Unsafe::Yes(span) = f.sig.header.unsafety {
-        return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
+        return Err(dcx.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
     }
 
     if let Some(coroutine_kind) = f.sig.header.coroutine_kind {
         match coroutine_kind {
             ast::CoroutineKind::Async { span, .. } => {
-                return Err(sd.emit_err(errors::TestBadFn {
+                return Err(dcx.emit_err(errors::TestBadFn {
                     span: i.span,
                     cause: span,
                     kind: "async",
                 }));
             }
             ast::CoroutineKind::Gen { span, .. } => {
-                return Err(sd.emit_err(errors::TestBadFn {
+                return Err(dcx.emit_err(errors::TestBadFn {
                     span: i.span,
                     cause: span,
                     kind: "gen",
                 }));
             }
             ast::CoroutineKind::AsyncGen { span, .. } => {
-                return Err(sd.emit_err(errors::TestBadFn {
+                return Err(dcx.emit_err(errors::TestBadFn {
                     span: i.span,
                     cause: span,
                     kind: "async gen",
@@ -576,15 +576,15 @@ fn check_test_signature(
     };
 
     if !f.sig.decl.inputs.is_empty() {
-        return Err(sd.span_err(i.span, "functions used as tests can not have any arguments"));
+        return Err(dcx.span_err(i.span, "functions used as tests can not have any arguments"));
     }
 
     if has_should_panic_attr && has_output {
-        return Err(sd.span_err(i.span, "functions using `#[should_panic]` must return `()`"));
+        return Err(dcx.span_err(i.span, "functions using `#[should_panic]` must return `()`"));
     }
 
     if f.generics.params.iter().any(|param| !matches!(param.kind, GenericParamKind::Lifetime)) {
-        return Err(sd.span_err(
+        return Err(dcx.span_err(
             i.span,
             "functions used as tests can not have any non-lifetime generic parameters",
         ));
@@ -601,7 +601,7 @@ fn check_bench_signature(
     // N.B., inadequate check, but we're running
     // well before resolve, can't get too deep.
     if f.sig.decl.inputs.len() != 1 {
-        return Err(cx.sess.diagnostic().emit_err(errors::BenchSig { span: i.span }));
+        return Err(cx.sess.dcx().emit_err(errors::BenchSig { span: i.span }));
     }
     Ok(())
 }
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index c5daf17abb9..dc28cd2ea31 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -47,7 +47,7 @@ pub fn inject(
     features: &Features,
     resolver: &mut dyn ResolverExpand,
 ) {
-    let span_diagnostic = sess.diagnostic();
+    let dcx = sess.dcx();
     let panic_strategy = sess.panic_strategy();
     let platform_panic_strategy = sess.target.panic_strategy;
 
@@ -60,7 +60,7 @@ pub fn inject(
 
     // Do this here so that the test_runner crate attribute gets marked as used
     // even in non-test builds
-    let test_runner = get_test_runner(span_diagnostic, krate);
+    let test_runner = get_test_runner(dcx, krate);
 
     if sess.is_test_crate() {
         let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) {
@@ -70,7 +70,7 @@ pub fn inject(
                     // Silently allow compiling with panic=abort on these platforms,
                     // but with old behavior (abort if a test fails).
                 } else {
-                    span_diagnostic.emit_err(errors::TestsNotSupport {});
+                    dcx.emit_err(errors::TestsNotSupport {});
                 }
                 PanicStrategy::Unwind
             }
@@ -389,7 +389,7 @@ fn get_test_name(i: &ast::Item) -> Option<Symbol> {
     attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker)
 }
 
-fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
+fn get_test_runner(dcx: &rustc_errors::DiagCtxt, krate: &ast::Crate) -> Option<ast::Path> {
     let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
     let meta_list = test_attr.meta_item_list()?;
     let span = test_attr.span;
@@ -397,11 +397,11 @@ fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast
         [single] => match single.meta_item() {
             Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
             _ => {
-                sd.emit_err(errors::TestRunnerInvalid { span });
+                dcx.emit_err(errors::TestRunnerInvalid { span });
             }
         },
         _ => {
-            sd.emit_err(errors::TestRunnerNargs { span });
+            dcx.emit_err(errors::TestRunnerNargs { span });
         }
     }
     None
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 71557d49ef2..8b0dc611075 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -176,10 +176,10 @@ pub(crate) fn compile_fn(
         match module.define_function(codegened_func.func_id, context) {
             Ok(()) => {}
             Err(ModuleError::Compilation(CodegenError::ImplLimitExceeded)) => {
-                let handler = rustc_session::EarlyErrorHandler::new(
+                let early_dcx = rustc_session::EarlyDiagCtxt::new(
                     rustc_session::config::ErrorOutputType::default(),
                 );
-                handler.early_error(format!(
+                early_dcx.early_error(format!(
                     "backend implementation limit exceeded while compiling {name}",
                     name = codegened_func.symbol_name
                 ));
diff --git a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
index 978891f2b0d..9678969134a 100644
--- a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
+++ b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
@@ -46,7 +46,7 @@ impl ConcurrencyLimiter {
         }
     }
 
-    pub(super) fn acquire(&mut self, handler: &rustc_errors::Handler) -> ConcurrencyLimiterToken {
+    pub(super) fn acquire(&mut self, dcx: &rustc_errors::DiagCtxt) -> ConcurrencyLimiterToken {
         let mut state = self.state.lock().unwrap();
         loop {
             state.assert_invariants();
@@ -64,7 +64,7 @@ impl ConcurrencyLimiter {
                     // Make sure to drop the mutex guard first to prevent poisoning the mutex.
                     drop(state);
                     if let Some(err) = err {
-                        handler.fatal(err);
+                        dcx.fatal(err);
                     } else {
                         // The error was already emitted, but compilation continued. Raise a silent
                         // fatal error.
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index 11229dd421e..b3ab533df3d 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -422,7 +422,7 @@ pub(crate) fn run_aot(
                                     backend_config.clone(),
                                     global_asm_config.clone(),
                                     cgu.name(),
-                                    concurrency_limiter.acquire(tcx.sess.diagnostic()),
+                                    concurrency_limiter.acquire(tcx.sess.dcx()),
                                 ),
                                 module_codegen,
                                 Some(rustc_middle::dep_graph::hash_result),
diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
index 02c0dcb8b1b..196418023d9 100644
--- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
+++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
@@ -231,9 +231,8 @@ pub(crate) fn write_ir_file(
     let res = std::fs::File::create(clif_file_name).and_then(|mut file| write(&mut file));
     if let Err(err) = res {
         // Using early_warn as no Session is available here
-        let handler = rustc_session::EarlyErrorHandler::new(
-            rustc_session::config::ErrorOutputType::default(),
-        );
+        let handler =
+            rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
         handler.early_warn(format!("error writing ir file: {}", err));
     }
 }
diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs
index 529454b119e..c21b7686823 100644
--- a/compiler/rustc_codegen_gcc/src/back/lto.rs
+++ b/compiler/rustc_codegen_gcc/src/back/lto.rs
@@ -30,7 +30,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
 use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
 use rustc_data_structures::memmap::Mmap;
-use rustc_errors::{FatalError, Handler};
+use rustc_errors::{FatalError, DiagCtxt};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::dep_graph::WorkProduct;
 use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
@@ -61,7 +61,7 @@ struct LtoData {
     tmp_path: TempDir,
 }
 
-fn prepare_lto(cgcx: &CodegenContext<GccCodegenBackend>, diag_handler: &Handler) -> Result<LtoData, FatalError> {
+fn prepare_lto(cgcx: &CodegenContext<GccCodegenBackend>, dcx: &DiagCtxt) -> Result<LtoData, FatalError> {
     let export_threshold = match cgcx.lto {
         // We're just doing LTO for our one crate
         Lto::ThinLocal => SymbolExportLevel::Rust,
@@ -106,18 +106,18 @@ fn prepare_lto(cgcx: &CodegenContext<GccCodegenBackend>, diag_handler: &Handler)
         // Make sure we actually can run LTO
         for crate_type in cgcx.crate_types.iter() {
             if !crate_type_allows_lto(*crate_type) {
-                diag_handler.emit_err(LtoDisallowed);
+                dcx.emit_err(LtoDisallowed);
                 return Err(FatalError);
             } else if *crate_type == CrateType::Dylib {
                 if !cgcx.opts.unstable_opts.dylib_lto {
-                    diag_handler.emit_err(LtoDylib);
+                    dcx.emit_err(LtoDylib);
                     return Err(FatalError);
                 }
             }
         }
 
         if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
-            diag_handler.emit_err(DynamicLinkingWithLTO);
+            dcx.emit_err(DynamicLinkingWithLTO);
             return Err(FatalError);
         }
 
@@ -154,7 +154,7 @@ fn prepare_lto(cgcx: &CodegenContext<GccCodegenBackend>, diag_handler: &Handler)
                         upstream_modules.push((module, CString::new(name).unwrap()));
                     }
                     Err(e) => {
-                        diag_handler.emit_err(e);
+                        dcx.emit_err(e);
                         return Err(FatalError);
                     }
                 }
@@ -183,16 +183,16 @@ pub(crate) fn run_fat(
     modules: Vec<FatLtoInput<GccCodegenBackend>>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> {
-    let diag_handler = cgcx.create_diag_handler();
-    let lto_data = prepare_lto(cgcx, &diag_handler)?;
+    let dcx = cgcx.create_dcx();
+    let lto_data = prepare_lto(cgcx, &dcx)?;
     /*let symbols_below_threshold =
         lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();*/
-    fat_lto(cgcx, &diag_handler, modules, cached_modules, lto_data.upstream_modules, lto_data.tmp_path,
+    fat_lto(cgcx, &dcx, modules, cached_modules, lto_data.upstream_modules, lto_data.tmp_path,
         //&symbols_below_threshold,
     )
 }
 
-fn fat_lto(cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, modules: Vec<FatLtoInput<GccCodegenBackend>>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, tmp_path: TempDir,
+fn fat_lto(cgcx: &CodegenContext<GccCodegenBackend>, _dcx: &DiagCtxt, modules: Vec<FatLtoInput<GccCodegenBackend>>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, tmp_path: TempDir,
     //symbols_below_threshold: &[*const libc::c_char],
 ) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> {
     let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module");
@@ -257,7 +257,7 @@ fn fat_lto(cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, mo
             let (buffer, name) = serialized_modules.remove(0);
             info!("no in-memory regular modules to choose from, parsing {:?}", name);
             ModuleCodegen {
-                module_llvm: GccContext::parse(cgcx, &name, buffer.data(), diag_handler)?,
+                module_llvm: GccContext::parse(cgcx, &name, buffer.data(), dcx)?,
                 name: name.into_string().unwrap(),
                 kind: ModuleKind::Regular,
             }*/
diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs
index 04772d7707a..2f8a54f529c 100644
--- a/compiler/rustc_codegen_gcc/src/back/write.rs
+++ b/compiler/rustc_codegen_gcc/src/back/write.rs
@@ -4,7 +4,7 @@ use gccjit::OutputKind;
 use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
 use rustc_codegen_ssa::back::link::ensure_removed;
 use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
-use rustc_errors::Handler;
+use rustc_errors::DiagCtxt;
 use rustc_fs_util::link_or_copy;
 use rustc_session::config::OutputType;
 use rustc_span::fatal_error::FatalError;
@@ -13,7 +13,7 @@ use rustc_target::spec::SplitDebuginfo;
 use crate::{GccCodegenBackend, GccContext};
 use crate::errors::CopyBitcode;
 
-pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, diag_handler: &Handler, module: ModuleCodegen<GccContext>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
+pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, dcx: &DiagCtxt, module: ModuleCodegen<GccContext>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
     let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name);
     {
         let context = &module.module_llvm.context;
@@ -127,12 +127,12 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, diag_hand
             EmitObj::Bitcode => {
                 debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out);
                 if let Err(err) = link_or_copy(&bc_out, &obj_out) {
-                    diag_handler.emit_err(CopyBitcode { err });
+                    dcx.emit_err(CopyBitcode { err });
                 }
 
                 if !config.emit_bc {
                     debug!("removing_bitcode {:?}", bc_out);
-                    ensure_removed(diag_handler, &bc_out);
+                    ensure_removed(dcx, &bc_out);
                 }
             }
 
@@ -148,7 +148,7 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, diag_hand
     ))
 }
 
-pub(crate) fn link(_cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, mut _modules: Vec<ModuleCodegen<GccContext>>) -> Result<ModuleCodegen<GccContext>, FatalError> {
+pub(crate) fn link(_cgcx: &CodegenContext<GccCodegenBackend>, _dcx: &DiagCtxt, mut _modules: Vec<ModuleCodegen<GccContext>>) -> Result<ModuleCodegen<GccContext>, FatalError> {
     unimplemented!();
 }
 
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index b7ddc410315..c6edd52d1e4 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -377,9 +377,6 @@ pub trait TypeReflection<'gcc, 'tcx>  {
     fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
     fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
 
-    fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
-    fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
-
     fn is_vector(&self) -> bool;
 }
 
@@ -464,14 +461,6 @@ impl<'gcc, 'tcx> TypeReflection<'gcc, 'tcx> for Type<'gcc> {
         self.unqualified() == cx.u128_type.unqualified()
     }
 
-    fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
-        self.unqualified() == cx.context.new_type::<f32>()
-    }
-
-    fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
-        self.unqualified() == cx.context.new_type::<f64>()
-    }
-
     fn is_vector(&self) -> bool {
         let mut typ = self.clone();
         loop {
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
index 893cad05161..ab9c703db37 100644
--- a/compiler/rustc_codegen_gcc/src/context.rs
+++ b/compiler/rustc_codegen_gcc/src/context.rs
@@ -22,12 +22,6 @@ use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
 use crate::callee::get_fn;
 use crate::common::SignType;
 
-#[derive(Clone)]
-pub struct FuncSig<'gcc> {
-    pub params: Vec<Type<'gcc>>,
-    pub return_type: Type<'gcc>,
-}
-
 pub struct CodegenCx<'gcc, 'tcx> {
     pub check_overflow: bool,
     pub codegen_unit: &'tcx CodegenUnit<'tcx>,
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 5fc4b12d7e6..766d90cf724 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,5 +1,6 @@
 use rustc_errors::{
-    DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, IntoDiagnosticArg,
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
+    IntoDiagnosticArg,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
@@ -111,8 +112,8 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
 pub(crate) struct MissingFeatures;
 
 impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::codegen_gcc_target_feature_disable_or_enable);
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = dcx.struct_err(fluent::codegen_gcc_target_feature_disable_or_enable);
         if let Some(span) = self.span {
             diag.set_span(span);
         };
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 0ececc5dda0..1f3f909d8b4 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -100,7 +100,7 @@ use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModul
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sync::IntoDynSyncSend;
 use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ThinBufferMethods, WriteBackendMethods};
-use rustc_errors::{ErrorGuaranteed, Handler};
+use rustc_errors::{ErrorGuaranteed, DiagCtxt};
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::util::Providers;
@@ -330,7 +330,7 @@ impl WriteBackendMethods for GccCodegenBackend {
         unimplemented!()
     }
 
-    unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
+    unsafe fn optimize(_cgcx: &CodegenContext<Self>, _dcx: &DiagCtxt, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
         module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
         Ok(())
     }
@@ -344,8 +344,8 @@ impl WriteBackendMethods for GccCodegenBackend {
         unimplemented!();
     }
 
-    unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
-        back::write::codegen(cgcx, diag_handler, module, config)
+    unsafe fn codegen(cgcx: &CodegenContext<Self>, dcx: &DiagCtxt, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
+        back::write::codegen(cgcx, dcx, module, config)
     }
 
     fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
@@ -356,8 +356,8 @@ impl WriteBackendMethods for GccCodegenBackend {
         unimplemented!();
     }
 
-    fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
-        back::write::link(cgcx, diag_handler, modules)
+    fn run_link(cgcx: &CodegenContext<Self>, dcx: &DiagCtxt, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+        back::write::link(cgcx, dcx, modules)
     }
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index c9e109a5d23..e9e8ade09b7 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -14,7 +14,7 @@ use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::memmap::Mmap;
-use rustc_errors::{FatalError, Handler};
+use rustc_errors::{DiagCtxt, FatalError};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::WorkProduct;
@@ -47,7 +47,7 @@ pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
 
 fn prepare_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
 ) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> {
     let export_threshold = match cgcx.lto {
         // We're just doing LTO for our one crate
@@ -84,23 +84,23 @@ fn prepare_lto(
         // Make sure we actually can run LTO
         for crate_type in cgcx.crate_types.iter() {
             if !crate_type_allows_lto(*crate_type) {
-                diag_handler.emit_err(LtoDisallowed);
+                dcx.emit_err(LtoDisallowed);
                 return Err(FatalError);
             } else if *crate_type == CrateType::Dylib {
                 if !cgcx.opts.unstable_opts.dylib_lto {
-                    diag_handler.emit_err(LtoDylib);
+                    dcx.emit_err(LtoDylib);
                     return Err(FatalError);
                 }
             } else if *crate_type == CrateType::ProcMacro {
                 if !cgcx.opts.unstable_opts.dylib_lto {
-                    diag_handler.emit_err(LtoProcMacro);
+                    dcx.emit_err(LtoProcMacro);
                     return Err(FatalError);
                 }
             }
         }
 
         if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
-            diag_handler.emit_err(DynamicLinkingWithLTO);
+            dcx.emit_err(DynamicLinkingWithLTO);
             return Err(FatalError);
         }
 
@@ -138,7 +138,7 @@ fn prepare_lto(
                         upstream_modules.push((module, CString::new(name).unwrap()));
                     }
                     Err(e) => {
-                        diag_handler.emit_err(e);
+                        dcx.emit_err(e);
                         return Err(FatalError);
                     }
                 }
@@ -200,18 +200,11 @@ pub(crate) fn run_fat(
     modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> {
-    let diag_handler = cgcx.create_diag_handler();
-    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &diag_handler)?;
+    let dcx = cgcx.create_dcx();
+    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &dcx)?;
     let symbols_below_threshold =
         symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
-    fat_lto(
-        cgcx,
-        &diag_handler,
-        modules,
-        cached_modules,
-        upstream_modules,
-        &symbols_below_threshold,
-    )
+    fat_lto(cgcx, &dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold)
 }
 
 /// Performs thin LTO by performing necessary global analysis and returning two
@@ -222,8 +215,8 @@ pub(crate) fn run_thin(
     modules: Vec<(String, ThinBuffer)>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
-    let diag_handler = cgcx.create_diag_handler();
-    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &diag_handler)?;
+    let dcx = cgcx.create_dcx();
+    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &dcx)?;
     let symbols_below_threshold =
         symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
     if cgcx.opts.cg.linker_plugin_lto.enabled() {
@@ -232,14 +225,7 @@ pub(crate) fn run_thin(
                       is deferred to the linker"
         );
     }
-    thin_lto(
-        cgcx,
-        &diag_handler,
-        modules,
-        upstream_modules,
-        cached_modules,
-        &symbols_below_threshold,
-    )
+    thin_lto(cgcx, &dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold)
 }
 
 pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBuffer) {
@@ -250,7 +236,7 @@ pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBu
 
 fn fat_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
     modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
     mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
@@ -316,7 +302,7 @@ fn fat_lto(
             let (buffer, name) = serialized_modules.remove(0);
             info!("no in-memory regular modules to choose from, parsing {:?}", name);
             ModuleCodegen {
-                module_llvm: ModuleLlvm::parse(cgcx, &name, buffer.data(), diag_handler)?,
+                module_llvm: ModuleLlvm::parse(cgcx, &name, buffer.data(), dcx)?,
                 name: name.into_string().unwrap(),
                 kind: ModuleKind::Regular,
             }
@@ -333,13 +319,8 @@ fn fat_lto(
         // The linking steps below may produce errors and diagnostics within LLVM
         // which we'd like to handle and print, so set up our diagnostic handlers
         // (which get unregistered when they go out of scope below).
-        let _handler = DiagnosticHandlers::new(
-            cgcx,
-            diag_handler,
-            llcx,
-            &module,
-            CodegenDiagnosticsStage::LTO,
-        );
+        let _handler =
+            DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::LTO);
 
         // For all other modules we codegened we'll need to link them into our own
         // bitcode. All modules were codegened in their own LLVM context, however,
@@ -367,9 +348,7 @@ fn fat_lto(
                 });
             info!("linking {:?}", name);
             let data = bc_decoded.data();
-            linker
-                .add(data)
-                .map_err(|()| write::llvm_err(diag_handler, LlvmError::LoadBitcode { name }))?;
+            linker.add(data).map_err(|()| write::llvm_err(dcx, LlvmError::LoadBitcode { name }))?;
             serialized_bitcode.push(bc_decoded);
         }
         drop(linker);
@@ -452,7 +431,7 @@ impl Drop for Linker<'_> {
 /// they all go out of scope.
 fn thin_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
     modules: Vec<(String, ThinBuffer)>,
     serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
@@ -527,7 +506,7 @@ fn thin_lto(
             symbols_below_threshold.as_ptr(),
             symbols_below_threshold.len() as u32,
         )
-        .ok_or_else(|| write::llvm_err(diag_handler, LlvmError::PrepareThinLtoContext))?;
+        .ok_or_else(|| write::llvm_err(dcx, LlvmError::PrepareThinLtoContext))?;
 
         let data = ThinData(data);
 
@@ -599,7 +578,7 @@ fn thin_lto(
         // session, overwriting the previous serialized data (if any).
         if let Some(path) = key_map_path {
             if let Err(err) = curr_key_map.save_to_file(&path) {
-                return Err(write::llvm_err(diag_handler, LlvmError::WriteThinLtoKey { err }));
+                return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }));
             }
         }
 
@@ -609,7 +588,7 @@ fn thin_lto(
 
 pub(crate) fn run_pass_manager(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
     module: &mut ModuleCodegen<ModuleLlvm>,
     thin: bool,
 ) -> Result<(), FatalError> {
@@ -637,7 +616,7 @@ pub(crate) fn run_pass_manager(
         }
         let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
         let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
-        write::llvm_optimize(cgcx, diag_handler, module, config, opt_level, opt_stage)?;
+        write::llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage)?;
     }
     debug!("lto done");
     Ok(())
@@ -721,11 +700,11 @@ pub unsafe fn optimize_thin_module(
     thin_module: ThinModule<LlvmCodegenBackend>,
     cgcx: &CodegenContext<LlvmCodegenBackend>,
 ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
-    let diag_handler = cgcx.create_diag_handler();
+    let dcx = cgcx.create_dcx();
 
     let module_name = &thin_module.shared.module_names[thin_module.idx];
     let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap());
-    let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, e))?;
+    let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?;
 
     // Right now the implementation we've got only works over serialized
     // modules, so we create a fresh new LLVM context and parse the module
@@ -733,7 +712,7 @@ pub unsafe fn optimize_thin_module(
     // crates but for locally codegened modules we may be able to reuse
     // that LLVM Context and Module.
     let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
-    let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &diag_handler)? as *const _;
+    let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &dcx)? as *const _;
     let mut module = ModuleCodegen {
         module_llvm: ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) },
         name: thin_module.name().to_string(),
@@ -756,7 +735,7 @@ pub unsafe fn optimize_thin_module(
             let _timer =
                 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) {
-                return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
         }
@@ -766,7 +745,7 @@ pub unsafe fn optimize_thin_module(
                 .prof
                 .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) {
-                return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve");
         }
@@ -776,7 +755,7 @@ pub unsafe fn optimize_thin_module(
                 .prof
                 .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) {
-                return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize");
         }
@@ -785,7 +764,7 @@ pub unsafe fn optimize_thin_module(
             let _timer =
                 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target) {
-                return Err(write::llvm_err(&diag_handler, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
         }
@@ -797,7 +776,7 @@ pub unsafe fn optimize_thin_module(
         // little differently.
         {
             info!("running thin lto passes over {}", module.name);
-            run_pass_manager(cgcx, &diag_handler, &mut module, true)?;
+            run_pass_manager(cgcx, &dcx, &mut module, true)?;
             save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
         }
     }
@@ -868,10 +847,10 @@ pub fn parse_module<'a>(
     cx: &'a llvm::Context,
     name: &CStr,
     data: &[u8],
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
 ) -> Result<&'a llvm::Module, FatalError> {
     unsafe {
         llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr())
-            .ok_or_else(|| write::llvm_err(diag_handler, LlvmError::ParseBitcode))
+            .ok_or_else(|| write::llvm_err(dcx, LlvmError::ParseBitcode))
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 048f66ad148..75f99f964d0 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -26,7 +26,7 @@ use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_errors::{FatalError, Handler, Level};
+use rustc_errors::{DiagCtxt, FatalError, Level};
 use rustc_fs_util::{link_or_copy, path_to_c_string};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath};
@@ -45,15 +45,15 @@ use std::slice;
 use std::str;
 use std::sync::Arc;
 
-pub fn llvm_err<'a>(handler: &rustc_errors::Handler, err: LlvmError<'a>) -> FatalError {
+pub fn llvm_err<'a>(dcx: &rustc_errors::DiagCtxt, err: LlvmError<'a>) -> FatalError {
     match llvm::last_error() {
-        Some(llvm_err) => handler.emit_almost_fatal(WithLlvmError(err, llvm_err)),
-        None => handler.emit_almost_fatal(err),
+        Some(llvm_err) => dcx.emit_almost_fatal(WithLlvmError(err, llvm_err)),
+        None => dcx.emit_almost_fatal(err),
     }
 }
 
 pub fn write_output_file<'ll>(
-    handler: &rustc_errors::Handler,
+    dcx: &rustc_errors::DiagCtxt,
     target: &'ll llvm::TargetMachine,
     pm: &llvm::PassManager<'ll>,
     m: &'ll llvm::Module,
@@ -93,9 +93,7 @@ pub fn write_output_file<'ll>(
             }
         }
 
-        result
-            .into_result()
-            .map_err(|()| llvm_err(handler, LlvmError::WriteOutput { path: output }))
+        result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output }))
     }
 }
 
@@ -105,7 +103,7 @@ pub fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine
     // system/tcx is set up.
     let features = llvm_util::global_llvm_features(sess, false);
     target_machine_factory(sess, config::OptLevel::No, &features)(config)
-        .unwrap_or_else(|err| llvm_err(sess.diagnostic(), err).raise())
+        .unwrap_or_else(|err| llvm_err(sess.dcx(), err).raise())
 }
 
 pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMachine {
@@ -128,7 +126,7 @@ pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMach
         tcx.backend_optimization_level(()),
         tcx.global_backend_features(()),
     )(config)
-    .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), err).raise())
+    .unwrap_or_else(|err| llvm_err(tcx.sess.dcx(), err).raise())
 }
 
 pub fn to_llvm_opt_settings(
@@ -332,7 +330,7 @@ pub enum CodegenDiagnosticsStage {
 }
 
 pub struct DiagnosticHandlers<'a> {
-    data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a Handler),
+    data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a DiagCtxt),
     llcx: &'a llvm::Context,
     old_handler: Option<&'a llvm::DiagnosticHandler>,
 }
@@ -340,7 +338,7 @@ pub struct DiagnosticHandlers<'a> {
 impl<'a> DiagnosticHandlers<'a> {
     pub fn new(
         cgcx: &'a CodegenContext<LlvmCodegenBackend>,
-        handler: &'a Handler,
+        dcx: &'a DiagCtxt,
         llcx: &'a llvm::Context,
         module: &ModuleCodegen<ModuleLlvm>,
         stage: CodegenDiagnosticsStage,
@@ -375,7 +373,7 @@ impl<'a> DiagnosticHandlers<'a> {
             .and_then(|dir| dir.to_str().and_then(|p| CString::new(p).ok()));
 
         let pgo_available = cgcx.opts.cg.profile_use.is_some();
-        let data = Box::into_raw(Box::new((cgcx, handler)));
+        let data = Box::into_raw(Box::new((cgcx, dcx)));
         unsafe {
             let old_handler = llvm::LLVMRustContextGetDiagnosticHandler(llcx);
             llvm::LLVMRustContextConfigureDiagnosticHandler(
@@ -429,7 +427,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
     if user.is_null() {
         return;
     }
-    let (cgcx, diag_handler) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &Handler));
+    let (cgcx, dcx) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &DiagCtxt));
 
     match llvm::diagnostic::Diagnostic::unpack(info) {
         llvm::diagnostic::InlineAsm(inline) => {
@@ -437,7 +435,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
         }
 
         llvm::diagnostic::Optimization(opt) => {
-            diag_handler.emit_note(FromLlvmOptimizationDiag {
+            dcx.emit_note(FromLlvmOptimizationDiag {
                 filename: &opt.filename,
                 line: opt.line,
                 column: opt.column,
@@ -459,14 +457,14 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
                 llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s)
             })
             .expect("non-UTF8 diagnostic");
-            diag_handler.emit_warning(FromLlvmDiag { message });
+            dcx.emit_warning(FromLlvmDiag { message });
         }
         llvm::diagnostic::Unsupported(diagnostic_ref) => {
             let message = llvm::build_string(|s| {
                 llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s)
             })
             .expect("non-UTF8 diagnostic");
-            diag_handler.emit_err(FromLlvmDiag { message });
+            dcx.emit_err(FromLlvmDiag { message });
         }
         llvm::diagnostic::UnknownDiagnostic(..) => {}
     }
@@ -507,7 +505,7 @@ fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> {
 
 pub(crate) unsafe fn llvm_optimize(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
     opt_level: config::OptLevel,
@@ -588,13 +586,13 @@ pub(crate) unsafe fn llvm_optimize(
         llvm_plugins.as_ptr().cast(),
         llvm_plugins.len(),
     );
-    result.into_result().map_err(|()| llvm_err(diag_handler, LlvmError::RunLlvmPasses))
+    result.into_result().map_err(|()| llvm_err(dcx, LlvmError::RunLlvmPasses))
 }
 
 // Unsafe due to LLVM calls.
 pub(crate) unsafe fn optimize(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
 ) -> Result<(), FatalError> {
@@ -602,8 +600,7 @@ pub(crate) unsafe fn optimize(
 
     let llmod = module.module_llvm.llmod();
     let llcx = &*module.module_llvm.llcx;
-    let _handlers =
-        DiagnosticHandlers::new(cgcx, diag_handler, llcx, module, CodegenDiagnosticsStage::Opt);
+    let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, module, CodegenDiagnosticsStage::Opt);
 
     let module_name = module.name.clone();
     let module_name = Some(&module_name[..]);
@@ -621,14 +618,14 @@ pub(crate) unsafe fn optimize(
             _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO,
             _ => llvm::OptStage::PreLinkNoLTO,
         };
-        return llvm_optimize(cgcx, diag_handler, module, config, opt_level, opt_stage);
+        return llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage);
     }
     Ok(())
 }
 
 pub(crate) fn link(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
     mut modules: Vec<ModuleCodegen<ModuleLlvm>>,
 ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
     use super::lto::{Linker, ModuleBuffer};
@@ -641,9 +638,9 @@ pub(crate) fn link(
     for module in elements {
         let _timer = cgcx.prof.generic_activity_with_arg("LLVM_link_module", &*module.name);
         let buffer = ModuleBuffer::new(module.module_llvm.llmod());
-        linker.add(buffer.data()).map_err(|()| {
-            llvm_err(diag_handler, LlvmError::SerializeModule { name: &module.name })
-        })?;
+        linker
+            .add(buffer.data())
+            .map_err(|()| llvm_err(dcx, LlvmError::SerializeModule { name: &module.name }))?;
     }
     drop(linker);
     Ok(modules.remove(0))
@@ -651,7 +648,7 @@ pub(crate) fn link(
 
 pub(crate) unsafe fn codegen(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    diag_handler: &Handler,
+    dcx: &DiagCtxt,
     module: ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
 ) -> Result<CompiledModule, FatalError> {
@@ -662,13 +659,8 @@ pub(crate) unsafe fn codegen(
         let tm = &*module.module_llvm.tm;
         let module_name = module.name.clone();
         let module_name = Some(&module_name[..]);
-        let _handlers = DiagnosticHandlers::new(
-            cgcx,
-            diag_handler,
-            llcx,
-            &module,
-            CodegenDiagnosticsStage::Codegen,
-        );
+        let _handlers =
+            DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::Codegen);
 
         if cgcx.msvc_imps_needed {
             create_msvc_imps(cgcx, llcx, llmod);
@@ -726,7 +718,7 @@ pub(crate) unsafe fn codegen(
                     .prof
                     .generic_activity_with_arg("LLVM_module_codegen_emit_bitcode", &*module.name);
                 if let Err(err) = fs::write(&bc_out, data) {
-                    diag_handler.emit_err(WriteBytecode { path: &bc_out, err });
+                    dcx.emit_err(WriteBytecode { path: &bc_out, err });
                 }
             }
 
@@ -776,9 +768,7 @@ pub(crate) unsafe fn codegen(
                 record_artifact_size(&cgcx.prof, "llvm_ir", &out);
             }
 
-            result
-                .into_result()
-                .map_err(|()| llvm_err(diag_handler, LlvmError::WriteIr { path: &out }))?;
+            result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteIr { path: &out }))?;
         }
 
         if config.emit_asm {
@@ -797,7 +787,7 @@ pub(crate) unsafe fn codegen(
             };
             with_codegen(tm, llmod, |cpm| {
                 write_output_file(
-                    diag_handler,
+                    dcx,
                     tm,
                     cpm,
                     llmod,
@@ -832,7 +822,7 @@ pub(crate) unsafe fn codegen(
 
                 with_codegen(tm, llmod, |cpm| {
                     write_output_file(
-                        diag_handler,
+                        dcx,
                         tm,
                         cpm,
                         llmod,
@@ -847,12 +837,12 @@ pub(crate) unsafe fn codegen(
             EmitObj::Bitcode => {
                 debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out);
                 if let Err(err) = link_or_copy(&bc_out, &obj_out) {
-                    diag_handler.emit_err(CopyBitcode { err });
+                    dcx.emit_err(CopyBitcode { err });
                 }
 
                 if !config.emit_bc {
                     debug!("removing_bitcode {:?}", bc_out);
-                    ensure_removed(diag_handler, &bc_out);
+                    ensure_removed(dcx, &bc_out);
                 }
             }
 
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index f4b9296dbbd..8f60175a603 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -353,7 +353,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         let new_kind = match ty.kind() {
             Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
             Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
-            t @ (Uint(_) | Int(_)) => t.clone(),
+            t @ (Uint(_) | Int(_)) => *t,
             _ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
         };
 
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 8386f067baf..0befbb5a39b 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -85,6 +85,14 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
 
         let bx = self;
 
+        match coverage.kind {
+            // Marker statements have no effect during codegen,
+            // so return early and don't create `func_coverage`.
+            CoverageKind::SpanMarker => return,
+            // Match exhaustively to ensure that newly-added kinds are classified correctly.
+            CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {}
+        }
+
         let Some(function_coverage_info) =
             bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
         else {
@@ -100,9 +108,9 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
 
         let Coverage { kind } = coverage;
         match *kind {
-            // Span markers are only meaningful during MIR instrumentation,
-            // and have no effect during codegen.
-            CoverageKind::SpanMarker => {}
+            CoverageKind::SpanMarker => unreachable!(
+                "unexpected marker statement {kind:?} should have caused an early return"
+            ),
             CoverageKind::CounterIncrement { id } => {
                 func_coverage.mark_counter_id_seen(id);
                 // We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 7968f238b40..883f82caa80 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -607,7 +607,7 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) ->
 
                     if let Ok(rel_path) = abs_path.strip_prefix(working_directory) {
                         (
-                            working_directory.to_string_lossy().into(),
+                            working_directory.to_string_lossy(),
                             rel_path.to_string_lossy().into_owned(),
                         )
                     } else {
@@ -977,6 +977,27 @@ fn build_field_di_node<'ll, 'tcx>(
     }
 }
 
+/// Returns the `DIFlags` corresponding to the visibility of the item identified by `did`.
+///
+/// `DIFlags::Flag{Public,Protected,Private}` correspond to `DW_AT_accessibility`
+/// (public/protected/private) aren't exactly right for Rust, but neither is `DW_AT_visibility`
+/// (local/exported/qualified), and there's no way to set `DW_AT_visibility` in LLVM's API.
+fn visibility_di_flags<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    did: DefId,
+    type_did: DefId,
+) -> DIFlags {
+    let parent_did = cx.tcx.parent(type_did);
+    let visibility = cx.tcx.visibility(did);
+    match visibility {
+        Visibility::Public => DIFlags::FlagPublic,
+        // Private fields have a restricted visibility of the module containing the type.
+        Visibility::Restricted(did) if did == parent_did => DIFlags::FlagPrivate,
+        // `pub(crate)`/`pub(super)` visibilities are any other restricted visibility.
+        Visibility::Restricted(..) => DIFlags::FlagProtected,
+    }
+}
+
 /// Creates the debuginfo node for a Rust struct type. Maybe be a regular struct or a tuple-struct.
 fn build_struct_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
@@ -1000,7 +1021,7 @@ fn build_struct_type_di_node<'ll, 'tcx>(
             &compute_debuginfo_type_name(cx.tcx, struct_type, false),
             size_and_align_of(struct_type_and_layout),
             Some(containing_scope),
-            DIFlags::FlagZero,
+            visibility_di_flags(cx, adt_def.did(), adt_def.did()),
         ),
         // Fields:
         |cx, owner| {
@@ -1023,7 +1044,7 @@ fn build_struct_type_di_node<'ll, 'tcx>(
                         &field_name[..],
                         (field_layout.size, field_layout.align.abi),
                         struct_type_and_layout.fields.offset(i),
-                        DIFlags::FlagZero,
+                        visibility_di_flags(cx, f.did, adt_def.did()),
                         type_di_node(cx, field_layout.ty),
                     )
                 })
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
index 7ef185250a3..4a2861af44c 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
@@ -26,8 +26,8 @@ use crate::{
             enums::{tag_base_type, DiscrResult},
             file_metadata, size_and_align_of, type_di_node,
             type_map::{self, Stub, UniqueTypeId},
-            unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA,
-            UNKNOWN_LINE_NUMBER,
+            unknown_file_metadata, visibility_di_flags, DINodeCreationResult, SmallVec,
+            NO_GENERICS, NO_SCOPE_METADATA, UNKNOWN_LINE_NUMBER,
         },
         utils::DIB,
     },
@@ -215,7 +215,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
             &enum_type_name,
             cx.size_and_align_of(enum_type),
             NO_SCOPE_METADATA,
-            DIFlags::FlagZero,
+            visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()),
         ),
         |cx, enum_type_di_node| {
             match enum_type_and_layout.variants {
@@ -320,6 +320,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
     variant_index: VariantIdx,
 ) -> SmallVec<&'ll DIType> {
     let variant_layout = enum_type_and_layout.for_variant(cx, variant_index);
+    let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did());
     let variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node(
         cx,
         enum_type_and_layout,
@@ -327,6 +328,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
         variant_index,
         enum_adt_def.variant(variant_index),
         variant_layout,
+        visibility_flags,
     );
 
     let tag_base_type = cx.tcx.types.u32;
@@ -364,7 +366,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
             //       since the later is sometimes smaller (if it has fewer fields).
             size_and_align_of(enum_type_and_layout),
             Size::ZERO,
-            DIFlags::FlagZero,
+            visibility_flags,
             variant_struct_type_wrapper_di_node,
         ),
         unsafe {
@@ -376,7 +378,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
                 unknown_file_metadata(cx),
                 UNKNOWN_LINE_NUMBER,
                 variant_names_type_di_node,
-                DIFlags::FlagZero,
+                visibility_flags,
                 Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)),
                 tag_base_type_align.bits() as u32,
             )
@@ -403,6 +405,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
             (variant_index, variant_name)
         }),
     );
+    let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did());
 
     let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_indices
         .map(|variant_index| {
@@ -417,6 +420,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
                 variant_index,
                 variant_def,
                 variant_layout,
+                visibility_flags,
             );
 
             VariantFieldInfo {
@@ -437,6 +441,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
         tag_base_type,
         tag_field,
         untagged_variant_index,
+        visibility_flags,
     )
 }
 
@@ -744,6 +749,7 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>(
         tag_base_type,
         tag_field,
         None,
+        DIFlags::FlagZero,
     )
 }
 
@@ -758,6 +764,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
     tag_base_type: Ty<'tcx>,
     tag_field: usize,
     untagged_variant_index: Option<VariantIdx>,
+    di_flags: DIFlags,
 ) -> SmallVec<&'ll DIType> {
     let tag_base_type_di_node = type_di_node(cx, tag_base_type);
     let mut unions_fields = SmallVec::with_capacity(variant_field_infos.len() + 1);
@@ -801,7 +808,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
                 align.bits() as u32,
                 // Union fields are always at offset zero
                 Size::ZERO.bits(),
-                DIFlags::FlagZero,
+                di_flags,
                 variant_struct_type_wrapper,
             )
         }
@@ -835,7 +842,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
             TAG_FIELD_NAME_128_LO,
             size_and_align,
             lo_offset,
-            DIFlags::FlagZero,
+            di_flags,
             type_di_node,
         ));
 
@@ -855,7 +862,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
             TAG_FIELD_NAME,
             cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
             enum_type_and_layout.fields.offset(tag_field),
-            DIFlags::FlagZero,
+            di_flags,
             tag_base_type_di_node,
         ));
     }
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
index df1df6d197e..eef8dbb33b4 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
@@ -250,6 +250,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
     variant_index: VariantIdx,
     variant_def: &VariantDef,
     variant_layout: TyAndLayout<'tcx>,
+    di_flags: DIFlags,
 ) -> &'ll DIType {
     debug_assert_eq!(variant_layout.ty, enum_type_and_layout.ty);
 
@@ -267,7 +268,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
             // NOTE: We use size and align of enum_type, not from variant_layout:
             size_and_align_of(enum_type_and_layout),
             Some(enum_type_di_node),
-            DIFlags::FlagZero,
+            di_flags,
         ),
         |cx, struct_type_di_node| {
             (0..variant_layout.fields.count())
@@ -289,7 +290,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
                         &field_name,
                         (field_layout.size, field_layout.align.abi),
                         variant_layout.fields.offset(field_index),
-                        DIFlags::FlagZero,
+                        di_flags,
                         type_di_node(cx, field_layout.ty),
                     )
                 })
@@ -395,7 +396,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
                 })
                 .collect();
 
-            state_specific_fields.into_iter().chain(common_fields.into_iter()).collect()
+            state_specific_fields.into_iter().chain(common_fields).collect()
         },
         |cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty),
     )
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
index 130ca2727e4..cba4e3811d5 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
@@ -7,8 +7,8 @@ use crate::{
             enums::tag_base_type,
             file_metadata, size_and_align_of, type_di_node,
             type_map::{self, Stub, StubInfo, UniqueTypeId},
-            unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS,
-            UNKNOWN_LINE_NUMBER,
+            unknown_file_metadata, visibility_di_flags, DINodeCreationResult, SmallVec,
+            NO_GENERICS, UNKNOWN_LINE_NUMBER,
         },
         utils::{create_DIArray, get_namespace_for_item, DIB},
     },
@@ -63,6 +63,8 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
     let enum_type_and_layout = cx.layout_of(enum_type);
     let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
 
+    let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did());
+
     debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
 
     type_map::build_type_with_children(
@@ -74,7 +76,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
             &enum_type_name,
             size_and_align_of(enum_type_and_layout),
             Some(containing_scope),
-            DIFlags::FlagZero,
+            visibility_flags,
         ),
         |cx, enum_type_di_node| {
             // Build the struct type for each variant. These will be referenced by the
@@ -92,6 +94,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
                         variant_index,
                         enum_adt_def.variant(variant_index),
                         enum_type_and_layout.for_variant(cx, variant_index),
+                        visibility_flags,
                     ),
                     source_info: None,
                 })
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index e6e37a02335..671a225259a 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -5,7 +5,7 @@ use std::path::Path;
 use crate::fluent_generated as fluent;
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_errors::{
-    DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, FatalError, Handler, IntoDiagnostic,
+    DiagCtxt, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, FatalError, IntoDiagnostic,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
@@ -102,13 +102,12 @@ pub(crate) struct DynamicLinkingWithLTO;
 pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);
 
 impl IntoDiagnostic<'_, FatalError> for ParseTargetMachineConfig<'_> {
-    fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, FatalError> {
-        let diag: DiagnosticBuilder<'_, FatalError> = self.0.into_diagnostic(handler);
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, FatalError> {
+        let diag: DiagnosticBuilder<'_, FatalError> = self.0.into_diagnostic(dcx);
         let (message, _) = diag.styled_message().first().expect("`LlvmError` with no message");
-        let message = handler.eagerly_translate_to_string(message.clone(), diag.args());
+        let message = dcx.eagerly_translate_to_string(message.clone(), diag.args());
 
-        let mut diag =
-            handler.struct_almost_fatal(fluent::codegen_llvm_parse_target_machine_config);
+        let mut diag = dcx.struct_almost_fatal(fluent::codegen_llvm_parse_target_machine_config);
         diag.set_arg("error", message);
         diag
     }
@@ -125,8 +124,8 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
 pub(crate) struct MissingFeatures;
 
 impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::codegen_llvm_target_feature_disable_or_enable);
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = dcx.struct_err(fluent::codegen_llvm_target_feature_disable_or_enable);
         if let Some(span) = self.span {
             diag.set_span(span);
         };
@@ -185,7 +184,7 @@ pub enum LlvmError<'a> {
 pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String);
 
 impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for WithLlvmError<'_> {
-    fn into_diagnostic(self, handler: &'_ Handler) -> DiagnosticBuilder<'_, G> {
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, G> {
         use LlvmError::*;
         let msg_with_llvm_err = match &self.0 {
             WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err,
@@ -202,7 +201,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for WithLlvmError<'_> {
             PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err,
             ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err,
         };
-        let mut diag = self.0.into_diagnostic(handler);
+        let mut diag = self.0.into_diagnostic(dcx);
         diag.set_primary_message(msg_with_llvm_err);
         diag.set_arg("llvm_err", self.1);
         diag
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index dfef2fbab56..3c42eb21d07 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -40,7 +40,7 @@ use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::ModuleCodegen;
 use rustc_codegen_ssa::{CodegenResults, CompiledModule};
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::{ErrorGuaranteed, FatalError, Handler};
+use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError};
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::ty::TyCtxt;
@@ -200,10 +200,10 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     fn run_link(
         cgcx: &CodegenContext<Self>,
-        diag_handler: &Handler,
+        dcx: &DiagCtxt,
         modules: Vec<ModuleCodegen<Self::Module>>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError> {
-        back::write::link(cgcx, diag_handler, modules)
+        back::write::link(cgcx, dcx, modules)
     }
     fn run_fat_lto(
         cgcx: &CodegenContext<Self>,
@@ -221,18 +221,18 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     unsafe fn optimize(
         cgcx: &CodegenContext<Self>,
-        diag_handler: &Handler,
+        dcx: &DiagCtxt,
         module: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<(), FatalError> {
-        back::write::optimize(cgcx, diag_handler, module, config)
+        back::write::optimize(cgcx, dcx, module, config)
     }
     fn optimize_fat(
         cgcx: &CodegenContext<Self>,
         module: &mut ModuleCodegen<Self::Module>,
     ) -> Result<(), FatalError> {
-        let diag_handler = cgcx.create_diag_handler();
-        back::lto::run_pass_manager(cgcx, &diag_handler, module, false)
+        let dcx = cgcx.create_dcx();
+        back::lto::run_pass_manager(cgcx, &dcx, module, false)
     }
     unsafe fn optimize_thin(
         cgcx: &CodegenContext<Self>,
@@ -242,11 +242,11 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     unsafe fn codegen(
         cgcx: &CodegenContext<Self>,
-        diag_handler: &Handler,
+        dcx: &DiagCtxt,
         module: ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<CompiledModule, FatalError> {
-        back::write::codegen(cgcx, diag_handler, module, config)
+        back::write::codegen(cgcx, dcx, module, config)
     }
     fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
         back::lto::prepare_thin(module)
@@ -447,16 +447,16 @@ impl ModuleLlvm {
         cgcx: &CodegenContext<LlvmCodegenBackend>,
         name: &CStr,
         buffer: &[u8],
-        handler: &Handler,
+        dcx: &DiagCtxt,
     ) -> Result<Self, FatalError> {
         unsafe {
             let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
-            let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?;
+            let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx)?;
             let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap());
             let tm = match (cgcx.tm_factory)(tm_factory_config) {
                 Ok(m) => m,
                 Err(e) => {
-                    return Err(handler.emit_almost_fatal(ParseTargetMachineConfig(e)));
+                    return Err(dcx.emit_almost_fatal(ParseTargetMachineConfig(e)));
                 }
             };
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 000b2748e4f..b32865a0518 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::temp_dir::MaybeTempDir;
-use rustc_errors::{ErrorGuaranteed, Handler};
+use rustc_errors::{DiagCtxt, ErrorGuaranteed};
 use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_metadata::find_native_static_library;
@@ -52,10 +52,10 @@ use std::path::{Path, PathBuf};
 use std::process::{ExitStatus, Output, Stdio};
 use std::{env, fmt, fs, io, mem, str};
 
-pub fn ensure_removed(diag_handler: &Handler, path: &Path) {
+pub fn ensure_removed(dcx: &DiagCtxt, path: &Path) {
     if let Err(e) = fs::remove_file(path) {
         if e.kind() != io::ErrorKind::NotFound {
-            diag_handler.err(format!("failed to remove {}: {}", path.display(), e));
+            dcx.err(format!("failed to remove {}: {}", path.display(), e));
         }
     }
 }
@@ -143,7 +143,7 @@ pub fn link_binary<'a>(
                 }
             }
             if sess.opts.json_artifact_notifications {
-                sess.diagnostic().emit_artifact_notification(&out_filename, "link");
+                sess.dcx().emit_artifact_notification(&out_filename, "link");
             }
 
             if sess.prof.enabled() {
@@ -183,13 +183,13 @@ pub fn link_binary<'a>(
             |preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| {
                 if !preserve_objects {
                     if let Some(ref obj) = module.object {
-                        ensure_removed(sess.diagnostic(), obj);
+                        ensure_removed(sess.dcx(), obj);
                     }
                 }
 
                 if !preserve_dwarf_objects {
                     if let Some(ref dwo_obj) = module.dwarf_object {
-                        ensure_removed(sess.diagnostic(), dwo_obj);
+                        ensure_removed(sess.dcx(), dwo_obj);
                     }
                 }
             };
@@ -208,7 +208,7 @@ pub fn link_binary<'a>(
 
         // Remove the temporary files if output goes to stdout
         for temp in tempfiles_for_stdout_output {
-            ensure_removed(sess.diagnostic(), &temp);
+            ensure_removed(sess.dcx(), &temp);
         }
 
         // If no requested outputs require linking, then the object temporaries should
@@ -933,7 +933,7 @@ fn link_natively<'a>(
                     command: &cmd,
                     escaped_output,
                 };
-                sess.diagnostic().emit_err(err);
+                sess.dcx().emit_err(err);
                 // If MSVC's `link.exe` was expected but the return code
                 // is not a Microsoft LNK error then suggest a way to fix or
                 // install the Visual Studio build tools.
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index bc0e3a82806..b683e1b45a8 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -158,12 +158,13 @@ pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a
         file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
     {
         let offset = metadata_symbol.address() as usize;
-        if offset < 8 {
+        // The offset specifies the location of rustc metadata in the .info section of XCOFF.
+        // Each string stored in .info section of XCOFF is preceded by a 4-byte length field.
+        if offset < 4 {
             return Err(format!("Invalid metadata symbol offset: {offset}"));
         }
-        // The offset specifies the location of rustc metadata in the comment section.
-        // The metadata is preceded by a 8-byte length field.
-        let len = u64::from_le_bytes(info_data[(offset - 8)..offset].try_into().unwrap()) as usize;
+        // XCOFF format uses big-endian byte order.
+        let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
         if offset + len > (info_data.len() as usize) {
             return Err(format!(
                 "Metadata at offset {offset} with size {len} is beyond .info section"
@@ -478,9 +479,12 @@ pub fn create_wrapper_file(
             file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
             file.section_mut(section).flags =
                 SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
-
-            let len = data.len() as u64;
-            let offset = file.append_section_data(section, &len.to_le_bytes(), 1);
+            // Encode string stored in .info section of XCOFF.
+            // FIXME: The length of data here is not guaranteed to fit in a u32.
+            // We may have to split the data into multiple pieces in order to
+            // store in .info section.
+            let len: u32 = data.len().try_into().unwrap();
+            let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
             // Add a symbol referring to the data in .info section.
             file.add_symbol(Symbol {
                 name: AIX_METADATA_SYMBOL_NAME.into(),
@@ -599,12 +603,12 @@ pub fn create_compressed_metadata_file_for_xcoff(
         section: SymbolSection::Section(data_section),
         flags: SymbolFlags::None,
     });
-    let len = data.len() as u64;
-    let offset = file.append_section_data(section, &len.to_le_bytes(), 1);
+    let len: u32 = data.len().try_into().unwrap();
+    let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
     // Add a symbol referring to the rustc metadata.
     file.add_symbol(Symbol {
         name: AIX_METADATA_SYMBOL_NAME.into(),
-        value: offset + 8, // The metadata is preceded by a 8-byte length field.
+        value: offset + 4, // The metadata is preceded by a 4-byte length field.
         size: 0,
         kind: SymbolKind::Unknown,
         scope: SymbolScope::Dynamic,
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 40fd8c5c1d6..0442bef8a44 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -14,7 +14,7 @@ use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::Emitter;
-use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
+use rustc_errors::{translation::Translate, DiagCtxt, DiagnosticId, FatalError, Level};
 use rustc_errors::{DiagnosticMessage, Style};
 use rustc_fs_util::link_or_copy;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
@@ -344,7 +344,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     /// how to call the compiler with the same arguments.
     pub expanded_args: Vec<String>,
 
-    /// Handler to use for diagnostics produced during codegen.
+    /// Emitter to use for diagnostics produced during codegen.
     pub diag_emitter: SharedEmitter,
     /// LLVM optimizations for which we want to print remarks.
     pub remark: Passes,
@@ -359,8 +359,8 @@ pub struct CodegenContext<B: WriteBackendMethods> {
 }
 
 impl<B: WriteBackendMethods> CodegenContext<B> {
-    pub fn create_diag_handler(&self) -> Handler {
-        Handler::with_emitter(Box::new(self.diag_emitter.clone()))
+    pub fn create_dcx(&self) -> DiagCtxt {
+        DiagCtxt::with_emitter(Box::new(self.diag_emitter.clone()))
     }
 
     pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
@@ -558,7 +558,7 @@ fn produce_final_output_artifacts(
             }
             if !sess.opts.cg.save_temps && !keep_numbered {
                 // The user just wants `foo.x`, not `foo.#module-name#.x`.
-                ensure_removed(sess.diagnostic(), &path);
+                ensure_removed(sess.dcx(), &path);
             }
         } else {
             let extension = crate_output
@@ -649,19 +649,19 @@ fn produce_final_output_artifacts(
         for module in compiled_modules.modules.iter() {
             if let Some(ref path) = module.object {
                 if !keep_numbered_objects {
-                    ensure_removed(sess.diagnostic(), path);
+                    ensure_removed(sess.dcx(), path);
                 }
             }
 
             if let Some(ref path) = module.dwarf_object {
                 if !keep_numbered_objects {
-                    ensure_removed(sess.diagnostic(), path);
+                    ensure_removed(sess.dcx(), path);
                 }
             }
 
             if let Some(ref path) = module.bytecode {
                 if !keep_numbered_bitcode {
-                    ensure_removed(sess.diagnostic(), path);
+                    ensure_removed(sess.dcx(), path);
                 }
             }
         }
@@ -669,7 +669,7 @@ fn produce_final_output_artifacts(
         if !user_wants_bitcode {
             if let Some(ref allocator_module) = compiled_modules.allocator_module {
                 if let Some(ref path) = allocator_module.bytecode {
-                    ensure_removed(sess.diagnostic(), path);
+                    ensure_removed(sess.dcx(), path);
                 }
             }
         }
@@ -825,10 +825,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
     module: ModuleCodegen<B::Module>,
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
-    let diag_handler = cgcx.create_diag_handler();
+    let dcx = cgcx.create_dcx();
 
     unsafe {
-        B::optimize(cgcx, &diag_handler, &module, module_config)?;
+        B::optimize(cgcx, &dcx, &module, module_config)?;
     }
 
     // After we've done the initial round of optimizations we need to
@@ -891,11 +891,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
         match link_or_copy(&source_file, &output_path) {
             Ok(_) => Some(output_path),
             Err(error) => {
-                cgcx.create_diag_handler().emit_err(errors::CopyPathBuf {
-                    source_file,
-                    output_path,
-                    error,
-                });
+                cgcx.create_dcx().emit_err(errors::CopyPathBuf { source_file, output_path, error });
                 None
             }
         }
@@ -939,13 +935,13 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
     module: ModuleCodegen<B::Module>,
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
-    let diag_handler = cgcx.create_diag_handler();
+    let dcx = cgcx.create_dcx();
 
     if !cgcx.opts.unstable_opts.combine_cgu
         || module.kind == ModuleKind::Metadata
         || module.kind == ModuleKind::Allocator
     {
-        let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? };
+        let module = unsafe { B::codegen(cgcx, &dcx, module, module_config)? };
         Ok(WorkItemResult::Finished(module))
     } else {
         Ok(WorkItemResult::NeedsLink(module))
@@ -1595,11 +1591,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
         let needs_link = mem::take(&mut needs_link);
         if !needs_link.is_empty() {
             assert!(compiled_modules.is_empty());
-            let diag_handler = cgcx.create_diag_handler();
-            let module = B::run_link(&cgcx, &diag_handler, needs_link).map_err(|_| ())?;
+            let dcx = cgcx.create_dcx();
+            let module = B::run_link(&cgcx, &dcx, needs_link).map_err(|_| ())?;
             let module = unsafe {
-                B::codegen(&cgcx, &diag_handler, module, cgcx.config(ModuleKind::Regular))
-                    .map_err(|_| ())?
+                B::codegen(&cgcx, &dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?
             };
             compiled_modules.push(module);
         }
@@ -1842,13 +1837,13 @@ impl SharedEmitterMain {
 
             match message {
                 Ok(SharedEmitterMessage::Diagnostic(diag)) => {
-                    let handler = sess.diagnostic();
+                    let dcx = sess.dcx();
                     let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msg);
                     if let Some(code) = diag.code {
                         d.code(code);
                     }
                     d.replace_args(diag.args);
-                    handler.emit_diagnostic(d);
+                    dcx.emit_diagnostic(d);
                 }
                 Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
                     let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 40a985cf255..e529956b1ba 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -386,7 +386,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                                 [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
                                     if !tcx.sess.target.has_thumb_interworking {
                                         struct_span_err!(
-                                            tcx.sess.diagnostic(),
+                                            tcx.sess.dcx(),
                                             attr.span,
                                             E0779,
                                             "target does not support `#[instruction_set]`"
@@ -403,7 +403,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                                 }
                                 _ => {
                                     struct_span_err!(
-                                        tcx.sess.diagnostic(),
+                                        tcx.sess.dcx(),
                                         attr.span,
                                         E0779,
                                         "invalid instruction set specified",
@@ -415,7 +415,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         }
                         [] => {
                             struct_span_err!(
-                                tcx.sess.diagnostic(),
+                                tcx.sess.dcx(),
                                 attr.span,
                                 E0778,
                                 "`#[instruction_set]` requires an argument"
@@ -425,7 +425,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         }
                         _ => {
                             struct_span_err!(
-                                tcx.sess.diagnostic(),
+                                tcx.sess.dcx(),
                                 attr.span,
                                 E0779,
                                 "cannot specify more than one instruction set"
@@ -443,7 +443,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     rustc_attr::parse_alignment(&literal.kind)
                         .map_err(|msg| {
                             struct_span_err!(
-                                tcx.sess.diagnostic(),
+                                tcx.sess.dcx(),
                                 attr.span,
                                 E0589,
                                 "invalid `repr(align)` attribute: {}",
@@ -469,27 +469,17 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             Some(MetaItemKind::List(ref items)) => {
                 inline_span = Some(attr.span);
                 if items.len() != 1 {
-                    struct_span_err!(
-                        tcx.sess.diagnostic(),
-                        attr.span,
-                        E0534,
-                        "expected one argument"
-                    )
-                    .emit();
+                    struct_span_err!(tcx.sess.dcx(), attr.span, E0534, "expected one argument")
+                        .emit();
                     InlineAttr::None
                 } else if list_contains_name(items, sym::always) {
                     InlineAttr::Always
                 } else if list_contains_name(items, sym::never) {
                     InlineAttr::Never
                 } else {
-                    struct_span_err!(
-                        tcx.sess.diagnostic(),
-                        items[0].span(),
-                        E0535,
-                        "invalid argument"
-                    )
-                    .help("valid inline arguments are `always` and `never`")
-                    .emit();
+                    struct_span_err!(tcx.sess.dcx(), items[0].span(), E0535, "invalid argument")
+                        .help("valid inline arguments are `always` and `never`")
+                        .emit();
 
                     InlineAttr::None
                 }
@@ -503,7 +493,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         if !attr.has_name(sym::optimize) {
             return ia;
         }
-        let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit();
+        let err = |sp, s| struct_span_err!(tcx.sess.dcx(), sp, E0722, "{}", s).emit();
         match attr.meta_kind() {
             Some(MetaItemKind::Word) => {
                 err(attr.span, "expected one argument");
@@ -578,7 +568,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     hir_id,
                     no_sanitize_span,
                     "`no_sanitize` will have no effect after inlining",
-                    |lint| lint.span_note(inline_span, "inlining requested here"),
+                    |lint| {
+                        lint.span_note(inline_span, "inlining requested here");
+                    },
                 )
             }
         }
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index ed6ac9f9c5d..668d39afbda 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -4,7 +4,7 @@ use crate::assert_module_sources::CguReuse;
 use crate::back::command::Command;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
     IntoDiagnosticArg,
 };
 use rustc_macros::Diagnostic;
@@ -210,192 +210,191 @@ pub enum LinkRlibError {
 pub struct ThorinErrorWrapper(pub thorin::Error);
 
 impl IntoDiagnostic<'_> for ThorinErrorWrapper {
-    fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
         let mut diag;
         match self.0 {
             thorin::Error::ReadInput(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_read_input_failure);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_read_input_failure);
                 diag
             }
             thorin::Error::ParseFileKind(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_input_file_kind);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_input_file_kind);
                 diag
             }
             thorin::Error::ParseObjectFile(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_input_object_file);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_input_object_file);
                 diag
             }
             thorin::Error::ParseArchiveFile(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_input_archive_file);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_input_archive_file);
                 diag
             }
             thorin::Error::ParseArchiveMember(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_archive_member);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_archive_member);
                 diag
             }
             thorin::Error::InvalidInputKind => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_invalid_input_kind);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_invalid_input_kind);
                 diag
             }
             thorin::Error::DecompressData(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_decompress_data);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_decompress_data);
                 diag
             }
             thorin::Error::NamelessSection(_, offset) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_section_without_name);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_section_without_name);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::RelocationWithInvalidSymbol(section, offset) => {
-                diag =
-                    handler.struct_err(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol);
                 diag.set_arg("section", section);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::MultipleRelocations(section, offset) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_multiple_relocations);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_multiple_relocations);
                 diag.set_arg("section", section);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::UnsupportedRelocation(section, offset) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_unsupported_relocation);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_unsupported_relocation);
                 diag.set_arg("section", section);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::MissingDwoName(id) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_missing_dwo_name);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_missing_dwo_name);
                 diag.set_arg("id", format!("0x{id:08x}"));
                 diag
             }
             thorin::Error::NoCompilationUnits => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_no_compilation_units);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_no_compilation_units);
                 diag
             }
             thorin::Error::NoDie => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_no_die);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_no_die);
                 diag
             }
             thorin::Error::TopLevelDieNotUnit => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_top_level_die_not_unit);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_top_level_die_not_unit);
                 diag
             }
             thorin::Error::MissingRequiredSection(section) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_missing_required_section);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_missing_required_section);
                 diag.set_arg("section", section);
                 diag
             }
             thorin::Error::ParseUnitAbbreviations(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_unit_abbreviations);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit_abbreviations);
                 diag
             }
             thorin::Error::ParseUnitAttribute(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_unit_attribute);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit_attribute);
                 diag
             }
             thorin::Error::ParseUnitHeader(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_unit_header);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit_header);
                 diag
             }
             thorin::Error::ParseUnit(_) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_unit);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit);
                 diag
             }
             thorin::Error::IncompatibleIndexVersion(section, format, actual) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_incompatible_index_version);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_incompatible_index_version);
                 diag.set_arg("section", section);
                 diag.set_arg("actual", actual);
                 diag.set_arg("format", format);
                 diag
             }
             thorin::Error::OffsetAtIndex(_, index) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_offset_at_index);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_offset_at_index);
                 diag.set_arg("index", index);
                 diag
             }
             thorin::Error::StrAtOffset(_, offset) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_str_at_offset);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_str_at_offset);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::ParseIndex(_, section) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_parse_index);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_index);
                 diag.set_arg("section", section);
                 diag
             }
             thorin::Error::UnitNotInIndex(unit) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_unit_not_in_index);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_unit_not_in_index);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::RowNotInIndex(_, row) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_row_not_in_index);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_row_not_in_index);
                 diag.set_arg("row", row);
                 diag
             }
             thorin::Error::SectionNotInRow => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_section_not_in_row);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_section_not_in_row);
                 diag
             }
             thorin::Error::EmptyUnit(unit) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_empty_unit);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_empty_unit);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::MultipleDebugInfoSection => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_multiple_debug_info_section);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_multiple_debug_info_section);
                 diag
             }
             thorin::Error::MultipleDebugTypesSection => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_multiple_debug_types_section);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_multiple_debug_types_section);
                 diag
             }
             thorin::Error::NotSplitUnit => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_not_split_unit);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_not_split_unit);
                 diag
             }
             thorin::Error::DuplicateUnit(unit) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_duplicate_unit);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_duplicate_unit);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::MissingReferencedUnit(unit) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_missing_referenced_unit);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_missing_referenced_unit);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::NoOutputObjectCreated => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_not_output_object_created);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_not_output_object_created);
                 diag
             }
             thorin::Error::MixedInputEncodings => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_mixed_input_encodings);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_mixed_input_encodings);
                 diag
             }
             thorin::Error::Io(e) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_io);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_io);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::ObjectRead(e) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_object_read);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_object_read);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::ObjectWrite(e) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_object_write);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_object_write);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::GimliRead(e) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_gimli_read);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_gimli_read);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::GimliWrite(e) => {
-                diag = handler.struct_err(fluent::codegen_ssa_thorin_gimli_write);
+                diag = dcx.struct_err(fluent::codegen_ssa_thorin_gimli_write);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
@@ -412,8 +411,8 @@ pub struct LinkingFailed<'a> {
 }
 
 impl IntoDiagnostic<'_> for LinkingFailed<'_> {
-    fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::codegen_ssa_linking_failed);
+    fn into_diagnostic(self, dcx: &DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = dcx.struct_err(fluent::codegen_ssa_linking_failed);
         diag.set_arg("linker_path", format!("{}", self.linker_path.display()));
         diag.set_arg("exit_status", format!("{}", self.exit_status));
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs
index ecf5095d8a3..048540894ac 100644
--- a/compiler/rustc_codegen_ssa/src/traits/write.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/write.rs
@@ -2,7 +2,7 @@ use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
 use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig};
 use crate::{CompiledModule, ModuleCodegen};
 
-use rustc_errors::{FatalError, Handler};
+use rustc_errors::{DiagCtxt, FatalError};
 use rustc_middle::dep_graph::WorkProduct;
 
 pub trait WriteBackendMethods: 'static + Sized + Clone {
@@ -16,7 +16,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     /// Merge all modules into main_module and returning it
     fn run_link(
         cgcx: &CodegenContext<Self>,
-        diag_handler: &Handler,
+        dcx: &DiagCtxt,
         modules: Vec<ModuleCodegen<Self::Module>>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     /// Performs fat LTO by merging all modules into a single one and returning it
@@ -38,7 +38,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     fn print_statistics(&self);
     unsafe fn optimize(
         cgcx: &CodegenContext<Self>,
-        diag_handler: &Handler,
+        dcx: &DiagCtxt,
         module: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<(), FatalError>;
@@ -52,7 +52,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     unsafe fn codegen(
         cgcx: &CodegenContext<Self>,
-        diag_handler: &Handler,
+        dcx: &DiagCtxt,
         module: ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<CompiledModule, FatalError>;
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 26cf3b3f2b0..8f18cd78d3f 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -154,7 +154,7 @@ where
             let mut err = tcx.sess.create_err(err);
 
             let msg = error.diagnostic_message();
-            error.add_args(tcx.sess.diagnostic(), &mut err);
+            error.add_args(tcx.sess.dcx(), &mut err);
 
             // Use *our* span to label the interp error
             err.span_label(our_span, msg);
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 46fb64fd5b3..adce1f5430f 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -1,5 +1,5 @@
 use rustc_errors::{
-    DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee,
     IntoDiagnostic,
 };
 use rustc_hir::ConstContext;
@@ -432,11 +432,7 @@ pub struct UndefinedBehavior {
 pub trait ReportErrorExt {
     /// Returns the diagnostic message for this error.
     fn diagnostic_message(&self) -> DiagnosticMessage;
-    fn add_args<G: EmissionGuarantee>(
-        self,
-        handler: &Handler,
-        builder: &mut DiagnosticBuilder<'_, G>,
-    );
+    fn add_args<G: EmissionGuarantee>(self, dcx: &DiagCtxt, builder: &mut DiagnosticBuilder<'_, G>);
 
     fn debug(self) -> String
     where
@@ -444,17 +440,17 @@ pub trait ReportErrorExt {
     {
         ty::tls::with(move |tcx| {
             let mut builder = tcx.sess.struct_allow(DiagnosticMessage::Str(String::new().into()));
-            let handler = tcx.sess.diagnostic();
+            let dcx = tcx.sess.dcx();
             let message = self.diagnostic_message();
-            self.add_args(handler, &mut builder);
-            let s = handler.eagerly_translate_to_string(message, builder.args());
+            self.add_args(dcx, &mut builder);
+            let s = dcx.eagerly_translate_to_string(message, builder.args());
             builder.cancel();
             s
         })
     }
 }
 
-fn bad_pointer_message(msg: CheckInAllocMsg, handler: &Handler) -> String {
+fn bad_pointer_message(msg: CheckInAllocMsg, dcx: &DiagCtxt) -> String {
     use crate::fluent_generated::*;
 
     let msg = match msg {
@@ -464,7 +460,7 @@ fn bad_pointer_message(msg: CheckInAllocMsg, handler: &Handler) -> String {
         CheckInAllocMsg::InboundsTest => const_eval_in_bounds_test,
     };
 
-    handler.eagerly_translate_to_string(msg, [].into_iter())
+    dcx.eagerly_translate_to_string(msg, [].into_iter())
 }
 
 impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
@@ -514,7 +510,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
 
     fn add_args<G: EmissionGuarantee>(
         self,
-        handler: &Handler,
+        dcx: &DiagCtxt,
         builder: &mut DiagnosticBuilder<'_, G>,
     ) {
         use UndefinedBehaviorInfo::*;
@@ -525,7 +521,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
                     builder.set_arg(name, value);
                 });
             }
-            ValidationError(e) => e.add_args(handler, builder),
+            ValidationError(e) => e.add_args(dcx, builder),
 
             Unreachable
             | DivisionByZero
@@ -549,7 +545,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             PointerUseAfterFree(alloc_id, msg) => {
                 builder
                     .set_arg("alloc_id", alloc_id)
-                    .set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
+                    .set_arg("bad_pointer_message", bad_pointer_message(msg, dcx));
             }
             PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => {
                 builder
@@ -557,14 +553,14 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
                     .set_arg("alloc_size", alloc_size.bytes())
                     .set_arg("ptr_offset", ptr_offset)
                     .set_arg("ptr_size", ptr_size.bytes())
-                    .set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
+                    .set_arg("bad_pointer_message", bad_pointer_message(msg, dcx));
             }
             DanglingIntPointer(ptr, msg) => {
                 if ptr != 0 {
                     builder.set_arg("pointer", format!("{ptr:#x}[noalloc]"));
                 }
 
-                builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
+                builder.set_arg("bad_pointer_message", bad_pointer_message(msg, dcx));
             }
             AlignmentCheckFailed(Misalignment { required, has }, msg) => {
                 builder.set_arg("required", required.bytes());
@@ -678,7 +674,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
         }
     }
 
-    fn add_args<G: EmissionGuarantee>(self, handler: &Handler, err: &mut DiagnosticBuilder<'_, G>) {
+    fn add_args<G: EmissionGuarantee>(self, dcx: &DiagCtxt, err: &mut DiagnosticBuilder<'_, G>) {
         use crate::fluent_generated as fluent;
         use rustc_middle::mir::interpret::ValidationErrorKind::*;
 
@@ -688,12 +684,12 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
         }
 
         let message = if let Some(path) = self.path {
-            handler.eagerly_translate_to_string(
+            dcx.eagerly_translate_to_string(
                 fluent::const_eval_validation_front_matter_invalid_value_with_path,
                 [("path".into(), DiagnosticArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)),
             )
         } else {
-            handler.eagerly_translate_to_string(
+            dcx.eagerly_translate_to_string(
                 fluent::const_eval_validation_front_matter_invalid_value,
                 [].into_iter(),
             )
@@ -704,7 +700,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
         fn add_range_arg<G: EmissionGuarantee>(
             r: WrappingRange,
             max_hi: u128,
-            handler: &Handler,
+            dcx: &DiagCtxt,
             err: &mut DiagnosticBuilder<'_, G>,
         ) {
             let WrappingRange { start: lo, end: hi } = r;
@@ -728,7 +724,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
                 ("hi".into(), DiagnosticArgValue::Str(hi.to_string().into())),
             ];
             let args = args.iter().map(|(a, b)| (a, b));
-            let message = handler.eagerly_translate_to_string(msg, args);
+            let message = dcx.eagerly_translate_to_string(msg, args);
             err.set_arg("in_range", message);
         }
 
@@ -750,7 +746,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
                     ExpectedKind::EnumTag => fluent::const_eval_validation_expected_enum_tag,
                     ExpectedKind::Str => fluent::const_eval_validation_expected_str,
                 };
-                let msg = handler.eagerly_translate_to_string(msg, [].into_iter());
+                let msg = dcx.eagerly_translate_to_string(msg, [].into_iter());
                 err.set_arg("expected", msg);
             }
             InvalidEnumTag { value }
@@ -761,11 +757,11 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
                 err.set_arg("value", value);
             }
             NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => {
-                add_range_arg(range, max_value, handler, err)
+                add_range_arg(range, max_value, dcx, err)
             }
             OutOfRange { range, max_value, value } => {
                 err.set_arg("value", value);
-                add_range_arg(range, max_value, handler, err);
+                add_range_arg(range, max_value, dcx, err);
             }
             UnalignedPtr { required_bytes, found_bytes, .. } => {
                 err.set_arg("required_bytes", required_bytes);
@@ -804,7 +800,7 @@ impl ReportErrorExt for UnsupportedOpInfo {
             UnsupportedOpInfo::ReadExternStatic(_) => const_eval_read_extern_static,
         }
     }
-    fn add_args<G: EmissionGuarantee>(self, _: &Handler, builder: &mut DiagnosticBuilder<'_, G>) {
+    fn add_args<G: EmissionGuarantee>(self, _: &DiagCtxt, builder: &mut DiagnosticBuilder<'_, G>) {
         use crate::fluent_generated::*;
 
         use UnsupportedOpInfo::*;
@@ -839,14 +835,14 @@ impl<'tcx> ReportErrorExt for InterpError<'tcx> {
     }
     fn add_args<G: EmissionGuarantee>(
         self,
-        handler: &Handler,
+        dcx: &DiagCtxt,
         builder: &mut DiagnosticBuilder<'_, G>,
     ) {
         match self {
-            InterpError::UndefinedBehavior(ub) => ub.add_args(handler, builder),
-            InterpError::Unsupported(e) => e.add_args(handler, builder),
-            InterpError::InvalidProgram(e) => e.add_args(handler, builder),
-            InterpError::ResourceExhaustion(e) => e.add_args(handler, builder),
+            InterpError::UndefinedBehavior(ub) => ub.add_args(dcx, builder),
+            InterpError::Unsupported(e) => e.add_args(dcx, builder),
+            InterpError::InvalidProgram(e) => e.add_args(dcx, builder),
+            InterpError::ResourceExhaustion(e) => e.add_args(dcx, builder),
             InterpError::MachineStop(e) => e.add_args(&mut |name, value| {
                 builder.set_arg(name, value);
             }),
@@ -871,7 +867,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
     }
     fn add_args<G: EmissionGuarantee>(
         self,
-        handler: &Handler,
+        dcx: &DiagCtxt,
         builder: &mut DiagnosticBuilder<'_, G>,
     ) {
         match self {
@@ -879,7 +875,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
             | InvalidProgramInfo::AlreadyReported(_)
             | InvalidProgramInfo::ConstPropNonsense => {}
             InvalidProgramInfo::Layout(e) => {
-                let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler);
+                let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(dcx);
                 for (name, val) in diag.args() {
                     builder.set_arg(name.clone(), val.clone());
                 }
@@ -904,5 +900,5 @@ impl ReportErrorExt for ResourceExhaustionInfo {
             ResourceExhaustionInfo::AddressSpaceFull => const_eval_address_space_full,
         }
     }
-    fn add_args<G: EmissionGuarantee>(self, _: &Handler, _: &mut DiagnosticBuilder<'_, G>) {}
+    fn add_args<G: EmissionGuarantee>(self, _: &DiagCtxt, _: &mut DiagnosticBuilder<'_, G>) {}
 }
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 847d6503f20..af8e5e7d151 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -473,12 +473,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         backtrace.print_backtrace();
         // FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
         // label and arguments from the InterpError.
-        let handler = self.tcx.sess.diagnostic();
+        let dcx = self.tcx.sess.dcx();
         #[allow(rustc::untranslatable_diagnostic)]
         let mut diag = self.tcx.sess.struct_allow("");
         let msg = e.diagnostic_message();
-        e.add_args(handler, &mut diag);
-        let s = handler.eagerly_translate_to_string(msg, diag.args());
+        e.add_args(dcx, &mut diag);
+        let s = dcx.eagerly_translate_to_string(msg, diag.args());
         diag.cancel();
         s
     }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index bb17602d3ba..949606ed6c9 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -278,7 +278,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
         let secondary_errors = mem::take(&mut self.secondary_errors);
         if self.error_emitted.is_none() {
             for error in secondary_errors {
-                self.tcx.sess.diagnostic().emit_diagnostic(error);
+                self.tcx.sess.dcx().emit_diagnostic(error);
             }
         } else {
             assert!(self.tcx.sess.has_errors().is_some());
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index eaf4abf39b7..cca5b90abb9 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -130,7 +130,7 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
         let span = self.body.source_info(location).span;
         // We use `span_delayed_bug` as we might see broken MIR when other errors have already
         // occurred.
-        self.tcx.sess.diagnostic().span_delayed_bug(
+        self.tcx.sess.dcx().span_delayed_bug(
             span,
             format!(
                 "broken MIR in {:?} ({}) at {:?}:\n{}",
@@ -571,7 +571,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
 
     fn visit_source_scope(&mut self, scope: SourceScope) {
         if self.body.source_scopes.get(scope).is_none() {
-            self.tcx.sess.diagnostic().span_delayed_bug(
+            self.tcx.sess.dcx().span_delayed_bug(
                 self.body.span,
                 format!(
                     "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs
index 60b343afbed..ed2e558bffa 100644
--- a/compiler/rustc_data_structures/src/sorted_map.rs
+++ b/compiler/rustc_data_structures/src/sorted_map.rs
@@ -198,7 +198,7 @@ impl<K: Ord, V> SortedMap<K, V> {
                 if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 {
                     // We can copy the whole range without having to mix with
                     // existing elements.
-                    self.data.splice(index..index, elements.into_iter());
+                    self.data.splice(index..index, elements);
                     return;
                 }
 
diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs
index 654d7636da2..dc546da7342 100644
--- a/compiler/rustc_driver_impl/src/args.rs
+++ b/compiler/rustc_driver_impl/src/args.rs
@@ -3,7 +3,7 @@ use std::fmt;
 use std::fs;
 use std::io;
 
-use rustc_session::EarlyErrorHandler;
+use rustc_session::EarlyDiagCtxt;
 
 fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
     if let Some(path) = arg.strip_prefix('@') {
@@ -23,12 +23,12 @@ fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
 /// **Note:** This function doesn't interpret argument 0 in any special way.
 /// If this function is intended to be used with command line arguments,
 /// `argv[0]` must be removed prior to calling it manually.
-pub fn arg_expand_all(handler: &EarlyErrorHandler, at_args: &[String]) -> Vec<String> {
+pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> {
     let mut args = Vec::new();
     for arg in at_args {
         match arg_expand(arg.clone()) {
             Ok(arg) => args.extend(arg),
-            Err(err) => handler.early_error(format!("Failed to load argument file: {err}")),
+            Err(err) => early_dcx.early_error(format!("Failed to load argument file: {err}")),
         }
     }
     args
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index e49db64536f..d67fea7e9a4 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -27,7 +27,7 @@ use rustc_data_structures::profiling::{
 use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
 use rustc_errors::{markdown, ColorConfig};
-use rustc_errors::{ErrorGuaranteed, Handler, PResult};
+use rustc_errors::{DiagCtxt, ErrorGuaranteed, PResult};
 use rustc_feature::find_gated_cfg;
 use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
 use rustc_interface::{interface, Queries};
@@ -38,7 +38,7 @@ use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
 use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType, TrimmedDefPaths};
 use rustc_session::getopts::{self, Matches};
 use rustc_session::lint::{Lint, LintId};
-use rustc_session::{config, EarlyErrorHandler, Session};
+use rustc_session::{config, EarlyDiagCtxt, Session};
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::source_map::FileLoader;
 use rustc_span::symbol::sym;
@@ -291,7 +291,7 @@ fn run_compiler(
     >,
     using_internal_features: Arc<std::sync::atomic::AtomicBool>,
 ) -> interface::Result<()> {
-    let mut default_handler = EarlyErrorHandler::new(ErrorOutputType::default());
+    let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
     // Throw away the first argument, the name of the binary.
     // In case of at_args being empty, as might be the case by
@@ -303,14 +303,14 @@ fn run_compiler(
     // the compiler with @empty_file as argv[0] and no more arguments.
     let at_args = at_args.get(1..).unwrap_or_default();
 
-    let args = args::arg_expand_all(&default_handler, at_args);
+    let args = args::arg_expand_all(&default_early_dcx, at_args);
 
-    let Some(matches) = handle_options(&default_handler, &args) else { return Ok(()) };
+    let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) };
 
-    let sopts = config::build_session_options(&mut default_handler, &matches);
+    let sopts = config::build_session_options(&mut default_early_dcx, &matches);
 
     if let Some(ref code) = matches.opt_str("explain") {
-        handle_explain(&default_handler, diagnostics_registry(), code, sopts.color);
+        handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color);
         return Ok(());
     }
 
@@ -336,7 +336,7 @@ fn run_compiler(
         expanded_args: args,
     };
 
-    let has_input = match make_input(&default_handler, &matches.free) {
+    let has_input = match make_input(&default_early_dcx, &matches.free) {
         Err(reported) => return Err(reported),
         Ok(Some(input)) => {
             config.input = input;
@@ -345,7 +345,7 @@ fn run_compiler(
         Ok(None) => match matches.free.len() {
             0 => false, // no input: we will exit early
             1 => panic!("make_input should have provided valid inputs"),
-            _ => default_handler.early_error(format!(
+            _ => default_early_dcx.early_error(format!(
                 "multiple input filenames provided (first two filenames are `{}` and `{}`)",
                 matches.free[0], matches.free[1],
             )),
@@ -354,8 +354,8 @@ fn run_compiler(
 
     callbacks.config(&mut config);
 
-    default_handler.abort_if_errors();
-    drop(default_handler);
+    default_early_dcx.abort_if_errors();
+    drop(default_early_dcx);
 
     interface::run_compiler(config, |compiler| {
         let sess = &compiler.sess;
@@ -369,18 +369,18 @@ fn run_compiler(
             return sess.compile_status();
         }
 
-        let handler = EarlyErrorHandler::new(sess.opts.error_format);
+        let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format);
 
-        if print_crate_info(&handler, codegen_backend, sess, has_input) == Compilation::Stop {
+        if print_crate_info(&early_dcx, codegen_backend, sess, has_input) == Compilation::Stop {
             return sess.compile_status();
         }
 
         if !has_input {
-            handler.early_error("no input filename given"); // this is fatal
+            early_dcx.early_error("no input filename given"); // this is fatal
         }
 
         if !sess.opts.unstable_opts.ls.is_empty() {
-            list_metadata(&handler, sess, &*codegen_backend.metadata_loader());
+            list_metadata(&early_dcx, sess, &*codegen_backend.metadata_loader());
             return sess.compile_status();
         }
 
@@ -495,7 +495,7 @@ fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileNa
 
 // Extract input (string or file and optional path) from matches.
 fn make_input(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     free_matches: &[String],
 ) -> Result<Option<Input>, ErrorGuaranteed> {
     if free_matches.len() == 1 {
@@ -505,7 +505,7 @@ fn make_input(
             if io::stdin().read_to_string(&mut src).is_err() {
                 // Immediately stop compilation if there was an issue reading
                 // the input (for example if the input stream is not UTF-8).
-                let reported = handler.early_error_no_abort(
+                let reported = early_dcx.early_error_no_abort(
                     "couldn't read from stdin, as it did not contain valid UTF-8",
                 );
                 return Err(reported);
@@ -537,7 +537,7 @@ pub enum Compilation {
     Continue,
 }
 
-fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str, color: ColorConfig) {
+fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, color: ColorConfig) {
     let upper_cased_code = code.to_ascii_uppercase();
     let normalised =
         if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
@@ -567,7 +567,7 @@ fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str, c
             }
         }
         Err(InvalidErrorCode) => {
-            handler.early_error(format!("{code} is not a valid error code"));
+            early_dcx.early_error(format!("{code} is not a valid error code"));
         }
     }
 }
@@ -669,11 +669,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) {
     }
 }
 
-fn list_metadata(
-    handler: &EarlyErrorHandler,
-    sess: &Session,
-    metadata_loader: &dyn MetadataLoader,
-) {
+fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dyn MetadataLoader) {
     match sess.io.input {
         Input::File(ref ifile) => {
             let path = &(*ifile);
@@ -689,13 +685,13 @@ fn list_metadata(
             safe_println!("{}", String::from_utf8(v).unwrap());
         }
         Input::Str { .. } => {
-            handler.early_error("cannot list metadata for stdin");
+            early_dcx.early_error("cannot list metadata for stdin");
         }
     }
 }
 
 fn print_crate_info(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     codegen_backend: &dyn CodegenBackend,
     sess: &Session,
     parse_attrs: bool,
@@ -842,7 +838,7 @@ fn print_crate_info(
                         .expect("unknown Apple target OS");
                     println_info!("deployment_target={}", format!("{major}.{minor}"))
                 } else {
-                    handler
+                    early_dcx
                         .early_error("only Apple targets currently support deployment version info")
                 }
             }
@@ -856,12 +852,12 @@ fn print_crate_info(
 /// Prints version information
 ///
 /// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate.
-pub macro version($handler: expr, $binary: literal, $matches: expr) {
+pub macro version($early_dcx: expr, $binary: literal, $matches: expr) {
     fn unw(x: Option<&str>) -> &str {
         x.unwrap_or("unknown")
     }
     $crate::version_at_macro_invocation(
-        $handler,
+        $early_dcx,
         $binary,
         $matches,
         unw(option_env!("CFG_VERSION")),
@@ -873,7 +869,7 @@ pub macro version($handler: expr, $binary: literal, $matches: expr) {
 
 #[doc(hidden)] // use the macro instead
 pub fn version_at_macro_invocation(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     binary: &str,
     matches: &getopts::Matches,
     version: &str,
@@ -894,7 +890,7 @@ pub fn version_at_macro_invocation(
 
         let debug_flags = matches.opt_strs("Z");
         let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
-        get_codegen_backend(handler, &None, backend_name).print_version();
+        get_codegen_backend(early_dcx, &None, backend_name).print_version();
     }
 }
 
@@ -1072,7 +1068,7 @@ Available lint options:
 /// Show help for flag categories shared between rustdoc and rustc.
 ///
 /// Returns whether a help option was printed.
-pub fn describe_flag_categories(handler: &EarlyErrorHandler, matches: &Matches) -> bool {
+pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> bool {
     // Handle the special case of -Wall.
     let wall = matches.opt_strs("W");
     if wall.iter().any(|x| *x == "all") {
@@ -1094,12 +1090,12 @@ pub fn describe_flag_categories(handler: &EarlyErrorHandler, matches: &Matches)
     }
 
     if cg_flags.iter().any(|x| *x == "no-stack-check") {
-        handler.early_warn("the --no-stack-check flag is deprecated and does nothing");
+        early_dcx.early_warn("the --no-stack-check flag is deprecated and does nothing");
     }
 
     if cg_flags.iter().any(|x| *x == "passes=list") {
         let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
-        get_codegen_backend(handler, &None, backend_name).print_passes();
+        get_codegen_backend(early_dcx, &None, backend_name).print_passes();
         return true;
     }
 
@@ -1160,7 +1156,7 @@ fn print_flag_list<T>(
 /// This does not need to be `pub` for rustc itself, but @chaosite needs it to
 /// be public when using rustc as a library, see
 /// <https://github.com/rust-lang/rust/commit/2b4c33817a5aaecabf4c6598d41e190080ec119e>
-pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
+pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<getopts::Matches> {
     if args.is_empty() {
         // user did not write `-v` nor `-Z unstable-options`, so do not
         // include that extra information.
@@ -1186,7 +1182,7 @@ pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<ge
                 .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
             _ => None,
         };
-        handler.early_error(msg.unwrap_or_else(|| e.to_string()));
+        early_dcx.early_error(msg.unwrap_or_else(|| e.to_string()));
     });
 
     // For all options we just parsed, we check a few aspects:
@@ -1200,7 +1196,7 @@ pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<ge
     //   we're good to go.
     // * Otherwise, if we're an unstable option then we generate an error
     //   (unstable option being used on stable)
-    nightly_options::check_nightly_options(handler, &matches, &config::rustc_optgroups());
+    nightly_options::check_nightly_options(early_dcx, &matches, &config::rustc_optgroups());
 
     if matches.opt_present("h") || matches.opt_present("help") {
         // Only show unstable options in --help if we accept unstable options.
@@ -1210,12 +1206,12 @@ pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<ge
         return None;
     }
 
-    if describe_flag_categories(handler, &matches) {
+    if describe_flag_categories(early_dcx, &matches) {
         return None;
     }
 
     if matches.opt_present("version") {
-        version!(handler, "rustc", &matches);
+        version!(early_dcx, "rustc", &matches);
         return None;
     }
 
@@ -1310,7 +1306,10 @@ fn ice_path() -> &'static Option<PathBuf> {
 /// internal features.
 ///
 /// A custom rustc driver can skip calling this to set up a custom ICE hook.
-pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) -> Arc<AtomicBool> {
+pub fn install_ice_hook(
+    bug_report_url: &'static str,
+    extra_info: fn(&DiagCtxt),
+) -> Arc<AtomicBool> {
     // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
     // full backtraces. When a compiler ICE happens, we want to gather
     // as much information as possible to present in the issue opened
@@ -1333,8 +1332,8 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
                 if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
                 {
                     // the error code is already going to be reported when the panic unwinds up the stack
-                    let handler = EarlyErrorHandler::new(ErrorOutputType::default());
-                    let _ = handler.early_error_no_abort(msg.clone());
+                    let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
+                    let _ = early_dcx.early_error_no_abort(msg.clone());
                     return;
                 }
             };
@@ -1388,7 +1387,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
 fn report_ice(
     info: &panic::PanicInfo<'_>,
     bug_report_url: &str,
-    extra_info: fn(&Handler),
+    extra_info: fn(&DiagCtxt),
     using_internal_features: &AtomicBool,
 ) {
     let fallback_bundle =
@@ -1397,20 +1396,20 @@ fn report_ice(
         rustc_errors::ColorConfig::Auto,
         fallback_bundle,
     ));
-    let handler = rustc_errors::Handler::with_emitter(emitter);
+    let dcx = rustc_errors::DiagCtxt::with_emitter(emitter);
 
     // a .span_bug or .bug call has already printed what
     // it wants to print.
     if !info.payload().is::<rustc_errors::ExplicitBug>()
         && !info.payload().is::<rustc_errors::DelayedBugPanic>()
     {
-        handler.emit_err(session_diagnostics::Ice);
+        dcx.emit_err(session_diagnostics::Ice);
     }
 
     if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) {
-        handler.emit_note(session_diagnostics::IceBugReportInternalFeature);
+        dcx.emit_note(session_diagnostics::IceBugReportInternalFeature);
     } else {
-        handler.emit_note(session_diagnostics::IceBugReport { bug_report_url });
+        dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url });
     }
 
     let version = util::version_str!().unwrap_or("unknown_version");
@@ -1422,7 +1421,7 @@ fn report_ice(
         // Create the ICE dump target file.
         match crate::fs::File::options().create(true).append(true).open(&path) {
             Ok(mut file) => {
-                handler.emit_note(session_diagnostics::IcePath { path: path.clone() });
+                dcx.emit_note(session_diagnostics::IcePath { path: path.clone() });
                 if FIRST_PANIC.swap(false, Ordering::SeqCst) {
                     let _ = write!(file, "\n\nrustc version: {version}\nplatform: {triple}");
                 }
@@ -1430,26 +1429,26 @@ fn report_ice(
             }
             Err(err) => {
                 // The path ICE couldn't be written to disk, provide feedback to the user as to why.
-                handler.emit_warning(session_diagnostics::IcePathError {
+                dcx.emit_warning(session_diagnostics::IcePathError {
                     path: path.clone(),
                     error: err.to_string(),
                     env_var: std::env::var_os("RUSTC_ICE")
                         .map(PathBuf::from)
                         .map(|env_var| session_diagnostics::IcePathErrorEnv { env_var }),
                 });
-                handler.emit_note(session_diagnostics::IceVersion { version, triple });
+                dcx.emit_note(session_diagnostics::IceVersion { version, triple });
                 None
             }
         }
     } else {
-        handler.emit_note(session_diagnostics::IceVersion { version, triple });
+        dcx.emit_note(session_diagnostics::IceVersion { version, triple });
         None
     };
 
     if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() {
-        handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
+        dcx.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
         if excluded_cargo_defaults {
-            handler.emit_note(session_diagnostics::IceExcludeCargoDefaults);
+            dcx.emit_note(session_diagnostics::IceExcludeCargoDefaults);
         }
     }
 
@@ -1458,11 +1457,11 @@ fn report_ice(
 
     let num_frames = if backtrace { None } else { Some(2) };
 
-    interface::try_print_query_stack(&handler, num_frames, file);
+    interface::try_print_query_stack(&dcx, num_frames, file);
 
     // We don't trust this callback not to panic itself, so run it at the end after we're sure we've
     // printed all the relevant info.
-    extra_info(&handler);
+    extra_info(&dcx);
 
     #[cfg(windows)]
     if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
@@ -1473,16 +1472,16 @@ fn report_ice(
 
 /// This allows tools to enable rust logging without having to magically match rustc's
 /// tracing crate version.
-pub fn init_rustc_env_logger(handler: &EarlyErrorHandler) {
-    init_logger(handler, rustc_log::LoggerConfig::from_env("RUSTC_LOG"));
+pub fn init_rustc_env_logger(early_dcx: &EarlyDiagCtxt) {
+    init_logger(early_dcx, rustc_log::LoggerConfig::from_env("RUSTC_LOG"));
 }
 
 /// This allows tools to enable rust logging without having to magically match rustc's
 /// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose
 /// the values directly rather than having to set an environment variable.
-pub fn init_logger(handler: &EarlyErrorHandler, cfg: rustc_log::LoggerConfig) {
+pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) {
     if let Err(error) = rustc_log::init_logger(cfg) {
-        handler.early_error(error.to_string());
+        early_dcx.early_error(error.to_string());
     }
 }
 
@@ -1490,9 +1489,9 @@ pub fn main() -> ! {
     let start_time = Instant::now();
     let start_rss = get_resident_set_size();
 
-    let handler = EarlyErrorHandler::new(ErrorOutputType::default());
+    let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
-    init_rustc_env_logger(&handler);
+    init_rustc_env_logger(&early_dcx);
     signal_handler::install();
     let mut callbacks = TimePassesCallbacks::default();
     let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
@@ -1501,7 +1500,7 @@ pub fn main() -> ! {
             .enumerate()
             .map(|(i, arg)| {
                 arg.into_string().unwrap_or_else(|arg| {
-                    handler.early_error(format!("argument {i} is not valid Unicode: {arg:?}"))
+                    early_dcx.early_error(format!("argument {i} is not valid Unicode: {arg:?}"))
                 })
             })
             .collect::<Vec<_>>();
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 4fb63d67e78..be506806065 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -91,10 +91,7 @@ where
 #[rustc_diagnostic_item = "DecorateLint"]
 pub trait DecorateLint<'a, G: EmissionGuarantee> {
     /// Decorate and emit a lint.
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut DiagnosticBuilder<'a, G>,
-    ) -> &'b mut DiagnosticBuilder<'a, G>;
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, G>);
 
     fn msg(&self) -> DiagnosticMessage;
 }
@@ -889,13 +886,13 @@ impl Diagnostic {
     /// interpolated variables).
     pub fn eager_subdiagnostic(
         &mut self,
-        handler: &crate::Handler,
+        dcx: &crate::DiagCtxt,
         subdiagnostic: impl AddToDiagnostic,
     ) -> &mut Self {
         subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
             let args = diag.args();
             let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
-            handler.eagerly_translate(msg, args)
+            dcx.eagerly_translate(msg, args)
         });
         self
     }
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 315e47c0971..3f66af1fcff 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -1,9 +1,9 @@
 use crate::diagnostic::IntoDiagnosticArg;
+use crate::{DiagCtxt, Level, MultiSpan, StashKey};
 use crate::{
     Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed,
     ExplicitBug, SubdiagnosticMessage,
 };
-use crate::{Handler, Level, MultiSpan, StashKey};
 use rustc_lint_defs::Applicability;
 use rustc_span::source_map::Spanned;
 
@@ -19,9 +19,9 @@ use std::thread::panicking;
 /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
 #[rustc_diagnostic_item = "IntoDiagnostic"]
 pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
-    /// Write out as a diagnostic out of `Handler`.
+    /// Write out as a diagnostic out of `DiagCtxt`.
     #[must_use]
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G>;
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, G>;
 }
 
 impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T>
@@ -29,8 +29,8 @@ where
     T: IntoDiagnostic<'a, G>,
     G: EmissionGuarantee,
 {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
-        let mut diag = self.node.into_diagnostic(handler);
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, G> {
+        let mut diag = self.node.into_diagnostic(dcx);
         diag.set_span(self.span);
         diag
     }
@@ -40,7 +40,7 @@ where
 ///
 /// If there is some state in a downstream crate you would like to
 /// access in the methods of `DiagnosticBuilder` here, consider
-/// extending `HandlerFlags`, accessed via `self.handler.flags`.
+/// extending `DiagCtxtFlags`.
 #[must_use]
 #[derive(Clone)]
 pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> {
@@ -74,8 +74,8 @@ struct DiagnosticBuilderInner<'a> {
 enum DiagnosticBuilderState<'a> {
     /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`.
     ///
-    /// The `Diagnostic` will be emitted through this `Handler`.
-    Emittable(&'a Handler),
+    /// The `Diagnostic` will be emitted through this `DiagCtxt`.
+    Emittable(&'a DiagCtxt),
 
     /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`.
     ///
@@ -95,7 +95,7 @@ enum DiagnosticBuilderState<'a> {
 // `DiagnosticBuilderState` should be pointer-sized.
 rustc_data_structures::static_assert_size!(
     DiagnosticBuilderState<'_>,
-    std::mem::size_of::<&Handler>()
+    std::mem::size_of::<&DiagCtxt>()
 );
 
 /// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
@@ -110,7 +110,7 @@ pub trait EmissionGuarantee: Sized {
     /// Creates a new `DiagnosticBuilder` that will return this type of guarantee.
     #[track_caller]
     fn make_diagnostic_builder(
-        handler: &Handler,
+        dcx: &DiagCtxt,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, Self>;
 }
@@ -128,11 +128,11 @@ impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
 impl EmissionGuarantee for ErrorGuaranteed {
     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) => {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
                 db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
 
-                let guar = handler.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                let guar = dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
 
                 // Only allow a guarantee if the `level` wasn't switched to a
                 // non-error - the field isn't `pub`, but the whole `Diagnostic`
@@ -166,10 +166,10 @@ impl EmissionGuarantee for ErrorGuaranteed {
 
     #[track_caller]
     fn make_diagnostic_builder(
-        handler: &Handler,
+        dcx: &DiagCtxt,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(handler, Level::Error { lint: false }, msg)
+        DiagnosticBuilder::new(dcx, Level::Error { lint: false }, msg)
     }
 }
 
@@ -177,11 +177,11 @@ impl EmissionGuarantee for ErrorGuaranteed {
 impl EmissionGuarantee for () {
     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) => {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
                 db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
 
-                handler.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
             }
             // `.emit()` was previously called, disallowed from repeating it.
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -189,10 +189,10 @@ impl EmissionGuarantee for () {
     }
 
     fn make_diagnostic_builder(
-        handler: &Handler,
+        dcx: &DiagCtxt,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(handler, Level::Warning(None), msg)
+        DiagnosticBuilder::new(dcx, Level::Warning(None), msg)
     }
 }
 
@@ -204,10 +204,10 @@ pub struct Noted;
 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) => {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
                 db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-                handler.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
             }
             // `.emit()` was previously called, disallowed from repeating it.
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -217,10 +217,10 @@ impl EmissionGuarantee for Noted {
     }
 
     fn make_diagnostic_builder(
-        handler: &Handler,
+        dcx: &DiagCtxt,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(handler, Level::Note, msg)
+        DiagnosticBuilder::new(dcx, Level::Note, msg)
     }
 }
 
@@ -232,11 +232,11 @@ pub struct Bug;
 impl EmissionGuarantee for Bug {
     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) => {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
                 db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
 
-                handler.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
             }
             // `.emit()` was previously called, disallowed from repeating it.
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -246,21 +246,21 @@ impl EmissionGuarantee for Bug {
     }
 
     fn make_diagnostic_builder(
-        handler: &Handler,
+        dcx: &DiagCtxt,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(handler, Level::Bug, msg)
+        DiagnosticBuilder::new(dcx, Level::Bug, msg)
     }
 }
 
 impl EmissionGuarantee for ! {
     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) => {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
                 db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
 
-                handler.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
             }
             // `.emit()` was previously called, disallowed from repeating it.
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -270,21 +270,21 @@ impl EmissionGuarantee for ! {
     }
 
     fn make_diagnostic_builder(
-        handler: &Handler,
+        dcx: &DiagCtxt,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(handler, Level::Fatal, msg)
+        DiagnosticBuilder::new(dcx, Level::Fatal, msg)
     }
 }
 
 impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
     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) => {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
                 db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
 
-                handler.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
             }
             // `.emit()` was previously called, disallowed from repeating it.
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -294,10 +294,10 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
     }
 
     fn make_diagnostic_builder(
-        handler: &Handler,
+        dcx: &DiagCtxt,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(handler, Level::Fatal, msg)
+        DiagnosticBuilder::new(dcx, Level::Fatal, msg)
     }
 }
 
@@ -340,25 +340,25 @@ impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
 
 impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// Convenience function for internal use, clients should use one of the
-    /// `struct_*` methods on [`Handler`].
+    /// `struct_*` methods on [`DiagCtxt`].
     #[track_caller]
     pub(crate) fn new<M: Into<DiagnosticMessage>>(
-        handler: &'a Handler,
+        dcx: &'a DiagCtxt,
         level: Level,
         message: M,
     ) -> Self {
         let diagnostic = Diagnostic::new(level, message);
-        Self::new_diagnostic(handler, diagnostic)
+        Self::new_diagnostic(dcx, diagnostic)
     }
 
     /// Creates a new `DiagnosticBuilder` with an already constructed
     /// diagnostic.
     #[track_caller]
-    pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+    pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diagnostic: Diagnostic) -> Self {
         debug!("Created new diagnostic");
         Self {
             inner: DiagnosticBuilderInner {
-                state: DiagnosticBuilderState::Emittable(handler),
+                state: DiagnosticBuilderState::Emittable(dcx),
                 diagnostic: Box::new(diagnostic),
             },
             _marker: PhantomData,
@@ -398,7 +398,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
     /// Stashes diagnostic for possible later improvement in a different,
     /// later stage of the compiler. The diagnostic can be accessed with
-    /// the provided `span` and `key` through [`Handler::steal_diagnostic()`].
+    /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`].
     ///
     /// As with `buffer`, this is unless the handler has disabled such buffering.
     pub fn stash(self, span: Span, key: StashKey) {
@@ -409,18 +409,18 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
     /// Converts the builder to a `Diagnostic` for later emission,
     /// unless handler has disabled such buffering, or `.emit()` was called.
-    pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
-        let handler = match self.inner.state {
-            // No `.emit()` calls, the `&Handler` is still available.
-            DiagnosticBuilderState::Emittable(handler) => handler,
+    pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
+        let dcx = match self.inner.state {
+            // No `.emit()` calls, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => dcx,
             // `.emit()` was previously called, nothing we can do.
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
                 return None;
             }
         };
 
-        if handler.inner.lock().flags.dont_buffer_diagnostics
-            || handler.inner.lock().flags.treat_err_as_bug.is_some()
+        if dcx.inner.lock().flags.dont_buffer_diagnostics
+            || dcx.inner.lock().flags.treat_err_as_bug.is_some()
         {
             self.emit();
             return None;
@@ -437,13 +437,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
         // actually emitted.
         debug!("buffer: diagnostic={:?}", diagnostic);
 
-        Some((diagnostic, handler))
+        Some((diagnostic, dcx))
     }
 
-    /// Retrieves the [`Handler`] if available
-    pub fn handler(&self) -> Option<&Handler> {
+    /// Retrieves the [`DiagCtxt`] if available
+    pub fn dcx(&self) -> Option<&DiagCtxt> {
         match self.inner.state {
-            DiagnosticBuilderState::Emittable(handler) => Some(handler),
+            DiagnosticBuilderState::Emittable(dcx) => Some(dcx),
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None,
         }
     }
@@ -640,15 +640,15 @@ impl Drop for DiagnosticBuilderInner<'_> {
     fn drop(&mut self) {
         match self.state {
             // No `.emit()` or `.cancel()` calls.
-            DiagnosticBuilderState::Emittable(handler) => {
+            DiagnosticBuilderState::Emittable(dcx) => {
                 if !panicking() {
-                    handler.emit_diagnostic(Diagnostic::new(
+                    dcx.emit_diagnostic(Diagnostic::new(
                         Level::Bug,
                         DiagnosticMessage::from(
                             "the following error was constructed but not emitted",
                         ),
                     ));
-                    handler.emit_diagnostic_without_consuming(&mut self.diagnostic);
+                    dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
                     panic!("error was constructed but not emitted");
                 }
             }
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 4f77f09b26e..3e4b3ee758a 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,6 +1,6 @@
 use crate::diagnostic::DiagnosticLocation;
 use crate::{fluent_generated as fluent, AddToDiagnostic};
-use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg};
+use crate::{DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, IntoDiagnostic, IntoDiagnosticArg};
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_hir as hir;
@@ -246,18 +246,18 @@ impl<Id> IntoDiagnosticArg for hir::def::Res<Id> {
 }
 
 impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
-    fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
+    fn into_diagnostic(self, dcx: &DiagCtxt) -> DiagnosticBuilder<'_, !> {
         let mut diag;
         match self {
             TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => {
-                diag = handler.struct_fatal(fluent::errors_target_invalid_address_space);
+                diag = dcx.struct_fatal(fluent::errors_target_invalid_address_space);
                 diag.set_arg("addr_space", addr_space);
                 diag.set_arg("cause", cause);
                 diag.set_arg("err", err);
                 diag
             }
             TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => {
-                diag = handler.struct_fatal(fluent::errors_target_invalid_bits);
+                diag = dcx.struct_fatal(fluent::errors_target_invalid_bits);
                 diag.set_arg("kind", kind);
                 diag.set_arg("bit", bit);
                 diag.set_arg("cause", cause);
@@ -265,31 +265,31 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
                 diag
             }
             TargetDataLayoutErrors::MissingAlignment { cause } => {
-                diag = handler.struct_fatal(fluent::errors_target_missing_alignment);
+                diag = dcx.struct_fatal(fluent::errors_target_missing_alignment);
                 diag.set_arg("cause", cause);
                 diag
             }
             TargetDataLayoutErrors::InvalidAlignment { cause, err } => {
-                diag = handler.struct_fatal(fluent::errors_target_invalid_alignment);
+                diag = dcx.struct_fatal(fluent::errors_target_invalid_alignment);
                 diag.set_arg("cause", cause);
                 diag.set_arg("err_kind", err.diag_ident());
                 diag.set_arg("align", err.align());
                 diag
             }
             TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {
-                diag = handler.struct_fatal(fluent::errors_target_inconsistent_architecture);
+                diag = dcx.struct_fatal(fluent::errors_target_inconsistent_architecture);
                 diag.set_arg("dl", dl);
                 diag.set_arg("target", target);
                 diag
             }
             TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => {
-                diag = handler.struct_fatal(fluent::errors_target_inconsistent_pointer_width);
+                diag = dcx.struct_fatal(fluent::errors_target_inconsistent_pointer_width);
                 diag.set_arg("pointer_size", pointer_size);
                 diag.set_arg("target", target);
                 diag
             }
             TargetDataLayoutErrors::InvalidBitsSize { err } => {
-                diag = handler.struct_fatal(fluent::errors_target_invalid_bits_size);
+                diag = dcx.struct_fatal(fluent::errors_target_invalid_bits_size);
                 diag.set_arg("err", err);
                 diag
             }
@@ -378,3 +378,9 @@ pub struct IndicateAnonymousLifetime {
     pub count: usize,
     pub suggestion: String,
 }
+
+impl IntoDiagnosticArg for type_ir::ClosureKind {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(self.as_str().into())
+    }
+}
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 379883a0c18..3fb993c3651 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -16,8 +16,8 @@ use crate::snippet::{
 use crate::styled_buffer::StyledBuffer;
 use crate::translation::{to_fluent_args, Translate};
 use crate::{
-    diagnostic::DiagnosticLocation, CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage,
-    FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
+    diagnostic::DiagnosticLocation, CodeSuggestion, DiagCtxt, Diagnostic, DiagnosticId,
+    DiagnosticMessage, FluentBundle, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
     SubstitutionHighlight, SuggestionStyle, TerminalUrl,
 };
 use rustc_lint_defs::pluralize;
@@ -553,10 +553,10 @@ impl Emitter for EmitterWriter {
 }
 
 /// An emitter that does nothing when emitting a non-fatal diagnostic.
-/// Fatal diagnostics are forwarded to `fatal_handler` to avoid silent
+/// Fatal diagnostics are forwarded to `fatal_dcx` to avoid silent
 /// failures of rustc, as witnessed e.g. in issue #89358.
 pub struct SilentEmitter {
-    pub fatal_handler: Handler,
+    pub fatal_dcx: DiagCtxt,
     pub fatal_note: Option<String>,
 }
 
@@ -581,7 +581,7 @@ impl Emitter for SilentEmitter {
             if let Some(ref note) = self.fatal_note {
                 d.note(note.clone());
             }
-            self.fatal_handler.emit_diagnostic(d);
+            self.fatal_dcx.emit_diagnostic(d);
         }
     }
 }
@@ -2677,10 +2677,7 @@ fn from_stderr(color: ColorConfig) -> Destination {
 /// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead.
 ///
 /// See #36178.
-#[cfg(windows)]
-const BRIGHT_BLUE: Color = Color::Cyan;
-#[cfg(not(windows))]
-const BRIGHT_BLUE: Color = Color::Blue;
+const BRIGHT_BLUE: Color = if cfg!(windows) { Color::Cyan } else { Color::Blue };
 
 impl Style {
     fn color_spec(&self, lvl: Level) -> ColorSpec {
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index 5f9e821a48c..303de0a93f6 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -1,7 +1,7 @@
 use super::*;
 
 use crate::emitter::ColorConfig;
-use crate::Handler;
+use crate::DiagCtxt;
 use rustc_span::BytePos;
 
 use std::str;
@@ -61,8 +61,8 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
         );
 
         let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
-        let handler = Handler::with_emitter(Box::new(je));
-        handler.span_err(span, "foo");
+        let dcx = DiagCtxt::with_emitter(Box::new(je));
+        dcx.span_err(span, "foo");
 
         let bytes = output.lock().unwrap();
         let actual_output = str::from_utf8(&bytes).unwrap();
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index cf73c638d85..959e26fec70 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -412,15 +412,15 @@ use std::backtrace::{Backtrace, BacktraceStatus};
 /// A handler deals with errors and other compiler output.
 /// Certain errors (fatal, bug, unimpl) may cause immediate exit,
 /// others log errors for later reporting.
-pub struct Handler {
-    inner: Lock<HandlerInner>,
+pub struct DiagCtxt {
+    inner: Lock<DiagCtxtInner>,
 }
 
 /// This inner struct exists to keep it all behind a single lock;
 /// this is done to prevent possible deadlocks in a multi-threaded compiler,
 /// as well as inconsistent state observation.
-struct HandlerInner {
-    flags: HandlerFlags,
+struct DiagCtxtInner {
+    flags: DiagCtxtFlags,
     /// The number of lint errors that have been emitted.
     lint_err_count: usize,
     /// The number of errors that have been emitted, including duplicates.
@@ -518,7 +518,7 @@ pub static TRACK_DIAGNOSTICS: AtomicRef<fn(&mut Diagnostic, &mut dyn FnMut(&mut
     AtomicRef::new(&(default_track_diagnostic as _));
 
 #[derive(Copy, Clone, Default)]
-pub struct HandlerFlags {
+pub struct DiagCtxtFlags {
     /// If false, warning-level lints are suppressed.
     /// (rustc: see `--allow warnings` and `--cap-lints`)
     pub can_emit_warnings: bool,
@@ -540,7 +540,7 @@ pub struct HandlerFlags {
     pub track_diagnostics: bool,
 }
 
-impl Drop for HandlerInner {
+impl Drop for DiagCtxtInner {
     fn drop(&mut self) {
         self.emit_stashed_diagnostics();
 
@@ -572,7 +572,7 @@ impl Drop for HandlerInner {
     }
 }
 
-impl Handler {
+impl DiagCtxt {
     pub fn with_tty_emitter(
         sm: Option<Lrc<SourceMap>>,
         fallback_bundle: LazyFallbackBundle,
@@ -585,7 +585,7 @@ impl Handler {
         self
     }
 
-    pub fn with_flags(mut self, flags: HandlerFlags) -> Self {
+    pub fn with_flags(mut self, flags: DiagCtxtFlags) -> Self {
         self.inner.get_mut().flags = flags;
         self
     }
@@ -597,8 +597,8 @@ impl Handler {
 
     pub fn with_emitter(emitter: Box<DynEmitter>) -> Self {
         Self {
-            inner: Lock::new(HandlerInner {
-                flags: HandlerFlags { can_emit_warnings: true, ..Default::default() },
+            inner: Lock::new(DiagCtxtInner {
+                flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() },
                 lint_err_count: 0,
                 err_count: 0,
                 warn_count: 0,
@@ -1057,7 +1057,7 @@ impl Handler {
         inner.emit_diagnostic(diagnostic).unwrap()
     }
 
-    // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
+    // FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
     // where the explanation of what "good path" is (also, it should be renamed).
     pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
         let mut inner = self.inner.borrow_mut();
@@ -1396,12 +1396,12 @@ impl Handler {
     }
 
     /// This methods steals all [`LintExpectationId`]s that are stored inside
-    /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled.
+    /// [`DiagCtxtInner`] and indicate that the linked expectation has been fulfilled.
     #[must_use]
     pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet<LintExpectationId> {
         assert!(
             self.inner.borrow().unstable_expect_diagnostics.is_empty(),
-            "`HandlerInner::unstable_expect_diagnostics` should be empty at this point",
+            "`DiagCtxtInner::unstable_expect_diagnostics` should be empty at this point",
         );
         std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations)
     }
@@ -1413,11 +1413,11 @@ impl Handler {
     }
 }
 
-// Note: we prefer implementing operations on `Handler`, rather than
-// `HandlerInner`, whenever possible. This minimizes functions where
-// `Handler::foo()` just borrows `inner` and forwards a call to
+// Note: we prefer implementing operations on `DiagCtxt`, rather than
+// `DiagCtxtInner`, whenever possible. This minimizes functions where
+// `DiagCtxt::foo()` just borrows `inner` and forwards a call to
 // `HanderInner::foo`.
-impl HandlerInner {
+impl DiagCtxtInner {
     /// Emit all stashed diagnostics.
     fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
         let has_errors = self.has_errors();
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index d75556ac7c4..b63609c48e9 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -790,7 +790,7 @@ impl SyntaxExtension {
             .map(|attr| {
                 // Override `helper_attrs` passed above if it's a built-in macro,
                 // marking `proc_macro_derive` macros as built-in is not a realistic use case.
-                parse_macro_name_and_helper_attrs(sess.diagnostic(), attr, "built-in").map_or_else(
+                parse_macro_name_and_helper_attrs(sess.dcx(), attr, "built-in").map_or_else(
                     || (Some(name), Vec::new()),
                     |(name, helper_attrs)| (Some(name), helper_attrs),
                 )
@@ -1119,7 +1119,7 @@ impl<'a> ExtCtxt<'a> {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        self.sess.diagnostic().struct_span_err(sp, msg)
+        self.sess.dcx().struct_span_err(sp, msg)
     }
 
     #[track_caller]
@@ -1143,10 +1143,10 @@ impl<'a> ExtCtxt<'a> {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
-        self.sess.diagnostic().span_err(sp, msg);
+        self.sess.dcx().span_err(sp, msg);
     }
     pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.sess.diagnostic().span_bug(sp, msg);
+        self.sess.dcx().span_bug(sp, msg);
     }
     pub fn trace_macros_diag(&mut self) {
         for (span, notes) in self.expansions.iter() {
@@ -1160,7 +1160,7 @@ impl<'a> ExtCtxt<'a> {
         self.expansions.clear();
     }
     pub fn bug(&self, msg: &'static str) -> ! {
-        self.sess.diagnostic().bug(msg);
+        self.sess.dcx().bug(msg);
     }
     pub fn trace_macros(&self) -> bool {
         self.ecfg.trace_mac
@@ -1208,7 +1208,7 @@ pub fn resolve_path(
                     span,
                     path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
                 }
-                .into_diagnostic(&parse_sess.span_diagnostic));
+                .into_diagnostic(&parse_sess.dcx));
             }
         };
         result.pop();
@@ -1350,7 +1350,7 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<
 }
 
 pub fn parse_macro_name_and_helper_attrs(
-    diag: &rustc_errors::Handler,
+    dcx: &rustc_errors::DiagCtxt,
     attr: &Attribute,
     macro_type: &str,
 ) -> Option<(Symbol, Vec<Symbol>)> {
@@ -1359,23 +1359,23 @@ pub fn parse_macro_name_and_helper_attrs(
     // `#[proc_macro_derive(Foo, attributes(A, ..))]`
     let list = attr.meta_item_list()?;
     if list.len() != 1 && list.len() != 2 {
-        diag.emit_err(errors::AttrNoArguments { span: attr.span });
+        dcx.emit_err(errors::AttrNoArguments { span: attr.span });
         return None;
     }
     let Some(trait_attr) = list[0].meta_item() else {
-        diag.emit_err(errors::NotAMetaItem { span: list[0].span() });
+        dcx.emit_err(errors::NotAMetaItem { span: list[0].span() });
         return None;
     };
     let trait_ident = match trait_attr.ident() {
         Some(trait_ident) if trait_attr.is_word() => trait_ident,
         _ => {
-            diag.emit_err(errors::OnlyOneWord { span: trait_attr.span });
+            dcx.emit_err(errors::OnlyOneWord { span: trait_attr.span });
             return None;
         }
     };
 
     if !trait_ident.name.can_be_raw() {
-        diag.emit_err(errors::CannotBeNameOfMacro {
+        dcx.emit_err(errors::CannotBeNameOfMacro {
             span: trait_attr.span,
             trait_ident,
             macro_type,
@@ -1385,29 +1385,29 @@ pub fn parse_macro_name_and_helper_attrs(
     let attributes_attr = list.get(1);
     let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
         if !attr.has_name(sym::attributes) {
-            diag.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
+            dcx.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
         }
         attr.meta_item_list()
             .unwrap_or_else(|| {
-                diag.emit_err(errors::AttributesWrongForm { span: attr.span() });
+                dcx.emit_err(errors::AttributesWrongForm { span: attr.span() });
                 &[]
             })
             .iter()
             .filter_map(|attr| {
                 let Some(attr) = attr.meta_item() else {
-                    diag.emit_err(errors::AttributeMetaItem { span: attr.span() });
+                    dcx.emit_err(errors::AttributeMetaItem { span: attr.span() });
                     return None;
                 };
 
                 let ident = match attr.ident() {
                     Some(ident) if attr.is_word() => ident,
                     _ => {
-                        diag.emit_err(errors::AttributeSingleWord { span: attr.span });
+                        dcx.emit_err(errors::AttributeSingleWord { span: attr.span });
                         return None;
                     }
                 };
                 if !ident.name.can_be_raw() {
-                    diag.emit_err(errors::HelperAttributeNameInvalid {
+                    dcx.emit_err(errors::HelperAttributeNameInvalid {
                         span: attr.span,
                         name: ident,
                     });
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 0b1f25b67c8..e66cfbe6fb6 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -205,7 +205,7 @@ pub(super) fn check_meta_variables(
     rhses: &[TokenTree],
 ) -> bool {
     if lhses.len() != rhses.len() {
-        sess.span_diagnostic.span_bug(span, "length mismatch between LHSes and RHSes")
+        sess.dcx.span_bug(span, "length mismatch between LHSes and RHSes")
     }
     let mut valid = true;
     for (lhs, rhs) in iter::zip(lhses, rhses) {
@@ -244,7 +244,7 @@ fn check_binders(
         // MetaVar(fragment) and not as MetaVarDecl(y, fragment).
         TokenTree::MetaVar(span, name) => {
             if macros.is_empty() {
-                sess.span_diagnostic.span_bug(span, "unexpected MetaVar in lhs");
+                sess.dcx.span_bug(span, "unexpected MetaVar in lhs");
             }
             let name = MacroRulesNormalizedIdent::new(name);
             // There are 3 possibilities:
@@ -275,14 +275,13 @@ fn check_binders(
                 );
             }
             if !macros.is_empty() {
-                sess.span_diagnostic.span_bug(span, "unexpected MetaVarDecl in nested lhs");
+                sess.dcx.span_bug(span, "unexpected MetaVarDecl in nested lhs");
             }
             let name = MacroRulesNormalizedIdent::new(name);
             if let Some(prev_info) = get_binder_info(macros, binders, name) {
                 // Duplicate binders at the top-level macro definition are errors. The lint is only
                 // for nested macro definitions.
-                sess.span_diagnostic
-                    .emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span });
+                sess.dcx.emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span });
                 *valid = false;
             } else {
                 binders.insert(name, BinderInfo { span, ops: ops.into() });
@@ -341,7 +340,7 @@ fn check_occurrences(
     match *rhs {
         TokenTree::Token(..) => {}
         TokenTree::MetaVarDecl(span, _name, _kind) => {
-            sess.span_diagnostic.span_bug(span, "unexpected MetaVarDecl in rhs")
+            sess.dcx.span_bug(span, "unexpected MetaVarDecl in rhs")
         }
         TokenTree::MetaVar(span, name) => {
             let name = MacroRulesNormalizedIdent::new(name);
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 393eec3997b..44f10e7d380 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -236,6 +236,13 @@ fn expand_macro<'cx>(
                             target_sp.open = source_sp.open.with_ctxt(ctxt);
                             target_sp.close = source_sp.close.with_ctxt(ctxt);
                         }
+                        (
+                            TokenTree::Delimited(target_sp, ..),
+                            mbe::TokenTree::MetaVar(source_sp, ..),
+                        ) => {
+                            target_sp.open = source_sp.with_ctxt(ctxt);
+                            target_sp.close = source_sp.with_ctxt(ctxt).shrink_to_hi();
+                        }
                         _ => {
                             let sp = rhs_tt.span().with_ctxt(ctxt);
                             tt.set_span(sp);
@@ -395,7 +402,7 @@ pub fn compile_declarative_macro(
     };
     let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new());
 
-    let diag = &sess.parse_sess.span_diagnostic;
+    let dcx = &sess.parse_sess.dcx;
     let lhs_nm = Ident::new(sym::lhs, def.span);
     let rhs_nm = Ident::new(sym::rhs, def.span);
     let tt_spec = Some(NonterminalKind::TT);
@@ -475,14 +482,14 @@ pub fn compile_declarative_macro(
 
                 let s = parse_failure_msg(&token);
                 let sp = token.span.substitute_dummy(def.span);
-                let mut err = sess.diagnostic().struct_span_err(sp, s);
+                let mut err = sess.dcx().struct_span_err(sp, s);
                 err.span_label(sp, msg);
                 annotate_doc_comment(&mut err, sess.source_map(), sp);
                 err.emit();
                 return dummy_syn_ext();
             }
             Error(sp, msg) => {
-                sess.diagnostic().struct_span_err(sp.substitute_dummy(def.span), msg).emit();
+                sess.dcx().struct_span_err(sp.substitute_dummy(def.span), msg).emit();
                 return dummy_syn_ext();
             }
             ErrorReported(_) => {
@@ -511,10 +518,10 @@ pub fn compile_declarative_macro(
                     valid &= check_lhs_nt_follows(&sess.parse_sess, def, &tt);
                     return tt;
                 }
-                sess.diagnostic().span_bug(def.span, "wrong-structured lhs")
+                sess.dcx().span_bug(def.span, "wrong-structured lhs")
             })
             .collect::<Vec<mbe::TokenTree>>(),
-        _ => sess.diagnostic().span_bug(def.span, "wrong-structured lhs"),
+        _ => sess.dcx().span_bug(def.span, "wrong-structured lhs"),
     };
 
     let rhses = match &argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
@@ -533,10 +540,10 @@ pub fn compile_declarative_macro(
                     .pop()
                     .unwrap();
                 }
-                sess.diagnostic().span_bug(def.span, "wrong-structured rhs")
+                sess.dcx().span_bug(def.span, "wrong-structured rhs")
             })
             .collect::<Vec<mbe::TokenTree>>(),
-        _ => sess.diagnostic().span_bug(def.span, "wrong-structured rhs"),
+        _ => sess.dcx().span_bug(def.span, "wrong-structured rhs"),
     };
 
     for rhs in &rhses {
@@ -553,10 +560,10 @@ pub fn compile_declarative_macro(
     let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
     match transparency_error {
         Some(TransparencyError::UnknownTransparency(value, span)) => {
-            diag.span_err(span, format!("unknown macro transparency: `{value}`"));
+            dcx.span_err(span, format!("unknown macro transparency: `{value}`"));
         }
         Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => {
-            diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes");
+            dcx.span_err(vec![old_span, new_span], "multiple macro transparency attributes");
         }
         None => {}
     }
@@ -592,7 +599,7 @@ pub fn compile_declarative_macro(
                     mbe::TokenTree::Delimited(.., delimited) => {
                         mbe::macro_parser::compute_locs(&delimited.tts)
                     }
-                    _ => sess.diagnostic().span_bug(def.span, "malformed macro lhs"),
+                    _ => sess.dcx().span_bug(def.span, "malformed macro lhs"),
                 }
             })
             .collect()
@@ -619,7 +626,7 @@ fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree)
         check_matcher(sess, def, &delimited.tts)
     } else {
         let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
-        sess.span_diagnostic.span_err(lhs.span(), msg);
+        sess.dcx.span_err(lhs.span(), msg);
         false
     }
     // we don't abort on errors on rejection, the driver will do that for us
@@ -645,8 +652,7 @@ fn is_empty_token_tree(sess: &ParseSess, seq: &mbe::SequenceRepetition) -> bool
                         iter.next();
                     }
                     let span = t.span.to(now.span);
-                    sess.span_diagnostic
-                        .span_note(span, "doc comments are ignored in matcher position");
+                    sess.dcx.span_note(span, "doc comments are ignored in matcher position");
                 }
                 mbe::TokenTree::Sequence(_, sub_seq)
                     if (sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
@@ -676,7 +682,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
             TokenTree::Sequence(span, seq) => {
                 if is_empty_token_tree(sess, seq) {
                     let sp = span.entire();
-                    sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
+                    sess.dcx.span_err(sp, "repetition matches empty token tree");
                     return false;
                 }
                 if !check_lhs_no_empty_seq(sess, &seq.tts) {
@@ -693,7 +699,7 @@ fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool {
     match *rhs {
         mbe::TokenTree::Delimited(..) => return true,
         _ => {
-            sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited");
+            sess.dcx.span_err(rhs.span(), "macro rhs must be delimited");
         }
     }
     false
@@ -702,9 +708,9 @@ fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool {
 fn check_matcher(sess: &ParseSess, def: &ast::Item, matcher: &[mbe::TokenTree]) -> bool {
     let first_sets = FirstSets::new(matcher);
     let empty_suffix = TokenSet::empty();
-    let err = sess.span_diagnostic.err_count();
+    let err = sess.dcx.err_count();
     check_matcher_core(sess, def, &first_sets, matcher, &empty_suffix);
-    err == sess.span_diagnostic.err_count()
+    err == sess.dcx.err_count()
 }
 
 fn has_compile_error_macro(rhs: &mbe::TokenTree) -> bool {
@@ -1183,7 +1189,7 @@ fn check_matcher_core<'tt>(
                             };
 
                             let sp = next_token.span();
-                            let mut err = sess.span_diagnostic.struct_span_err(
+                            let mut err = sess.dcx.struct_span_err(
                                 sp,
                                 format!(
                                     "`${name}:{frag}` {may_be} followed by `{next}`, which \
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index 4b8c6feb93e..e3dc73d0d85 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -36,7 +36,7 @@ impl MetaVarExpr {
         let ident = parse_ident(&mut tts, sess, outer_span)?;
         let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = tts.next() else {
             let msg = "meta-variable expression parameter must be wrapped in parentheses";
-            return Err(sess.span_diagnostic.struct_span_err(ident.span, msg));
+            return Err(sess.dcx.struct_span_err(ident.span, msg));
         };
         check_trailing_token(&mut tts, sess)?;
         let mut iter = args.trees();
@@ -50,7 +50,7 @@ impl MetaVarExpr {
             "length" => MetaVarExpr::Length(parse_depth(&mut iter, sess, ident.span)?),
             _ => {
                 let err_msg = "unrecognized meta-variable expression";
-                let mut err = sess.span_diagnostic.struct_span_err(ident.span, err_msg);
+                let mut err = sess.dcx.struct_span_err(ident.span, err_msg);
                 err.span_suggestion(
                     ident.span,
                     "supported expressions are count, ignore, index and length",
@@ -79,7 +79,7 @@ fn check_trailing_token<'sess>(
 ) -> PResult<'sess, ()> {
     if let Some(tt) = iter.next() {
         let mut diag = sess
-            .span_diagnostic
+            .dcx
             .struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt)));
         diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens");
         Err(diag)
@@ -98,7 +98,7 @@ fn parse_count<'sess>(
     let ident = parse_ident(iter, sess, span)?;
     let depth = if try_eat_comma(iter) {
         if iter.look_ahead(0).is_none() {
-            return Err(sess.span_diagnostic.struct_span_err(
+            return Err(sess.dcx.struct_span_err(
                 span,
                 "`count` followed by a comma must have an associated index indicating its depth",
             ));
@@ -119,7 +119,7 @@ fn parse_depth<'sess>(
     let Some(tt) = iter.next() else { return Ok(0) };
     let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. }, _) = tt else {
         return Err(sess
-            .span_diagnostic
+            .dcx
             .struct_span_err(span, "meta-variable expression depth must be a literal"));
     };
     if let Ok(lit_kind) = LitKind::from_token_lit(*lit)
@@ -129,7 +129,7 @@ fn parse_depth<'sess>(
         Ok(n_usize)
     } else {
         let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
-        Err(sess.span_diagnostic.struct_span_err(span, msg))
+        Err(sess.dcx.struct_span_err(span, msg))
     }
 }
 
@@ -146,9 +146,8 @@ fn parse_ident<'sess>(
             return Ok(elem);
         }
         let token_str = pprust::token_to_string(token);
-        let mut err = sess
-            .span_diagnostic
-            .struct_span_err(span, format!("expected identifier, found `{}`", &token_str));
+        let mut err =
+            sess.dcx.struct_span_err(span, format!("expected identifier, found `{}`", &token_str));
         err.span_suggestion(
             token.span,
             format!("try removing `{}`", &token_str),
@@ -157,7 +156,7 @@ fn parse_ident<'sess>(
         );
         return Err(err);
     }
-    Err(sess.span_diagnostic.struct_span_err(span, "expected identifier"))
+    Err(sess.dcx.struct_span_err(span, "expected identifier"))
 }
 
 /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
@@ -181,7 +180,7 @@ fn eat_dollar<'sess>(
         let _ = iter.next();
         return Ok(());
     }
-    Err(sess.span_diagnostic.struct_span_err(
+    Err(sess.dcx.struct_span_err(
         span,
         "meta-variables within meta-variable expressions must be referenced using a dollar sign",
     ))
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index ab9fb20b364..445be01bc97 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -84,7 +84,7 @@ pub(super) fn parse(
                                                     "invalid fragment specifier `{}`",
                                                     frag.name
                                                 );
-                                                sess.span_diagnostic
+                                                sess.dcx
                                                     .struct_span_err(span, msg)
                                                     .help(VALID_FRAGMENT_NAMES_MSG)
                                                     .emit();
@@ -195,7 +195,7 @@ fn parse_tree<'a>(
                             _ => {
                                 let tok = pprust::token_kind_to_string(&token::OpenDelim(delim));
                                 let msg = format!("expected `(` or `{{`, found `{tok}`");
-                                sess.span_diagnostic.span_err(delim_span.entire(), msg);
+                                sess.dcx.span_err(delim_span.entire(), msg);
                             }
                         }
                     }
@@ -244,7 +244,7 @@ fn parse_tree<'a>(
                 Some(tokenstream::TokenTree::Token(token, _)) => {
                     let msg =
                         format!("expected identifier, found `{}`", pprust::token_to_string(token),);
-                    sess.span_diagnostic.span_err(token.span, msg);
+                    sess.dcx.span_err(token.span, msg);
                     TokenTree::MetaVar(token.span, Ident::empty())
                 }
 
@@ -325,7 +325,7 @@ fn parse_sep_and_kleene_op<'a>(
             // #2 is the `?` Kleene op, which does not take a separator (error)
             Ok(Ok((KleeneOp::ZeroOrOne, span))) => {
                 // Error!
-                sess.span_diagnostic.span_err(
+                sess.dcx.span_err(
                     token.span,
                     "the `?` macro repetition operator does not take a separator",
                 );
@@ -346,7 +346,7 @@ fn parse_sep_and_kleene_op<'a>(
     };
 
     // If we ever get to this point, we have experienced an "unexpected token" error
-    sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`");
+    sess.dcx.span_err(span, "expected one of: `*`, `+`, or `?`");
 
     // Return a dummy
     (None, KleeneToken::new(KleeneOp::ZeroOrMore, span))
@@ -356,9 +356,8 @@ fn parse_sep_and_kleene_op<'a>(
 //
 // For example, `macro_rules! foo { ( ${length()} ) => {} }`
 fn span_dollar_dollar_or_metavar_in_the_lhs_err(sess: &ParseSess, token: &Token) {
-    sess.span_diagnostic
-        .span_err(token.span, format!("unexpected token: {}", pprust::token_to_string(token)));
-    sess.span_diagnostic.span_note(
+    sess.dcx.span_err(token.span, format!("unexpected token: {}", pprust::token_to_string(token)));
+    sess.dcx.span_note(
         token.span,
         "`$$` and meta-variable expressions are not allowed inside macro parameter definitions",
     );
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index d08026b9c14..429bfa61450 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -156,7 +156,7 @@ impl MultiItemModifier for DeriveProcMacro {
             }
         };
 
-        let error_count_before = ecx.sess.diagnostic().err_count();
+        let error_count_before = ecx.sess.dcx().err_count();
         let mut parser =
             rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
         let mut items = vec![];
@@ -179,7 +179,7 @@ impl MultiItemModifier for DeriveProcMacro {
         }
 
         // fail if there have been errors emitted
-        if ecx.sess.diagnostic().err_count() > error_count_before {
+        if ecx.sess.dcx().err_count() > error_count_before {
             ecx.sess.emit_err(errors::ProcMacroDeriveTokens { span });
         }
 
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index d2c26668ea8..c412b064ce9 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -426,6 +426,10 @@ impl server::Types for Rustc<'_, '_> {
 }
 
 impl server::FreeFunctions for Rustc<'_, '_> {
+    fn injected_env_var(&mut self, var: &str) -> Option<String> {
+        self.ecx.sess.opts.logical_env.get(var).cloned()
+    }
+
     fn track_env_var(&mut self, var: &str, value: Option<&str>) {
         self.sess()
             .env_depinfo
@@ -502,7 +506,7 @@ impl server::FreeFunctions for Rustc<'_, '_> {
                 None,
             );
         }
-        self.sess().span_diagnostic.emit_diagnostic(diag);
+        self.sess().dcx.emit_diagnostic(diag);
     }
 }
 
diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs
index b4724b0e9d0..0b859841828 100644
--- a/compiler/rustc_expand/src/tests.rs
+++ b/compiler/rustc_expand/src/tests.rs
@@ -8,7 +8,7 @@ use rustc_span::{BytePos, Span};
 
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Handler, MultiSpan, PResult};
+use rustc_errors::{DiagCtxt, MultiSpan, PResult};
 use termcolor::WriteColor;
 
 use std::io;
@@ -23,7 +23,7 @@ fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> {
     new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str)
 }
 
-fn create_test_handler() -> (Handler, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
+fn create_test_handler() -> (DiagCtxt, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
     let output = Arc::new(Mutex::new(Vec::new()));
     let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
     let fallback_bundle = rustc_errors::fallback_fluent_bundle(
@@ -33,8 +33,8 @@ fn create_test_handler() -> (Handler, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
     let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
         .sm(Some(source_map.clone()))
         .diagnostic_width(Some(140));
-    let handler = Handler::with_emitter(Box::new(emitter));
-    (handler, source_map, output)
+    let dcx = DiagCtxt::with_emitter(Box::new(emitter));
+    (dcx, source_map, output)
 }
 
 /// Returns the result of parsing the given string via the given callback.
@@ -46,7 +46,7 @@ where
 {
     let mut p = string_to_parser(&ps, s);
     let x = f(&mut p).unwrap();
-    p.sess.span_diagnostic.abort_if_errors();
+    p.sess.dcx.abort_if_errors();
     x
 }
 
@@ -57,7 +57,7 @@ where
     F: for<'a> FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
 {
     let (handler, source_map, output) = create_test_handler();
-    let ps = ParseSess::with_span_handler(handler, source_map);
+    let ps = ParseSess::with_dcx(handler, source_map);
     let mut p = string_to_parser(&ps, source_str.to_string());
     let result = f(&mut p);
     assert!(result.is_ok());
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 01508375b1a..760945554f0 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -435,8 +435,6 @@ pub enum TraitBoundModifier {
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
 pub enum GenericBound<'hir> {
     Trait(PolyTraitRef<'hir>, TraitBoundModifier),
-    // FIXME(davidtwco): Introduce `PolyTraitRef::LangItem`
-    LangItemTrait(LangItem, Span, HirId, &'hir GenericArgs<'hir>),
     Outlives(&'hir Lifetime),
 }
 
@@ -451,7 +449,6 @@ impl GenericBound<'_> {
     pub fn span(&self) -> Span {
         match self {
             GenericBound::Trait(t, ..) => t.span,
-            GenericBound::LangItemTrait(_, span, ..) => *span,
             GenericBound::Outlives(l) => l.ident.span,
         }
     }
@@ -1560,7 +1557,7 @@ impl Expr<'_> {
             ExprKind::Call(..) => ExprPrecedence::Call,
             ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
             ExprKind::Tup(_) => ExprPrecedence::Tup,
-            ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node.into()),
+            ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node),
             ExprKind::Unary(..) => ExprPrecedence::Unary,
             ExprKind::Lit(_) => ExprPrecedence::Lit,
             ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast,
@@ -1700,11 +1697,9 @@ impl Expr<'_> {
                 // them being used only for its side-effects.
                 base.can_have_side_effects()
             }
-            ExprKind::Struct(_, fields, init) => fields
-                .iter()
-                .map(|field| field.expr)
-                .chain(init.into_iter())
-                .any(|e| e.can_have_side_effects()),
+            ExprKind::Struct(_, fields, init) => {
+                fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects())
+            }
 
             ExprKind::Array(args)
             | ExprKind::Tup(args)
@@ -2815,7 +2810,7 @@ impl TraitRef<'_> {
         match self.path.res {
             Res::Def(DefKind::Trait | DefKind::TraitAlias, did) => Some(did),
             Res::Err => None,
-            _ => unreachable!(),
+            res => panic!("{res:?} did not resolve to a trait or trait alias"),
         }
     }
 }
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 9cf1db166a5..67e058a3219 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -1075,10 +1075,6 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericB
         GenericBound::Trait(ref typ, _modifier) => {
             visitor.visit_poly_trait_ref(typ);
         }
-        GenericBound::LangItemTrait(_, _span, hir_id, args) => {
-            visitor.visit_id(hir_id);
-            visitor.visit_generic_args(args);
-        }
         GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
     }
 }
diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs
index 0d65ddb5642..8948a03e4a6 100644
--- a/compiler/rustc_hir/src/target.rs
+++ b/compiler/rustc_hir/src/target.rs
@@ -67,6 +67,42 @@ impl Display for Target {
 }
 
 impl Target {
+    pub fn is_associated_item(self) -> bool {
+        match self {
+            Target::AssocConst | Target::AssocTy | Target::Method(_) => true,
+            Target::ExternCrate
+            | Target::Use
+            | Target::Static
+            | Target::Const
+            | Target::Fn
+            | Target::Closure
+            | Target::Mod
+            | Target::ForeignMod
+            | Target::GlobalAsm
+            | Target::TyAlias
+            | Target::OpaqueTy
+            | Target::Enum
+            | Target::Variant
+            | Target::Struct
+            | Target::Field
+            | Target::Union
+            | Target::Trait
+            | Target::TraitAlias
+            | Target::Impl
+            | Target::Expression
+            | Target::Statement
+            | Target::Arm
+            | Target::ForeignFn
+            | Target::ForeignStatic
+            | Target::ForeignTy
+            | Target::GenericParam(_)
+            | Target::MacroDef
+            | Target::Param
+            | Target::PatField
+            | Target::ExprField => false,
+        }
+    }
+
     pub fn from_item(item: &Item<'_>) -> Target {
         match item.kind {
             ItemKind::ExternCrate(..) => Target::ExternCrate,
diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
index dfec3c5e829..0748644cc0a 100644
--- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -134,17 +134,6 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                         only_self_bounds,
                     );
                 }
-                &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
-                    self.instantiate_lang_item_trait_ref(
-                        lang_item,
-                        span,
-                        hir_id,
-                        args,
-                        param_ty,
-                        bounds,
-                        only_self_bounds,
-                    );
-                }
                 hir::GenericBound::Outlives(lifetime) => {
                     let region = self.ast_region_to_region(lifetime, None);
                     bounds.push_region_bound(
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index 47fbed45b91..be73c027fdc 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -661,7 +661,7 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
                 args.args[0].hir_id(),
                 multispan,
                 msg,
-                |lint| lint,
+                |_| {},
             );
         }
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs
index 43d4248ab74..9afb04b7470 100644
--- a/compiler/rustc_hir_analysis/src/astconv/lint.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs
@@ -122,7 +122,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             Applicability::MachineApplicable,
                         );
                         self.maybe_lint_blanket_trait_impl(self_ty, lint);
-                        lint
                     },
                 );
             }
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index caae6fa4f06..6f8e80172dd 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -477,7 +477,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             ty::Const::new_misc_error(tcx, ty).into()
                         }
                     }
-                    _ => unreachable!(),
+                    (kind, arg) => span_bug!(
+                        self.span,
+                        "mismatched path argument for kind {kind:?}: found arg {arg:?}"
+                    ),
                 }
             }
 
@@ -675,36 +678,57 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         )
     }
 
-    fn instantiate_poly_trait_ref_inner(
+    /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct
+    /// a full trait reference. The resulting trait reference is returned. This may also generate
+    /// auxiliary bounds, which are added to `bounds`.
+    ///
+    /// Example:
+    ///
+    /// ```ignore (illustrative)
+    /// poly_trait_ref = Iterator<Item = u32>
+    /// self_ty = Foo
+    /// ```
+    ///
+    /// this would return `Foo: Iterator` and add `<Foo as Iterator>::Item = u32` into `bounds`.
+    ///
+    /// **A note on binders:** against our usual convention, there is an implied binder around
+    /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions.
+    /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>`
+    /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be
+    /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly,
+    /// however.
+    #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
+    pub(crate) fn instantiate_poly_trait_ref(
         &self,
-        hir_id: hir::HirId,
+        trait_ref: &hir::TraitRef<'_>,
         span: Span,
-        binding_span: Option<Span>,
         constness: ty::BoundConstness,
         polarity: ty::ImplPolarity,
+        self_ty: Ty<'tcx>,
         bounds: &mut Bounds<'tcx>,
         speculative: bool,
-        trait_ref_span: Span,
-        trait_def_id: DefId,
-        trait_segment: &hir::PathSegment<'_>,
-        args: &GenericArgs<'_>,
-        infer_args: bool,
-        self_ty: Ty<'tcx>,
         only_self_bounds: OnlySelfBounds,
     ) -> GenericArgCountResult {
+        let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
+        let trait_segment = trait_ref.path.segments.last().unwrap();
+        let args = trait_segment.args();
+
+        self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
+        self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
+
         let (generic_args, arg_count) = self.create_args_for_ast_path(
-            trait_ref_span,
+            trait_ref.path.span,
             trait_def_id,
             &[],
             trait_segment,
             args,
-            infer_args,
+            trait_segment.infer_args,
             Some(self_ty),
             constness,
         );
 
         let tcx = self.tcx();
-        let bound_vars = tcx.late_bound_vars(hir_id);
+        let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id);
         debug!(?bound_vars);
 
         let assoc_bindings = self.create_assoc_bindings_for_generic_args(args);
@@ -732,13 +756,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
             // Specify type to assert that error was already reported in `Err` case.
             let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding(
-                hir_id,
+                trait_ref.hir_ref_id,
                 poly_trait_ref,
                 binding,
                 bounds,
                 speculative,
                 &mut dup_bindings,
-                binding_span.unwrap_or(binding.span),
+                binding.span,
                 constness,
                 only_self_bounds,
                 polarity,
@@ -749,102 +773,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         arg_count
     }
 
-    /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct
-    /// a full trait reference. The resulting trait reference is returned. This may also generate
-    /// auxiliary bounds, which are added to `bounds`.
-    ///
-    /// Example:
-    ///
-    /// ```ignore (illustrative)
-    /// poly_trait_ref = Iterator<Item = u32>
-    /// self_ty = Foo
-    /// ```
-    ///
-    /// this would return `Foo: Iterator` and add `<Foo as Iterator>::Item = u32` into `bounds`.
-    ///
-    /// **A note on binders:** against our usual convention, there is an implied bounder around
-    /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions.
-    /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>`
-    /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be
-    /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly,
-    /// however.
-    #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
-    pub(crate) fn instantiate_poly_trait_ref(
-        &self,
-        trait_ref: &hir::TraitRef<'_>,
-        span: Span,
-        constness: ty::BoundConstness,
-        polarity: ty::ImplPolarity,
-        self_ty: Ty<'tcx>,
-        bounds: &mut Bounds<'tcx>,
-        speculative: bool,
-        only_self_bounds: OnlySelfBounds,
-    ) -> GenericArgCountResult {
-        let hir_id = trait_ref.hir_ref_id;
-        let binding_span = None;
-        let trait_ref_span = trait_ref.path.span;
-        let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
-        let trait_segment = trait_ref.path.segments.last().unwrap();
-        let args = trait_segment.args();
-        let infer_args = trait_segment.infer_args;
-
-        self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
-        self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
-
-        self.instantiate_poly_trait_ref_inner(
-            hir_id,
-            span,
-            binding_span,
-            constness,
-            polarity,
-            bounds,
-            speculative,
-            trait_ref_span,
-            trait_def_id,
-            trait_segment,
-            args,
-            infer_args,
-            self_ty,
-            only_self_bounds,
-        )
-    }
-
-    pub(crate) fn instantiate_lang_item_trait_ref(
-        &self,
-        lang_item: hir::LangItem,
-        span: Span,
-        hir_id: hir::HirId,
-        args: &GenericArgs<'_>,
-        self_ty: Ty<'tcx>,
-        bounds: &mut Bounds<'tcx>,
-        only_self_bounds: OnlySelfBounds,
-    ) {
-        let binding_span = Some(span);
-        let constness = ty::BoundConstness::NotConst;
-        let speculative = false;
-        let trait_ref_span = span;
-        let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span));
-        let trait_segment = &hir::PathSegment::invalid();
-        let infer_args = false;
-
-        self.instantiate_poly_trait_ref_inner(
-            hir_id,
-            span,
-            binding_span,
-            constness,
-            ty::ImplPolarity::Positive,
-            bounds,
-            speculative,
-            trait_ref_span,
-            trait_def_id,
-            trait_segment,
-            args,
-            infer_args,
-            self_ty,
-            only_self_bounds,
-        );
-    }
-
     fn ast_path_to_mono_trait_ref(
         &self,
         span: Span,
@@ -1506,8 +1434,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
                         Applicability::MachineApplicable,
                     );
-
-                    lint
                 },
             );
         }
@@ -1946,7 +1872,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     "s",
                 ),
                 [only] => (only.to_string(), ""),
-                [] => unreachable!(),
+                [] => unreachable!("expected at least one generic to prohibit"),
             };
             let last_span = *arg_spans.last().unwrap();
             let span: MultiSpan = arg_spans.into();
@@ -2555,8 +2481,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             if let Some(i) = (param.index as usize).checked_sub(generics.count() - lifetimes.len())
             {
                 // Resolve our own lifetime parameters.
-                let GenericParamDefKind::Lifetime { .. } = param.kind else { bug!() };
-                let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { bug!() };
+                let GenericParamDefKind::Lifetime { .. } = param.kind else {
+                    span_bug!(
+                        tcx.def_span(param.def_id),
+                        "only expected lifetime for opaque's own generics, got {:?}",
+                        param.kind
+                    );
+                };
+                let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else {
+                    bug!(
+                        "expected lifetime argument for param {param:?}, found {:?}",
+                        &lifetimes[i]
+                    )
+                };
                 self.ast_region_to_region(lifetime, None).into()
             } else {
                 tcx.mk_param_from_def(param)
diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
index ce5426b5142..dd5deb6f244 100644
--- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
@@ -73,7 +73,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 | ty::ClauseKind::ConstArgHasType(..)
                 | ty::ClauseKind::WellFormed(_)
                 | ty::ClauseKind::ConstEvaluatable(_) => {
-                    bug!()
+                    span_bug!(span, "did not expect {pred} clause in object bounds");
                 }
             }
         }
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index e6245e4d0b1..8413a1cc0db 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -51,7 +51,7 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
                 hir_id,
                 span,
                 "use of calling convention not supported on this target",
-                |lint| lint,
+                |_| {},
             );
         }
     }
@@ -190,7 +190,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             "static of uninhabited type",
             |lint| {
                 lint
-                .note("uninhabited statics cannot be initialized, and any access would be an immediate error")
+                .note("uninhabited statics cannot be initialized, and any access would be an immediate error");
             },
         );
     }
@@ -1093,7 +1093,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
                             "this {descr} contains `{field_ty}`, which {note}, \
                                 and makes it not a breaking change to become \
                                 non-zero-sized in the future."
-                        ))
+                        ));
                     },
                 )
             } else {
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 00302ec831c..264868fdfc7 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -2,9 +2,7 @@ use super::potentially_plural_count;
 use crate::errors::LifetimesOrBoundsMismatchOnTrait;
 use hir::def_id::{DefId, LocalDefId};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_errors::{
-    pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan,
-};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit;
@@ -50,13 +48,7 @@ pub(super) fn compare_impl_method<'tcx>(
 
     let _: Result<_, ErrorGuaranteed> = try {
         check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
-        compare_method_predicate_entailment(
-            tcx,
-            impl_m,
-            trait_m,
-            impl_trait_ref,
-            CheckImpliedWfMode::Check,
-        )?;
+        compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
         refine::check_refining_return_position_impl_trait_in_trait(
             tcx,
             impl_m,
@@ -170,7 +162,6 @@ fn compare_method_predicate_entailment<'tcx>(
     impl_m: ty::AssocItem,
     trait_m: ty::AssocItem,
     impl_trait_ref: ty::TraitRef<'tcx>,
-    check_implied_wf: CheckImpliedWfMode,
 ) -> Result<(), ErrorGuaranteed> {
     let trait_to_impl_args = impl_trait_ref.args;
 
@@ -317,7 +308,7 @@ fn compare_method_predicate_entailment<'tcx>(
         return Err(emitted);
     }
 
-    if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() {
+    if !(impl_sig, trait_sig).references_error() {
         // Select obligations to make progress on inference before processing
         // the wf obligation below.
         // FIXME(-Znext-solver): Not needed when the hack below is removed.
@@ -333,8 +324,9 @@ fn compare_method_predicate_entailment<'tcx>(
         // trigger the lint. Instead, let's only consider type outlives and
         // region outlives obligations.
         //
-        // FIXME(-Znext-solver): Try removing this hack again once
-        // the new solver is stable.
+        // FIXME(-Znext-solver): Try removing this hack again once the new
+        // solver is stable. We should just be able to register a WF pred for
+        // the fn sig.
         let mut wf_args: smallvec::SmallVec<[_; 4]> =
             unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect();
         // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?))
@@ -357,7 +349,7 @@ fn compare_method_predicate_entailment<'tcx>(
                     // We need to register Projection oblgiations too, because we may end up with
                     // an implied `X::Item: 'a`, which gets desugared into `X::Item = ?0`, `?0: 'a`.
                     // If we only register the region outlives obligation, this leads to an unconstrained var.
-                    // See `implied_bounds_entailment_alias_var` test.
+                    // See `implied_bounds_entailment_alias_var.rs` test.
                     ty::PredicateKind::Clause(
                         ty::ClauseKind::RegionOutlives(..)
                         | ty::ClauseKind::TypeOutlives(..)
@@ -378,26 +370,8 @@ fn compare_method_predicate_entailment<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        match check_implied_wf {
-            CheckImpliedWfMode::Check => {
-                let impl_m_hir_id = tcx.local_def_id_to_hir_id(impl_m_def_id);
-                return compare_method_predicate_entailment(
-                    tcx,
-                    impl_m,
-                    trait_m,
-                    impl_trait_ref,
-                    CheckImpliedWfMode::Skip,
-                )
-                .map(|()| {
-                    // If the skip-mode was successful, emit a lint.
-                    emit_implied_wf_lint(infcx.tcx, impl_m, impl_m_hir_id, vec![]);
-                });
-            }
-            CheckImpliedWfMode::Skip => {
-                let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
-                return Err(reported);
-            }
-        }
+        let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
+        return Err(reported);
     }
 
     // Finally, resolve all regions. This catches wily misuses of
@@ -408,119 +382,14 @@ fn compare_method_predicate_entailment<'tcx>(
     );
     let errors = infcx.resolve_regions(&outlives_env);
     if !errors.is_empty() {
-        // FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
-        // becomes a hard error (i.e. ideally we'd just call `resolve_regions_and_report_errors`
-        let impl_m_hir_id = tcx.local_def_id_to_hir_id(impl_m_def_id);
-        match check_implied_wf {
-            CheckImpliedWfMode::Check => {
-                return compare_method_predicate_entailment(
-                    tcx,
-                    impl_m,
-                    trait_m,
-                    impl_trait_ref,
-                    CheckImpliedWfMode::Skip,
-                )
-                .map(|()| {
-                    let bad_args = extract_bad_args_for_implies_lint(
-                        tcx,
-                        &errors,
-                        (trait_m, trait_sig),
-                        // Unnormalized impl sig corresponds to the HIR types written
-                        (impl_m, unnormalized_impl_sig),
-                        impl_m_hir_id,
-                    );
-                    // If the skip-mode was successful, emit a lint.
-                    emit_implied_wf_lint(tcx, impl_m, impl_m_hir_id, bad_args);
-                });
-            }
-            CheckImpliedWfMode::Skip => {
-                if infcx.tainted_by_errors().is_none() {
-                    infcx.err_ctxt().report_region_errors(impl_m_def_id, &errors);
-                }
-                return Err(tcx
-                    .sess
-                    .span_delayed_bug(rustc_span::DUMMY_SP, "error should have been emitted"));
-            }
-        }
+        return Err(infcx
+            .tainted_by_errors()
+            .unwrap_or_else(|| infcx.err_ctxt().report_region_errors(impl_m_def_id, &errors)));
     }
 
     Ok(())
 }
 
-fn extract_bad_args_for_implies_lint<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    errors: &[infer::RegionResolutionError<'tcx>],
-    (trait_m, trait_sig): (ty::AssocItem, ty::FnSig<'tcx>),
-    (impl_m, impl_sig): (ty::AssocItem, ty::FnSig<'tcx>),
-    hir_id: hir::HirId,
-) -> Vec<(Span, Option<String>)> {
-    let mut blame_generics = vec![];
-    for error in errors {
-        // Look for the subregion origin that contains an input/output type
-        let origin = match error {
-            infer::RegionResolutionError::ConcreteFailure(o, ..) => o,
-            infer::RegionResolutionError::GenericBoundFailure(o, ..) => o,
-            infer::RegionResolutionError::SubSupConflict(_, _, o, ..) => o,
-            infer::RegionResolutionError::UpperBoundUniverseConflict(.., o, _) => o,
-        };
-        // Extract (possible) input/output types from origin
-        match origin {
-            infer::SubregionOrigin::Subtype(trace) => {
-                if let Some((a, b)) = trace.values.ty() {
-                    blame_generics.extend([a, b]);
-                }
-            }
-            infer::SubregionOrigin::RelateParamBound(_, ty, _) => blame_generics.push(*ty),
-            infer::SubregionOrigin::ReferenceOutlivesReferent(ty, _) => blame_generics.push(*ty),
-            _ => {}
-        }
-    }
-
-    let fn_decl = tcx.hir().fn_decl_by_hir_id(hir_id).unwrap();
-    let opt_ret_ty = match fn_decl.output {
-        hir::FnRetTy::DefaultReturn(_) => None,
-        hir::FnRetTy::Return(ty) => Some(ty),
-    };
-
-    // Map late-bound regions from trait to impl, so the names are right.
-    let mapping = std::iter::zip(
-        tcx.fn_sig(trait_m.def_id).skip_binder().bound_vars(),
-        tcx.fn_sig(impl_m.def_id).skip_binder().bound_vars(),
-    )
-    .filter_map(|(impl_bv, trait_bv)| {
-        if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
-            && let ty::BoundVariableKind::Region(trait_bv) = trait_bv
-        {
-            Some((impl_bv, trait_bv))
-        } else {
-            None
-        }
-    })
-    .collect();
-
-    // For each arg, see if it was in the "blame" of any of the region errors.
-    // If so, then try to produce a suggestion to replace the argument type with
-    // one from the trait.
-    let mut bad_args = vec![];
-    for (idx, (ty, hir_ty)) in
-        std::iter::zip(impl_sig.inputs_and_output, fn_decl.inputs.iter().chain(opt_ret_ty))
-            .enumerate()
-    {
-        let expected_ty = trait_sig.inputs_and_output[idx]
-            .fold_with(&mut RemapLateBound { tcx, mapping: &mapping });
-        if blame_generics.iter().any(|blame| ty.contains(*blame)) {
-            let expected_ty_sugg = expected_ty.to_string();
-            bad_args.push((
-                hir_ty.span,
-                // Only suggest something if it actually changed.
-                (expected_ty_sugg != ty.to_string()).then_some(expected_ty_sugg),
-            ));
-        }
-    }
-
-    bad_args
-}
-
 struct RemapLateBound<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     mapping: &'a FxHashMap<ty::BoundRegionKind, ty::BoundRegionKind>,
@@ -544,53 +413,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> {
     }
 }
 
-fn emit_implied_wf_lint<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    impl_m: ty::AssocItem,
-    hir_id: hir::HirId,
-    bad_args: Vec<(Span, Option<String>)>,
-) {
-    let span: MultiSpan = if bad_args.is_empty() {
-        tcx.def_span(impl_m.def_id).into()
-    } else {
-        bad_args.iter().map(|(span, _)| *span).collect::<Vec<_>>().into()
-    };
-    tcx.struct_span_lint_hir(
-        rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
-        hir_id,
-        span,
-        "impl method assumes more implied bounds than the corresponding trait method",
-        |lint| {
-            let bad_args: Vec<_> =
-                bad_args.into_iter().filter_map(|(span, sugg)| Some((span, sugg?))).collect();
-            if !bad_args.is_empty() {
-                lint.multipart_suggestion(
-                    format!(
-                        "replace {} type{} to make the impl signature compatible",
-                        pluralize!("this", bad_args.len()),
-                        pluralize!(bad_args.len())
-                    ),
-                    bad_args,
-                    Applicability::MaybeIncorrect,
-                );
-            }
-            lint
-        },
-    );
-}
-
-#[derive(Debug, PartialEq, Eq)]
-enum CheckImpliedWfMode {
-    /// Checks implied well-formedness of the impl method. If it fails, we will
-    /// re-check with `Skip`, and emit a lint if it succeeds.
-    Check,
-    /// Skips checking implied well-formedness of the impl method, but will emit
-    /// a lint if the `compare_method_predicate_entailment` succeeded. This means that
-    /// the reason that we had failed earlier during `Check` was due to the impl
-    /// having stronger requirements than the trait.
-    Skip,
-}
-
 fn compare_asyncness<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_m: ty::AssocItem,
@@ -1086,8 +908,14 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
             _ => return Ok(region),
         }
 
-        let e = if let Some(region) = self.map.get(&region) {
-            if let ty::ReEarlyParam(e) = region.kind() { e } else { bug!() }
+        let e = if let Some(id_region) = self.map.get(&region) {
+            if let ty::ReEarlyParam(e) = id_region.kind() {
+                e
+            } else {
+                bug!(
+                    "expected to map region {region} to early-bound identity region, but got {id_region}"
+                );
+            }
         } else {
             let guar = match region.kind() {
                 ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
@@ -1710,92 +1538,87 @@ fn compare_synthetic_generics<'tcx>(
                 trait_m.name
             );
             err.span_label(trait_span, "declaration in trait here");
-            match (impl_synthetic, trait_synthetic) {
+            if impl_synthetic {
                 // The case where the impl method uses `impl Trait` but the trait method uses
                 // explicit generics
-                (true, false) => {
-                    err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
-                    let _: Option<_> = try {
-                        // try taking the name from the trait impl
-                        // FIXME: this is obviously suboptimal since the name can already be used
-                        // as another generic argument
-                        let new_name = tcx.opt_item_name(trait_def_id)?;
-                        let trait_m = trait_m.def_id.as_local()?;
-                        let trait_m = tcx.hir().expect_trait_item(trait_m);
-
-                        let impl_m = impl_m.def_id.as_local()?;
-                        let impl_m = tcx.hir().expect_impl_item(impl_m);
-
-                        // in case there are no generics, take the spot between the function name
-                        // and the opening paren of the argument list
-                        let new_generics_span = tcx.def_ident_span(impl_def_id)?.shrink_to_hi();
-                        // in case there are generics, just replace them
-                        let generics_span =
-                            impl_m.generics.span.substitute_dummy(new_generics_span);
-                        // replace with the generics from the trait
-                        let new_generics =
-                            tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?;
-
-                        err.multipart_suggestion(
-                            "try changing the `impl Trait` argument to a generic parameter",
-                            vec![
-                                // replace `impl Trait` with `T`
-                                (impl_span, new_name.to_string()),
-                                // replace impl method generics with trait method generics
-                                // This isn't quite right, as users might have changed the names
-                                // of the generics, but it works for the common case
-                                (generics_span, new_generics),
-                            ],
-                            Applicability::MaybeIncorrect,
-                        );
-                    };
-                }
+                err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
+                let _: Option<_> = try {
+                    // try taking the name from the trait impl
+                    // FIXME: this is obviously suboptimal since the name can already be used
+                    // as another generic argument
+                    let new_name = tcx.opt_item_name(trait_def_id)?;
+                    let trait_m = trait_m.def_id.as_local()?;
+                    let trait_m = tcx.hir().expect_trait_item(trait_m);
+
+                    let impl_m = impl_m.def_id.as_local()?;
+                    let impl_m = tcx.hir().expect_impl_item(impl_m);
+
+                    // in case there are no generics, take the spot between the function name
+                    // and the opening paren of the argument list
+                    let new_generics_span = tcx.def_ident_span(impl_def_id)?.shrink_to_hi();
+                    // in case there are generics, just replace them
+                    let generics_span = impl_m.generics.span.substitute_dummy(new_generics_span);
+                    // replace with the generics from the trait
+                    let new_generics =
+                        tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?;
+
+                    err.multipart_suggestion(
+                        "try changing the `impl Trait` argument to a generic parameter",
+                        vec![
+                            // replace `impl Trait` with `T`
+                            (impl_span, new_name.to_string()),
+                            // replace impl method generics with trait method generics
+                            // This isn't quite right, as users might have changed the names
+                            // of the generics, but it works for the common case
+                            (generics_span, new_generics),
+                        ],
+                        Applicability::MaybeIncorrect,
+                    );
+                };
+            } else {
                 // The case where the trait method uses `impl Trait`, but the impl method uses
                 // explicit generics.
-                (false, true) => {
-                    err.span_label(impl_span, "expected `impl Trait`, found generic parameter");
-                    let _: Option<_> = try {
-                        let impl_m = impl_m.def_id.as_local()?;
-                        let impl_m = tcx.hir().expect_impl_item(impl_m);
-                        let (sig, _) = impl_m.expect_fn();
-                        let input_tys = sig.decl.inputs;
-
-                        struct Visitor(Option<Span>, hir::def_id::LocalDefId);
-                        impl<'v> intravisit::Visitor<'v> for Visitor {
-                            fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
-                                intravisit::walk_ty(self, ty);
-                                if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
-                                    && let Res::Def(DefKind::TyParam, def_id) = path.res
-                                    && def_id == self.1.to_def_id()
-                                {
-                                    self.0 = Some(ty.span);
-                                }
+                err.span_label(impl_span, "expected `impl Trait`, found generic parameter");
+                let _: Option<_> = try {
+                    let impl_m = impl_m.def_id.as_local()?;
+                    let impl_m = tcx.hir().expect_impl_item(impl_m);
+                    let (sig, _) = impl_m.expect_fn();
+                    let input_tys = sig.decl.inputs;
+
+                    struct Visitor(Option<Span>, hir::def_id::LocalDefId);
+                    impl<'v> intravisit::Visitor<'v> for Visitor {
+                        fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+                            intravisit::walk_ty(self, ty);
+                            if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
+                                && let Res::Def(DefKind::TyParam, def_id) = path.res
+                                && def_id == self.1.to_def_id()
+                            {
+                                self.0 = Some(ty.span);
                             }
                         }
+                    }
 
-                        let mut visitor = Visitor(None, impl_def_id);
-                        for ty in input_tys {
-                            intravisit::Visitor::visit_ty(&mut visitor, ty);
-                        }
-                        let span = visitor.0?;
-
-                        let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
-                        let bounds = bounds.first()?.span().to(bounds.last()?.span());
-                        let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
-
-                        err.multipart_suggestion(
-                            "try removing the generic parameter and using `impl Trait` instead",
-                            vec![
-                                // delete generic parameters
-                                (impl_m.generics.span, String::new()),
-                                // replace param usage with `impl Trait`
-                                (span, format!("impl {bounds}")),
-                            ],
-                            Applicability::MaybeIncorrect,
-                        );
-                    };
-                }
-                _ => unreachable!(),
+                    let mut visitor = Visitor(None, impl_def_id);
+                    for ty in input_tys {
+                        intravisit::Visitor::visit_ty(&mut visitor, ty);
+                    }
+                    let span = visitor.0?;
+
+                    let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
+                    let bounds = bounds.first()?.span().to(bounds.last()?.span());
+                    let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
+
+                    err.multipart_suggestion(
+                        "try removing the generic parameter and using `impl Trait` instead",
+                        vec![
+                            // delete generic parameters
+                            (impl_m.generics.span, String::new()),
+                            // replace param usage with `impl Trait`
+                            (span, format!("impl {bounds}")),
+                        ],
+                        Applicability::MaybeIncorrect,
+                    );
+                };
             }
             error_found = Some(err.emit_unless(delay));
         }
@@ -1859,7 +1682,9 @@ fn compare_generic_param_kinds<'tcx>(
             // this is exhaustive so that anyone adding new generic param kinds knows
             // to make sure this error is reported for them.
             (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false,
-            (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(),
+            (Lifetime { .. }, _) | (_, Lifetime { .. }) => {
+                bug!("lifetime params are expected to be filtered by `ty_const_params_of`")
+            }
         } {
             let param_impl_span = tcx.def_span(param_impl.def_id);
             let param_trait_span = tcx.def_span(param_trait.def_id);
@@ -1883,7 +1708,10 @@ fn compare_generic_param_kinds<'tcx>(
                     )
                 }
                 Type { .. } => format!("{prefix} type parameter"),
-                Lifetime { .. } => unreachable!(),
+                Lifetime { .. } => span_bug!(
+                    tcx.def_span(param.def_id),
+                    "lifetime params are expected to be filtered by `ty_const_params_of`"
+                ),
             };
 
             let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap();
@@ -2187,7 +2015,10 @@ pub(super) fn check_type_bounds<'tcx>(
                 ..
             }) => ty.span,
             hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Type(ty), .. }) => ty.span,
-            _ => bug!(),
+            item => span_bug!(
+                tcx.def_span(impl_ty_def_id),
+                "cannot call `check_type_bounds` on item: {item:?}",
+            ),
         }
     };
     let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl_ty_def_id)?;
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index f866543dd0d..67796855ece 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -262,7 +262,10 @@ fn report_mismatched_rpitit_signature<'tcx>(
 
     if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() {
         let ty::Alias(ty::Projection, future_ty) = return_ty.kind() else {
-            bug!();
+            span_bug!(
+                tcx.def_span(trait_m_def_id),
+                "expected return type of async fn in trait to be a AFIT projection"
+            );
         };
         let Some(future_output_ty) = tcx
             .explicit_item_bounds(future_ty.def_id)
@@ -272,7 +275,7 @@ fn report_mismatched_rpitit_signature<'tcx>(
                 _ => None,
             })
         else {
-            bug!()
+            span_bug!(tcx.def_span(trait_m_def_id), "expected `Future` projection bound in AFIT");
         };
         return_ty = future_output_ty;
     }
diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs
index 8f194ae88ab..1d737e17e82 100644
--- a/compiler/rustc_hir_analysis/src/check/entry.rs
+++ b/compiler/rustc_hir_analysis/src/check/entry.rs
@@ -92,24 +92,6 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
 
     let mut error = false;
     let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span);
-    let main_fn_generics = tcx.generics_of(main_def_id);
-    let main_fn_predicates = tcx.predicates_of(main_def_id);
-    if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
-        let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
-        tcx.sess.emit_err(errors::MainFunctionGenericParameters {
-            span: generics_param_span.unwrap_or(main_span),
-            label_span: generics_param_span,
-        });
-        error = true;
-    } else if !main_fn_predicates.predicates.is_empty() {
-        // generics may bring in implicit predicates, so we skip this check if generics is present.
-        let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
-        tcx.sess.emit_err(errors::WhereClauseOnMain {
-            span: generics_where_clauses_span.unwrap_or(main_span),
-            generics_span: generics_where_clauses_span,
-        });
-        error = true;
-    }
 
     let main_asyncness = tcx.asyncness(main_def_id);
     if main_asyncness.is_async() {
@@ -142,10 +124,6 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
     if let Some(term_did) = tcx.lang_items().termination() {
         let return_ty = main_fnsig.output();
         let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
-        if !return_ty.bound_vars().is_empty() {
-            tcx.sess.emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span });
-            error = true;
-        }
         let return_ty = return_ty.skip_binder();
         let infcx = tcx.infer_ctxt().build();
         let cause = traits::ObligationCause::new(
@@ -180,7 +158,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         Abi::Rust,
     ));
 
-    check_function_signature(
+    if check_function_signature(
         tcx,
         ObligationCause::new(
             main_span,
@@ -189,7 +167,28 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         ),
         main_def_id,
         expected_sig,
-    );
+    )
+    .is_err()
+    {
+        return;
+    }
+
+    let main_fn_generics = tcx.generics_of(main_def_id);
+    let main_fn_predicates = tcx.predicates_of(main_def_id);
+    if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
+        let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
+        tcx.sess.emit_err(errors::MainFunctionGenericParameters {
+            span: generics_param_span.unwrap_or(main_span),
+            label_span: generics_param_span,
+        });
+    } else if !main_fn_predicates.predicates.is_empty() {
+        // generics may bring in implicit predicates, so we skip this check if generics is present.
+        let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
+        tcx.sess.emit_err(errors::WhereClauseOnMain {
+            span: generics_where_clauses_span.unwrap_or(main_span),
+            generics_span: generics_where_clauses_span,
+        });
+    }
 }
 
 fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
@@ -255,7 +254,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
                 Abi::Rust,
             ));
 
-            check_function_signature(
+            let _ = check_function_signature(
                 tcx,
                 ObligationCause::new(
                     start_span,
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 33337190562..126bab68ae3 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -55,7 +55,7 @@ fn equate_intrinsic_type<'tcx>(
         && gen_count_ok(own_counts.consts, n_cts, "const")
     {
         let it_def_id = it.owner_id.def_id;
-        check_function_signature(
+        let _ = check_function_signature(
             tcx,
             ObligationCause::new(it.span, it_def_id, ObligationCauseCode::IntrinsicType),
             it_def_id.into(),
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index 2428fe6ae79..d86ebc2c9c3 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -49,7 +49,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             16 => InlineAsmType::I16,
             32 => InlineAsmType::I32,
             64 => InlineAsmType::I64,
-            _ => unreachable!(),
+            width => bug!("unsupported pointer width: {width}"),
         };
 
         match *ty.kind() {
@@ -101,7 +101,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                             16 => InlineAsmType::VecI16(size),
                             32 => InlineAsmType::VecI32(size),
                             64 => InlineAsmType::VecI64(size),
-                            _ => unreachable!(),
+                            width => bug!("unsupported pointer width: {width}"),
                         })
                     }
                     ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)),
@@ -109,7 +109,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                     _ => None,
                 }
             }
-            ty::Infer(_) => unreachable!(),
+            ty::Infer(_) => bug!("unexpected infer ty in asm operand"),
             _ => None,
         }
     }
@@ -136,8 +136,15 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             ty::Adt(adt, args) if Some(adt.did()) == self.tcx.lang_items().maybe_uninit() => {
                 let fields = &adt.non_enum_variant().fields;
                 let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
-                let ty::Adt(ty, args) = ty.kind() else { unreachable!() };
-                assert!(ty.is_manually_drop());
+                // FIXME: Are we just trying to map to the `T` in `MaybeUninit<T>`?
+                // If so, just get it from the args.
+                let ty::Adt(ty, args) = ty.kind() else {
+                    unreachable!("expected first field of `MaybeUninit` to be an ADT")
+                };
+                assert!(
+                    ty.is_manually_drop(),
+                    "expected first field of `MaybeUnit` to be `ManuallyDrop`"
+                );
                 let fields = &ty.non_enum_variant().fields;
                 let ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args);
                 self.get_asm_ty(ty)
@@ -269,7 +276,6 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         lint.help(format!(
                             "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`",
                         ));
-                        lint
                     },
                 );
             }
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index df17879a967..e4904a0437b 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -77,6 +77,7 @@ use std::num::NonZeroU32;
 
 use check::check_mod_item_types;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::ErrorGuaranteed;
 use rustc_errors::{pluralize, struct_span_err, Diagnostic, DiagnosticBuilder};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
@@ -570,7 +571,26 @@ pub fn check_function_signature<'tcx>(
     mut cause: ObligationCause<'tcx>,
     fn_id: DefId,
     expected_sig: ty::PolyFnSig<'tcx>,
-) {
+) -> Result<(), ErrorGuaranteed> {
+    fn extract_span_for_error_reporting<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        err: TypeError<'_>,
+        cause: &ObligationCause<'tcx>,
+        fn_id: LocalDefId,
+    ) -> rustc_span::Span {
+        let mut args = {
+            let node = tcx.hir().expect_owner(fn_id);
+            let decl = node.fn_decl().unwrap_or_else(|| bug!("expected fn decl, found {:?}", node));
+            decl.inputs.iter().map(|t| t.span).chain(std::iter::once(decl.output.span()))
+        };
+
+        match err {
+            TypeError::ArgumentMutability(i)
+            | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => args.nth(i).unwrap(),
+            _ => cause.span(),
+        }
+    }
+
     let local_id = fn_id.as_local().unwrap_or(CRATE_DEF_ID);
 
     let param_env = ty::ParamEnv::empty();
@@ -587,8 +607,7 @@ pub fn check_function_signature<'tcx>(
         Ok(()) => {
             let errors = ocx.select_all_or_error();
             if !errors.is_empty() {
-                infcx.err_ctxt().report_fulfillment_errors(errors);
-                return;
+                return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
             }
         }
         Err(err) => {
@@ -610,30 +629,14 @@ pub fn check_function_signature<'tcx>(
                 false,
                 false,
             );
-            diag.emit();
-            return;
+            return Err(diag.emit());
         }
     }
 
     let outlives_env = OutlivesEnvironment::new(param_env);
-    let _ = ocx.resolve_regions_and_report_errors(local_id, &outlives_env);
-
-    fn extract_span_for_error_reporting<'tcx>(
-        tcx: TyCtxt<'tcx>,
-        err: TypeError<'_>,
-        cause: &ObligationCause<'tcx>,
-        fn_id: LocalDefId,
-    ) -> rustc_span::Span {
-        let mut args = {
-            let node = tcx.hir().expect_owner(fn_id);
-            let decl = node.fn_decl().unwrap_or_else(|| bug!("expected fn decl, found {:?}", node));
-            decl.inputs.iter().map(|t| t.span).chain(std::iter::once(decl.output.span()))
-        };
-
-        match err {
-            TypeError::ArgumentMutability(i)
-            | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => args.nth(i).unwrap(),
-            _ => cause.span(),
-        }
+    if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, &outlives_env) {
+        return Err(e);
     }
+
+    Ok(())
 }
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 1686479bd0e..b2ff7959106 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -204,11 +204,14 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
                 res = Err(err.emit());
             }
             // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span.
-            match (tcx.impl_polarity(def_id), impl_.polarity) {
-                (ty::ImplPolarity::Positive, _) => {
+            match tcx.impl_polarity(def_id) {
+                ty::ImplPolarity::Positive => {
                     res = res.and(check_impl(tcx, item, impl_.self_ty, &impl_.of_trait));
                 }
-                (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => {
+                ty::ImplPolarity::Negative => {
+                    let ast::ImplPolarity::Negative(span) = impl_.polarity else {
+                        bug!("impl_polarity query disagrees with impl's polarity in AST");
+                    };
                     // FIXME(#27579): what amount of WF checking do we need for neg impls?
                     if let hir::Defaultness::Default { .. } = impl_.defaultness {
                         let mut spans = vec![span];
@@ -222,10 +225,9 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
                         .emit());
                     }
                 }
-                (ty::ImplPolarity::Reservation, _) => {
+                ty::ImplPolarity::Reservation => {
                     // FIXME: what amount of WF checking do we need for reservation impls?
                 }
-                _ => unreachable!(),
             }
             res
         }
@@ -992,15 +994,6 @@ fn check_associated_item(
     })
 }
 
-fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> {
-    match kind {
-        ItemKind::Struct(..) => Some(AdtKind::Struct),
-        ItemKind::Union(..) => Some(AdtKind::Union),
-        ItemKind::Enum(..) => Some(AdtKind::Enum),
-        _ => None,
-    }
-}
-
 /// In a type definition, we check that to ensure that the types of the fields are well-formed.
 fn check_type_defn<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -1068,9 +1061,14 @@ fn check_type_defn<'tcx>(
                         hir_ty.span,
                         wfcx.body_def_id,
                         traits::FieldSized {
-                            adt_kind: match item_adt_kind(&item.kind) {
-                                Some(i) => i,
-                                None => bug!(),
+                            adt_kind: match &item.kind {
+                                ItemKind::Struct(..) => AdtKind::Struct,
+                                ItemKind::Union(..) => AdtKind::Union,
+                                ItemKind::Enum(..) => AdtKind::Enum,
+                                kind => span_bug!(
+                                    item.span,
+                                    "should be wfchecking an ADT, got {kind:?}"
+                                ),
                             },
                             span: hir_ty.span,
                             last,
@@ -1302,7 +1300,9 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
         | GenericParamDefKind::Const { has_default, .. } => {
             has_default && def.index >= generics.parent_count as u32
         }
-        GenericParamDefKind::Lifetime => unreachable!(),
+        GenericParamDefKind::Lifetime => {
+            span_bug!(tcx.def_span(def.def_id), "lifetime params can have no default")
+        }
     };
 
     // Check that concrete defaults are well-formed. See test `type-check-defaults.rs`.
@@ -1607,15 +1607,10 @@ fn check_method_receiver<'tcx>(
 }
 
 fn e0307(tcx: TyCtxt<'_>, span: Span, receiver_ty: Ty<'_>) -> ErrorGuaranteed {
-    struct_span_err!(
-        tcx.sess.diagnostic(),
-        span,
-        E0307,
-        "invalid `self` parameter type: {receiver_ty}"
-    )
-    .note("type of `self` must be `Self` or a type that dereferences to it")
-    .help(HELP_FOR_SELF_TYPE)
-    .emit()
+    struct_span_err!(tcx.sess.dcx(), span, E0307, "invalid `self` parameter type: {receiver_ty}")
+        .note("type of `self` must be `Self` or a type that dereferences to it")
+        .help(HELP_FOR_SELF_TYPE)
+        .emit()
 }
 
 /// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If
@@ -1750,15 +1745,15 @@ fn check_variances_for_type_defn<'tcx>(
             }
         }
         ItemKind::TyAlias(..) => {
-            if tcx.type_alias_is_lazy(item.owner_id) {
-                if tcx.type_of(item.owner_id).skip_binder().references_error() {
-                    return;
-                }
-            } else {
-                bug!();
+            assert!(
+                tcx.type_alias_is_lazy(item.owner_id),
+                "should not be computing variance of non-weak type alias"
+            );
+            if tcx.type_of(item.owner_id).skip_binder().references_error() {
+                return;
             }
         }
-        _ => bug!(),
+        kind => span_bug!(item.span, "cannot compute the variances of {kind:?}"),
     }
 
     let ty_predicates = tcx.predicates_of(item.owner_id);
diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs
index 9ad73eeffc6..36cb8f7a202 100644
--- a/compiler/rustc_hir_analysis/src/check_unused.rs
+++ b/compiler/rustc_hir_analysis/src/check_unused.rs
@@ -45,7 +45,7 @@ fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) {
             item.hir_id(),
             path.span,
             msg,
-            |lint| lint,
+            |_| {},
         );
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 45100457629..d33cfe4ad4d 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -453,7 +453,7 @@ fn lint_auto_trait_impl<'tcx>(
     impl_def_id: LocalDefId,
 ) {
     if trait_ref.args.len() != 1 {
-        tcx.sess.diagnostic().span_delayed_bug(
+        tcx.sess.dcx().span_delayed_bug(
             tcx.def_span(impl_def_id),
             "auto traits cannot have generic parameters",
         );
@@ -522,7 +522,7 @@ fn lint_auto_trait_impl<'tcx>(
                 format!(
                     "try using the same sequence of generic parameters as the {self_descr} definition",
                 ),
-            )
+            );
         },
     );
 }
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index fdd6424a19e..688d32fa32d 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -672,7 +672,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
 
         hir::TraitItemKind::Const(ty, body_id) => {
             tcx.ensure().type_of(def_id);
-            if !tcx.sess.diagnostic().has_stashed_diagnostic(ty.span, StashKey::ItemNoType)
+            if !tcx.sess.dcx().has_stashed_diagnostic(ty.span, StashKey::ItemNoType)
                 && !(is_suggestable_infer_ty(ty) && body_id.is_some())
             {
                 // Account for `const C: _;`.
@@ -836,7 +836,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
     use rustc_hir::*;
 
     let Node::Item(item) = tcx.hir_node_by_def_id(def_id) else {
-        bug!();
+        bug!("expected ADT to be an item");
     };
 
     let repr = tcx.repr_options_of_def(def_id.to_def_id());
@@ -887,7 +887,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
 
             (adt_kind, variants)
         }
-        _ => bug!(),
+        _ => bug!("{:?} is not an ADT", item.owner_id.def_id),
     };
     tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr)
 }
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index c1c2bb62c66..3ee2822edd8 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -279,7 +279,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                             param.hir_id,
                             param.span,
                             TYPE_DEFAULT_NOT_ALLOWED,
-                            |lint| lint,
+                            |_| {},
                         );
                     }
                     Defaults::Deny => {
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 0053af2b7df..39ca1bba065 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -97,8 +97,10 @@ pub(super) fn explicit_item_bounds(
                 item.span,
             ));
         }
-        // These should have been fed!
-        Some(ty::ImplTraitInTraitData::Impl { .. }) => unreachable!(),
+        Some(ty::ImplTraitInTraitData::Impl { .. }) => span_bug!(
+            tcx.def_span(def_id),
+            "item bounds for RPITIT in impl to be fed on def-id creation"
+        ),
         None => {}
     }
 
@@ -128,7 +130,7 @@ pub(super) fn explicit_item_bounds(
             let (hir::OpaqueTyOrigin::FnReturn(fn_def_id)
             | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = *origin
             else {
-                bug!()
+                span_bug!(*span, "RPITIT cannot be a TAIT, but got origin {origin:?}");
             };
             let args = GenericArgs::identity_for_item(tcx, def_id);
             let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index de85a5d8626..41520718aa8 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -296,7 +296,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                         hir::GenericBound::Outlives(lt) => {
                             (icx.astconv().ast_region_to_region(lt, None), lt.ident.span)
                         }
-                        _ => bug!(),
+                        bound => {
+                            span_bug!(
+                                bound.span(),
+                                "lifetime param bounds must be outlives, but found {bound:?}"
+                            )
+                        }
                     };
                     let pred = ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r1, r2))
                         .to_predicate(tcx);
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index a972c51e3ee..9f0742dade8 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -350,7 +350,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     // Nested poly trait refs have the binders concatenated
                     let mut full_binders =
                         self.map.late_bound_vars.entry(*hir_id).or_default().clone();
-                    full_binders.extend(supertrait_bound_vars.into_iter());
+                    full_binders.extend(supertrait_bound_vars);
                     break (full_binders, BinderScopeType::Concatenating);
                 }
             }
@@ -925,7 +925,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                                     "you can use the `'static` lifetime directly, in place of `{}`",
                                     lifetime.ident,
                                 );
-                                lint.help(help)
+                                lint.help(help);
                             },
                         );
                     }
@@ -938,32 +938,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
         }
     }
 
-    fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) {
-        match bound {
-            hir::GenericBound::LangItemTrait(_, _, hir_id, _) => {
-                // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
-                // through the regular poly trait ref code, so we don't get another
-                // chance to introduce a binder. For now, I'm keeping the existing logic
-                // of "if there isn't a Binder scope above us, add one", but I
-                // imagine there's a better way to go about this.
-                let (binders, scope_type) = self.poly_trait_ref_binder_info();
-
-                self.record_late_bound_vars(*hir_id, binders);
-                let scope = Scope::Binder {
-                    hir_id: *hir_id,
-                    bound_vars: FxIndexMap::default(),
-                    s: self.scope,
-                    scope_type,
-                    where_bound_origin: None,
-                };
-                self.with(scope, |this| {
-                    intravisit::walk_param_bound(this, bound);
-                });
-            }
-            _ => intravisit::walk_param_bound(self, bound),
-        }
-    }
-
     fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) {
         self.visit_poly_trait_ref_inner(trait_ref, NonLifetimeBinderAllowed::Allow);
     }
@@ -1300,7 +1274,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                             what,
                         })
                     }
-                    _ => unreachable!(),
+                    kind => span_bug!(
+                        use_span,
+                        "did not expect to resolve lifetime to {}",
+                        kind.descr(param_def_id)
+                    ),
                 };
                 def = ResolvedArg::Error(guar);
             } else if let Some(body_id) = outermost_body {
@@ -1441,7 +1419,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                             what,
                         })
                     }
-                    _ => unreachable!(),
+                    kind => span_bug!(
+                        use_span,
+                        "did not expect to resolve non-lifetime param to {}",
+                        kind.descr(param_def_id.to_def_id())
+                    ),
                 };
                 self.map.defs.insert(hir_id, ResolvedArg::Error(guar));
             } else {
@@ -2123,7 +2105,7 @@ pub fn deny_non_region_late_bound(
 
     for (var, arg) in bound_vars {
         let Node::GenericParam(param) = tcx.hir_node_by_def_id(*var) else {
-            bug!();
+            span_bug!(tcx.def_span(*var), "expected bound-var def-id to resolve to param");
         };
 
         let what = match param.kind {
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 4b2ace748df..15d546537dd 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -20,7 +20,13 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
     use rustc_middle::ty::Ty;
     let hir_id = tcx.local_def_id_to_hir_id(def_id);
 
-    let Node::AnonConst(_) = tcx.hir_node(hir_id) else { panic!() };
+    let node = tcx.hir_node(hir_id);
+    let Node::AnonConst(_) = node else {
+        span_bug!(
+            tcx.def_span(def_id),
+            "expected anon const in `anon_const_type_of`, got {node:?}"
+        );
+    };
 
     let parent_node_id = tcx.hir().parent_id(hir_id);
     let parent_node = tcx.hir_node(parent_node_id);
@@ -568,7 +574,7 @@ fn infer_placeholder_type<'a>(
     // then the user may have written e.g. `const A = 42;`.
     // In this case, the parser has stashed a diagnostic for
     // us to improve in typeck so we do that now.
-    match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) {
+    match tcx.sess.dcx().steal_diagnostic(span, StashKey::ItemNoType) {
         Some(mut err) => {
             if !ty.references_error() {
                 // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic)
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index bf0d9d4856a..3785b61f2f7 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -278,6 +278,10 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
 
     let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
     if let Some(mir_opaque_ty) = mir_opaque_ty {
+        if mir_opaque_ty.references_error() {
+            return mir_opaque_ty.ty;
+        }
+
         let scope = tcx.local_def_id_to_hir_id(owner_def_id);
         debug!(?scope);
         let mut locator = RpitConstraintChecker { def_id, tcx, found: mir_opaque_ty };
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index a4772293697..f461b6a94ec 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -2,7 +2,7 @@
 
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
+    error_code, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
     MultiSpan,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -317,8 +317,8 @@ pub struct MissingTypeParams {
 // Manual implementation of `IntoDiagnostic` to be able to call `span_to_snippet`.
 impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
     #[track_caller]
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut err = handler.struct_span_err_with_code(
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let mut err = dcx.struct_span_err_with_code(
             self.span,
             fluent::hir_analysis_missing_type_params,
             error_code!(E0393),
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 8701626058d..21a50b94a37 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -2003,18 +2003,14 @@ impl<'a> State<'a> {
         });
         self.word("|");
 
-        if let hir::FnRetTy::DefaultReturn(..) = decl.output {
-            return;
-        }
-
-        self.space_if_not_bol();
-        self.word_space("->");
         match decl.output {
             hir::FnRetTy::Return(ty) => {
+                self.space_if_not_bol();
+                self.word_space("->");
                 self.print_type(ty);
                 self.maybe_print_comment(ty.span.lo());
             }
-            hir::FnRetTy::DefaultReturn(..) => unreachable!(),
+            hir::FnRetTy::DefaultReturn(..) => {}
         }
     }
 
@@ -2088,11 +2084,6 @@ impl<'a> State<'a> {
                     }
                     self.print_poly_trait_ref(tref);
                 }
-                GenericBound::LangItemTrait(lang_item, span, ..) => {
-                    self.word("#[lang = \"");
-                    self.print_ident(Ident::new(lang_item.name(), *span));
-                    self.word("\"]");
-                }
                 GenericBound::Outlives(lt) => {
                     self.print_lifetime(lt);
                 }
@@ -2179,7 +2170,7 @@ impl<'a> State<'a> {
                             GenericBound::Outlives(lt) => {
                                 self.print_lifetime(lt);
                             }
-                            _ => panic!(),
+                            _ => panic!("unexpected bound on lifetime param: {bound:?}"),
                         }
 
                         if i != 0 {
@@ -2216,16 +2207,14 @@ impl<'a> State<'a> {
     }
 
     fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
-        if let hir::FnRetTy::DefaultReturn(..) = decl.output {
-            return;
-        }
-
-        self.space_if_not_bol();
-        self.ibox(INDENT_UNIT);
-        self.word_space("->");
         match decl.output {
-            hir::FnRetTy::DefaultReturn(..) => unreachable!(),
-            hir::FnRetTy::Return(ty) => self.print_type(ty),
+            hir::FnRetTy::Return(ty) => {
+                self.space_if_not_bol();
+                self.ibox(INDENT_UNIT);
+                self.word_space("->");
+                self.print_type(ty);
+            }
+            hir::FnRetTy::DefaultReturn(..) => return,
         }
         self.end();
 
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index baca2be06e6..5e6b54950b3 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -417,7 +417,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && let Some(mut diag) = self
                         .tcx
                         .sess
-                        .diagnostic()
+                        .dcx()
                         .steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod)
                 {
                     // Try suggesting `foo(a)` -> `a.foo()` if possible.
@@ -471,6 +471,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
+        if let Some(def_id) = def_id
+            && self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
+            && self.tcx.is_intrinsic(def_id)
+            && self.tcx.item_name(def_id) == sym::const_eval_select
+        {
+            let fn_sig = self.resolve_vars_if_possible(fn_sig);
+            for idx in 0..=1 {
+                let arg_ty = fn_sig.inputs()[idx + 1];
+                let span = arg_exprs.get(idx + 1).map_or(call_expr.span, |arg| arg.span);
+                // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
+                // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
+                // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
+                //
+                // This check is here because there is currently no way to express a trait bound for `FnDef` types only.
+                if let ty::FnDef(def_id, _args) = *arg_ty.kind() {
+                    let fn_once_def_id =
+                        self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span));
+                    let fn_once_output_def_id =
+                        self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span));
+                    if self.tcx.generics_of(fn_once_def_id).host_effect_index.is_none() {
+                        if idx == 0 && !self.tcx.is_const_fn_raw(def_id) {
+                            self.tcx.sess.emit_err(errors::ConstSelectMustBeConst { span });
+                        }
+                    } else {
+                        let const_param: ty::GenericArg<'tcx> =
+                            ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into();
+                        self.register_predicate(traits::Obligation::new(
+                            self.tcx,
+                            self.misc(span),
+                            self.param_env,
+                            ty::TraitRef::new(
+                                self.tcx,
+                                fn_once_def_id,
+                                [arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
+                            ),
+                        ));
+
+                        self.register_predicate(traits::Obligation::new(
+                            self.tcx,
+                            self.misc(span),
+                            self.param_env,
+                            ty::ProjectionPredicate {
+                                projection_ty: ty::AliasTy::new(
+                                    self.tcx,
+                                    fn_once_output_def_id,
+                                    [arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
+                                ),
+                                term: fn_sig.output().into(),
+                            },
+                        ));
+
+                        self.select_obligations_where_possible(|_| {});
+                    }
+                } else {
+                    self.tcx.sess.emit_err(errors::ConstSelectMustBeFn { span, ty: arg_ty });
+                }
+            }
+        }
+
         fn_sig.output()
     }
 
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index d89af297560..0de0365364c 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -149,7 +149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 pub enum CastError {
     ErrorGuaranteed(ErrorGuaranteed),
 
@@ -271,7 +271,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         match e {
                             CastError::NeedViaPtr => "a raw pointer",
                             CastError::NeedViaThinPtr => "a thin pointer",
-                            _ => bug!(),
+                            e => unreachable!("control flow means we should never encounter a {e:?}"),
                         }
                     ));
                 }
@@ -288,13 +288,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     self.cast_ty,
                     fcx,
                 )
-                .help(format!(
-                    "cast through {} first",
-                    match e {
-                        CastError::NeedViaInt => "an integer",
-                        _ => bug!(),
-                    }
-                ))
+                .help("cast through an integer first")
                 .emit();
             }
             CastError::IllegalCast => {
@@ -534,7 +528,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 let unknown_cast_to = match e {
                     CastError::UnknownCastPtrKind => true,
                     CastError::UnknownExprPtrKind => false,
-                    _ => bug!(),
+                    e => unreachable!("control flow means we should never encounter a {e:?}"),
                 };
                 let (span, sub) = if unknown_cast_to {
                     (self.cast_span, errors::CastUnknownPointerSub::To(self.cast_span))
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 7facf8a4016..2855cea80b2 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -261,7 +261,7 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_>
         bounds,
     );
 
-    check_function_signature(
+    let _ = check_function_signature(
         tcx,
         ObligationCause::new(
             tcx.def_span(fn_id),
@@ -300,7 +300,7 @@ fn check_lang_start_fn<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: ty::FnSig<'tcx>, def_id:
         Abi::Rust,
     ));
 
-    check_function_signature(
+    let _ = check_function_signature(
         tcx,
         ObligationCause::new(
             tcx.def_span(def_id),
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index fa087d0a4b0..d19d304128a 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -141,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!(?sig, ?opt_kind);
 
         let closure_kind_ty = match opt_kind {
-            Some(kind) => kind.to_ty(self.tcx),
+            Some(kind) => Ty::from_closure_kind(self.tcx, kind),
 
             // Create a type variable (for now) to represent the closure kind.
             // It will be unified during the upvar inference phase (`upvar.rs`)
@@ -650,9 +650,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         },
                     )
                 }
-                // For a `gen {}` block created as a `gen fn` body, we need the return type to be
-                // ().
-                Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => self.tcx.types.unit,
+                // All `gen {}` and `async gen {}` must return unit.
+                Some(hir::CoroutineKind::Gen(_) | hir::CoroutineKind::AsyncGen(_)) => {
+                    self.tcx.types.unit
+                }
 
                 _ => astconv.ty_infer(None, decl.output.span()),
             },
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 22af79e02a7..61236c07135 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1182,14 +1182,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_sig.unsafety()))
                 }
                 ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer),
-                _ => unreachable!(),
+                _ => span_bug!(cause.span, "should not try to coerce a {prev_ty} to a fn pointer"),
             };
             let next_adjustment = match new_ty.kind() {
                 ty::Closure(..) => {
                     Adjust::Pointer(PointerCoercion::ClosureFnPointer(b_sig.unsafety()))
                 }
                 ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer),
-                _ => unreachable!(),
+                _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"),
             };
             for expr in exprs.iter().map(|e| e.as_coercion_site()) {
                 self.apply_adjustments(
@@ -1918,7 +1918,7 @@ where
 
 impl AsCoercionSite for ! {
     fn as_coercion_site(&self) -> &hir::Expr<'_> {
-        unreachable!()
+        *self
     }
 }
 
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 580586f6267..8b666c63425 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -331,13 +331,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty_op: |ty| {
                 if let ty::Infer(infer) = ty.kind() {
                     match infer {
-                        ty::InferTy::TyVar(_) => self.next_ty_var(TypeVariableOrigin {
+                        ty::TyVar(_) => self.next_ty_var(TypeVariableOrigin {
                             kind: TypeVariableOriginKind::MiscVariable,
                             span: DUMMY_SP,
                         }),
-                        ty::InferTy::IntVar(_) => self.next_int_var(),
-                        ty::InferTy::FloatVar(_) => self.next_float_var(),
-                        _ => bug!(),
+                        ty::IntVar(_) => self.next_int_var(),
+                        ty::FloatVar(_) => self.next_float_var(),
+                        ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
+                            bug!("unexpected fresh ty outside of the trait solver")
+                        }
                     }
                 } else {
                     ty
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 74aec897f95..ff03cf16a27 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -573,13 +573,13 @@ impl rustc_errors::AddToDiagnostic for CastUnknownPointerSub {
     {
         match self {
             CastUnknownPointerSub::To(span) => {
-                let msg = f(diag, crate::fluent_generated::hir_typeck_label_to.into());
+                let msg = f(diag, crate::fluent_generated::hir_typeck_label_to);
                 diag.span_label(span, msg);
-                let msg = f(diag, crate::fluent_generated::hir_typeck_note.into());
+                let msg = f(diag, crate::fluent_generated::hir_typeck_note);
                 diag.note(msg);
             }
             CastUnknownPointerSub::From(span) => {
-                let msg = f(diag, crate::fluent_generated::hir_typeck_label_from.into());
+                let msg = f(diag, crate::fluent_generated::hir_typeck_label_from);
                 diag.span_label(span, msg);
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 725f327d835..7bd2c3f8b6b 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1436,12 +1436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
             && let Some(span) = self.tcx.hir().opt_span(hir_id)
         {
-            match self
-                .tcx
-                .sess
-                .diagnostic()
-                .steal_diagnostic(span, StashKey::UnderscoreForArrayLengths)
-            {
+            match self.tcx.sess.dcx().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) {
                 Some(mut err) => {
                     err.span_suggestion(
                         span,
@@ -2002,11 +1997,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 != range_def_id
         {
             // Suppress any range expr type mismatches
-            if let Some(mut diag) = self
-                .tcx
-                .sess
-                .diagnostic()
-                .steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo)
+            if let Some(mut diag) =
+                self.tcx.sess.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo)
             {
                 diag.delay_as_bug();
             }
@@ -2087,7 +2079,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
                     format!("{} and `{last}` ", names.join(", "))
                 }
-                [] => unreachable!(),
+                [] => bug!("expected at least one private field to report"),
             };
             err.note(format!(
                 "{}private field{s} {names}that {were} not provided",
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 7744f75121e..24b577fd3c5 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -72,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             orig_span,
                             custom_note
                                 .unwrap_or("any code following this expression is unreachable"),
-                        )
+                        );
                     },
                 )
             }
@@ -848,7 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && let Some(mut diag) = self
                         .tcx
                         .sess
-                        .diagnostic()
+                        .dcx()
                         .steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
                 {
                     diag.emit();
@@ -884,7 +884,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && let Some(mut diag) = self
                         .tcx
                         .sess
-                        .diagnostic()
+                        .dcx()
                         .steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
                 {
                     if trait_missing_method {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index bb393e27a5b..4caa0df58b6 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -230,11 +230,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let minimum_input_count = expected_input_tys.len();
         let provided_arg_count = provided_args.len();
 
-        let is_const_eval_select = matches!(fn_def_id, Some(def_id) if
-            self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
-            && self.tcx.is_intrinsic(def_id)
-            && self.tcx.item_name(def_id) == sym::const_eval_select);
-
         // We introduce a helper function to demand that a given argument satisfy a given input
         // This is more complicated than just checking type equality, as arguments could be coerced
         // This version writes those types back so further type checking uses the narrowed types
@@ -269,30 +264,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return Compatibility::Incompatible(coerce_error);
             }
 
-            // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
-            // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
-            // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
-            //
-            // This check is here because there is currently no way to express a trait bound for `FnDef` types only.
-            if is_const_eval_select && (1..=2).contains(&idx) {
-                if let ty::FnDef(def_id, args) = *checked_ty.kind() {
-                    if idx == 1 {
-                        if !self.tcx.is_const_fn_raw(def_id) {
-                            self.tcx.sess.emit_err(errors::ConstSelectMustBeConst {
-                                span: provided_arg.span,
-                            });
-                        } else {
-                            self.enforce_context_effects(provided_arg.span, def_id, args)
-                        }
-                    }
-                } else {
-                    self.tcx.sess.emit_err(errors::ConstSelectMustBeFn {
-                        span: provided_arg.span,
-                        ty: checked_ty,
-                    });
-                }
-            }
-
             // 3. Check if the formal type is a supertype of the checked one
             //    and register any such obligations for future type checks
             let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup(
@@ -993,7 +964,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }) {
                         match e {
                             Error::Missing(expected_idx) => missing_idxs.push(expected_idx),
-                            _ => unreachable!(),
+                            _ => unreachable!(
+                                "control flow ensures that we should always get an `Error::Missing`"
+                            ),
                         }
                     }
 
@@ -1850,7 +1823,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         for (span, code) in errors_causecode {
             let Some(mut diag) =
-                self.tcx.sess.diagnostic().steal_diagnostic(span, StashKey::MaybeForgetReturn)
+                self.tcx.sess.dcx().steal_diagnostic(span, StashKey::MaybeForgetReturn)
             else {
                 continue;
             };
@@ -2018,7 +1991,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let new_def_id = self.probe(|_| {
                         let trait_ref = ty::TraitRef::new(
                             self.tcx,
-                            call_kind.to_def_id(self.tcx),
+                            self.tcx.fn_trait_kind_to_def_id(call_kind)?,
                             [
                                 callee_ty,
                                 self.next_ty_var(TypeVariableOrigin {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 28f377083f6..668e547571f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -825,9 +825,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && let hir::Node::Item(hir::Item {
                         kind: hir::ItemKind::OpaqueTy(op_ty), ..
                     }) = self.tcx.hir_node(item_id.hir_id())
-                    && let [
-                        hir::GenericBound::LangItemTrait(hir::LangItem::Future, _, _, generic_args),
-                    ] = op_ty.bounds
+                    && let [hir::GenericBound::Trait(trait_ref, _)] = op_ty.bounds
+                    && let Some(hir::PathSegment { args: Some(generic_args), .. }) =
+                        trait_ref.trait_ref.path.segments.last()
                     && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args
                     && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(term) } =
                         ty_binding.kind
@@ -2072,8 +2072,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     Some(CtorKind::Fn) => ("(".to_owned(), ")"),
                     None => (format!(" {{ {field_name}: "), " }"),
 
-                    // unit variants don't have fields
-                    Some(CtorKind::Const) => unreachable!(),
+                    Some(CtorKind::Const) => unreachable!("unit variants don't have fields"),
                 };
 
                 // Suggest constructor as deep into the block tree as possible.
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index f1f893623f6..13a24948611 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -413,21 +413,21 @@ enum TupleArgumentsFlag {
 }
 
 fn fatally_break_rust(tcx: TyCtxt<'_>) {
-    let handler = tcx.sess.diagnostic();
-    handler.span_bug_no_panic(
+    let dcx = tcx.sess.dcx();
+    dcx.span_bug_no_panic(
         MultiSpan::new(),
         "It looks like you're trying to break rust; would you like some ICE?",
     );
-    handler.note("the compiler expectedly panicked. this is a feature.");
-    handler.note(
+    dcx.note("the compiler expectedly panicked. this is a feature.");
+    dcx.note(
         "we would appreciate a joke overview: \
          https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675",
     );
-    handler.note(format!("rustc {} running on {}", tcx.sess.cfg_version, config::host_triple(),));
+    dcx.note(format!("rustc {} running on {}", tcx.sess.cfg_version, config::host_triple(),));
     if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() {
-        handler.note(format!("compiler flags: {}", flags.join(" ")));
+        dcx.note(format!("compiler flags: {}", flags.join(" ")));
         if excluded_cargo_defaults {
-            handler.note("some of the compiler flags provided by cargo are hidden");
+            dcx.note("some of the compiler flags provided by cargo are hidden");
         }
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index e7af7da205c..b2ead3cd40b 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -415,7 +415,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                             )
                             .into()
                     }
-                    _ => unreachable!(),
+                    (kind, arg) => {
+                        bug!("mismatched method arg kind {kind:?} in turbofish: {arg:?}")
+                    }
                 }
             }
 
diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
index 3f1dca5b1de..43d258de6ca 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
@@ -122,8 +122,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             format!("disambiguate the method call with `({self_adjusted})`",),
                         );
                     }
-
-                    lint
                 },
             );
         } else {
@@ -187,8 +185,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             ),
                         );
                     }
-
-                    lint
                 },
             );
         }
@@ -307,8 +303,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
                     Applicability::MachineApplicable,
                 );
-
-                lint
             },
         );
     }
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 4243bce377f..fe2d43a3c92 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -445,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         scope_expr_id,
                         span,
                         "type annotations needed",
-                        |lint| lint,
+                        |_| {},
                     );
                 }
             } else {
@@ -1427,8 +1427,6 @@ impl<'tcx> Pick<'tcx> {
                         ));
                     }
                 }
-
-                lint
             },
         );
     }
@@ -1548,9 +1546,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     );
 
                     let candidate_obligations = impl_obligations
-                        .chain(norm_obligations.into_iter())
+                        .chain(norm_obligations)
                         .chain(ref_obligations.iter().cloned())
-                        .chain(normalization_obligations.into_iter());
+                        .chain(normalization_obligations);
 
                     // Evaluate those obligations to see if they might possibly hold.
                     for o in candidate_obligations {
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 8fb703fa7f5..7595f21d9f1 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -1929,7 +1929,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         };
         let Some(mut diag) =
-            self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
+            self.tcx.sess.dcx().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
         else {
             return;
         };
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 165fc630787..56a420fab4f 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1820,7 +1820,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fn joined_uncovered_patterns(witnesses: &[&Ident]) -> String {
             const LIMIT: usize = 3;
             match witnesses {
-                [] => bug!(),
+                [] => {
+                    unreachable!(
+                        "expected an uncovered pattern, otherwise why are we emitting an error?"
+                    )
+                }
                 [witness] => format!("`{witness}`"),
                 [head @ .., tail] if head.len() < LIMIT => {
                     let head: Vec<_> = head.iter().map(<_>::to_string).collect();
@@ -1845,8 +1849,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lint.note(format!(
             "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found",
         ));
-
-        lint
     });
     }
 
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index f5bed834eba..726ee02d75e 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -261,7 +261,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Unify the (as yet unbound) type variable in the closure
             // args with the kind we inferred.
             let closure_kind_ty = closure_args.as_closure().kind_ty();
-            self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty);
+            self.demand_eqtype(
+                span,
+                Ty::from_closure_kind(self.tcx, closure_kind),
+                closure_kind_ty,
+            );
 
             // If we have an origin, store it.
             if let Some(mut origin) = origin {
@@ -676,49 +680,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // `tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`.
         for (_, captures) in &mut root_var_min_capture_list {
             captures.sort_by(|capture1, capture2| {
-                for (p1, p2) in capture1.place.projections.iter().zip(&capture2.place.projections) {
+                fn is_field<'a>(p: &&Projection<'a>) -> bool {
+                    match p.kind {
+                        ProjectionKind::Field(_, _) => true,
+                        ProjectionKind::Deref | ProjectionKind::OpaqueCast => false,
+                        p @ (ProjectionKind::Subslice | ProjectionKind::Index) => {
+                            bug!("ProjectionKind {:?} was unexpected", p)
+                        }
+                    }
+                }
+
+                // Need to sort only by Field projections, so filter away others.
+                // A previous implementation considered other projection types too
+                // but that caused ICE #118144
+                let capture1_field_projections = capture1.place.projections.iter().filter(is_field);
+                let capture2_field_projections = capture2.place.projections.iter().filter(is_field);
+
+                for (p1, p2) in capture1_field_projections.zip(capture2_field_projections) {
                     // We do not need to look at the `Projection.ty` fields here because at each
                     // step of the iteration, the projections will either be the same and therefore
                     // the types must be as well or the current projection will be different and
                     // we will return the result of comparing the field indexes.
                     match (p1.kind, p2.kind) {
-                        // Paths are the same, continue to next loop.
-                        (ProjectionKind::Deref, ProjectionKind::Deref) => {}
-                        (ProjectionKind::OpaqueCast, ProjectionKind::OpaqueCast) => {}
-                        (ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _))
-                            if i1 == i2 => {}
-
-                        // Fields are different, compare them.
                         (ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _)) => {
-                            return i1.cmp(&i2);
+                            // Compare only if paths are different.
+                            // Otherwise continue to the next iteration
+                            if i1 != i2 {
+                                return i1.cmp(&i2);
+                            }
                         }
-
-                        // We should have either a pair of `Deref`s or a pair of `Field`s.
-                        // Anything else is a bug.
-                        (
-                            l @ (ProjectionKind::Deref | ProjectionKind::Field(..)),
-                            r @ (ProjectionKind::Deref | ProjectionKind::Field(..)),
-                        ) => bug!(
-                            "ProjectionKinds Deref and Field were mismatched: ({:?}, {:?})",
-                            l,
-                            r
-                        ),
-                        (
-                            l @ (ProjectionKind::Index
-                            | ProjectionKind::Subslice
-                            | ProjectionKind::Deref
-                            | ProjectionKind::OpaqueCast
-                            | ProjectionKind::Field(..)),
-                            r @ (ProjectionKind::Index
-                            | ProjectionKind::Subslice
-                            | ProjectionKind::Deref
-                            | ProjectionKind::OpaqueCast
-                            | ProjectionKind::Field(..)),
-                        ) => bug!(
-                            "ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})",
-                            l,
-                            r
-                        ),
+                        // Given the filter above, this arm should never be hit
+                        (l, r) => bug!("ProjectionKinds {:?} or {:?} were unexpected", l, r),
                     }
                 }
 
@@ -908,8 +900,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Applicability::HasPlaceholders
                         );
                     }
-
-                    lint
                 },
             );
         }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 5e562d9453f..d0cf4575c8f 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -505,7 +505,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             if !errors_buffer.is_empty() {
                 errors_buffer.sort_by_key(|diag| diag.span.primary_span());
                 for diag in errors_buffer {
-                    self.tcx().sess.diagnostic().emit_diagnostic(diag);
+                    self.tcx().sess.dcx().emit_diagnostic(diag);
                 }
             }
         }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 8fe6c1b0d86..817a4451dd1 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -232,7 +232,9 @@ fn ty_to_string<'tcx>(
 /// something users are familiar with. Directly printing the `fn_sig` of closures also
 /// doesn't work as they actually use the "rust-call" API.
 fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String {
-    let ty::Closure(_, args) = ty.kind() else { unreachable!() };
+    let ty::Closure(_, args) = ty.kind() else {
+        bug!("cannot convert non-closure to fn str in `closure_as_fn_str`")
+    };
     let fn_sig = args.as_closure().sig();
     let args = fn_sig
         .inputs()
@@ -374,7 +376,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 multi_suggestions,
                 bad_label,
             }
-            .into_diagnostic(self.tcx.sess.diagnostic()),
+            .into_diagnostic(self.tcx.sess.dcx()),
             TypeAnnotationNeeded::E0283 => AmbiguousImpl {
                 span,
                 source_kind,
@@ -384,7 +386,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 multi_suggestions,
                 bad_label,
             }
-            .into_diagnostic(self.tcx.sess.diagnostic()),
+            .into_diagnostic(self.tcx.sess.dcx()),
             TypeAnnotationNeeded::E0284 => AmbiguousReturn {
                 span,
                 source_kind,
@@ -394,7 +396,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 multi_suggestions,
                 bad_label,
             }
-            .into_diagnostic(self.tcx.sess.diagnostic()),
+            .into_diagnostic(self.tcx.sess.dcx()),
         }
     }
 }
@@ -581,7 +583,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 multi_suggestions,
                 bad_label: None,
             }
-            .into_diagnostic(self.tcx.sess.diagnostic()),
+            .into_diagnostic(self.tcx.sess.dcx()),
             TypeAnnotationNeeded::E0283 => AmbiguousImpl {
                 span,
                 source_kind,
@@ -591,7 +593,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 multi_suggestions,
                 bad_label: None,
             }
-            .into_diagnostic(self.tcx.sess.diagnostic()),
+            .into_diagnostic(self.tcx.sess.dcx()),
             TypeAnnotationNeeded::E0284 => AmbiguousReturn {
                 span,
                 source_kind,
@@ -601,7 +603,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 multi_suggestions,
                 bad_label: None,
             }
-            .into_diagnostic(self.tcx.sess.diagnostic()),
+            .into_diagnostic(self.tcx.sess.dcx()),
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 362bb816910..859c10ef142 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -140,7 +140,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     span,
                     notes: reference_valid.into_iter().chain(content_valid).collect(),
                 }
-                .into_diagnostic(self.tcx.sess.diagnostic())
+                .into_diagnostic(self.tcx.sess.dcx())
             }
             infer::RelateObjectBound(span) => {
                 let object_valid = note_and_explain::RegionExplanation::new(
@@ -161,7 +161,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     span,
                     notes: object_valid.into_iter().chain(pointer_valid).collect(),
                 }
-                .into_diagnostic(self.tcx.sess.diagnostic())
+                .into_diagnostic(self.tcx.sess.dcx())
             }
             infer::RelateParamBound(span, ty, opt_span) => {
                 let prefix = match *sub {
@@ -177,7 +177,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     self.tcx, sub, opt_span, prefix, suffix,
                 );
                 FulfillReqLifetime { span, ty: self.resolve_vars_if_possible(ty), note }
-                    .into_diagnostic(self.tcx.sess.diagnostic())
+                    .into_diagnostic(self.tcx.sess.dcx())
             }
             infer::RelateRegionParamBound(span) => {
                 let param_instantiated = note_and_explain::RegionExplanation::new(
@@ -198,7 +198,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     span,
                     notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
                 }
-                .into_diagnostic(self.tcx.sess.diagnostic())
+                .into_diagnostic(self.tcx.sess.dcx())
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
                 let pointer_valid = note_and_explain::RegionExplanation::new(
@@ -220,7 +220,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ty: self.resolve_vars_if_possible(ty),
                     notes: pointer_valid.into_iter().chain(data_valid).collect(),
                 }
-                .into_diagnostic(self.tcx.sess.diagnostic())
+                .into_diagnostic(self.tcx.sess.dcx())
             }
             infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
                 let mut err = self.report_extra_impl_obligation(
@@ -281,7 +281,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     span,
                     notes: instantiated.into_iter().chain(must_outlive).collect(),
                 }
-                .into_diagnostic(self.tcx.sess.diagnostic())
+                .into_diagnostic(self.tcx.sess.dcx())
             }
         };
         if sub.is_error() || sup.is_error() {
@@ -375,7 +375,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     err.span_note(span, "the lifetime requirement is introduced here");
                     err
                 } else {
-                    unreachable!()
+                    unreachable!(
+                        "control flow ensures we have a `BindingObligation` or `ExprBindingObligation` here..."
+                    )
                 }
             }
             infer::Subtype(box trace) => {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index b762778122b..bbe07b8ed72 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -668,26 +668,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     (
                         hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
                         hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
-                    ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
-                        match (left, right) {
-                            (
-                                hir::GenericBound::Trait(tl, ml),
-                                hir::GenericBound::Trait(tr, mr),
-                            ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
+                    ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
+                        left, right,
+                    ) {
+                        (hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr))
+                            if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
                                 && ml == mr =>
-                            {
-                                true
-                            }
-                            (
-                                hir::GenericBound::LangItemTrait(langl, _, _, argsl),
-                                hir::GenericBound::LangItemTrait(langr, _, _, argsr),
-                            ) if langl == langr => {
-                                // FIXME: consider the bounds!
-                                debug!("{:?} {:?}", argsl, argsr);
-                                true
-                            }
-                            _ => false,
+                        {
+                            true
                         }
+                        _ => false,
                     }) =>
                     {
                         StatementAsExpression::NeedsBoxing
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 18231af2bed..e092bbbfdd6 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -5,7 +5,7 @@ pub use self::BoundRegionConversionTime::*;
 pub use self::RegionVariableOrigin::*;
 pub use self::SubregionOrigin::*;
 pub use self::ValuePairs::*;
-pub use combine::ObligationEmittingRelation;
+pub use relate::combine::ObligationEmittingRelation;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::undo_log::UndoLogs;
 use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey};
@@ -43,7 +43,6 @@ use rustc_span::{Span, DUMMY_SP};
 use std::cell::{Cell, RefCell};
 use std::fmt;
 
-use self::combine::CombineFields;
 use self::error_reporting::TypeErrCtxt;
 use self::free_regions::RegionRelations;
 use self::lexical_region_resolve::LexicalRegionResolutions;
@@ -51,29 +50,23 @@ use self::region_constraints::{GenericKind, VarInfos, VerifyBound};
 use self::region_constraints::{
     RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
 };
+pub use self::relate::combine::CombineFields;
+pub use self::relate::nll as nll_relate;
 use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 
 pub mod at;
 pub mod canonical;
-mod combine;
-mod equate;
 pub mod error_reporting;
 pub mod free_regions;
 mod freshen;
 mod fudge;
-mod generalize;
-mod glb;
-mod higher_ranked;
-pub mod lattice;
 mod lexical_region_resolve;
-mod lub;
-pub mod nll_relate;
 pub mod opaque_types;
 pub mod outlives;
 mod projection;
 pub mod region_constraints;
+mod relate;
 pub mod resolve;
-mod sub;
 pub mod type_variable;
 mod undo_log;
 
@@ -381,17 +374,13 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
         self.probe_ty_var(vid).ok()
     }
 
-    fn root_lt_var(&self, vid: ty::RegionVid) -> ty::RegionVid {
-        self.root_region_var(vid)
-    }
-
-    fn probe_lt_var(&self, vid: ty::RegionVid) -> Option<ty::Region<'tcx>> {
+    fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> Option<ty::Region<'tcx>> {
         let re = self
             .inner
             .borrow_mut()
             .unwrap_region_constraints()
             .opportunistic_resolve_var(self.tcx, vid);
-        if re.is_var() { None } else { Some(re) }
+        if *re == ty::ReVar(vid) { None } else { Some(re) }
     }
 
     fn root_ct_var(&self, vid: ConstVid) -> ConstVid {
@@ -724,10 +713,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
 }
 
 impl<'tcx, T> InferOk<'tcx, T> {
-    pub fn unit(self) -> InferOk<'tcx, ()> {
-        InferOk { value: (), obligations: self.obligations }
-    }
-
     /// Extracts `value`, registering any obligations into `fulfill_cx`.
     pub fn into_value_registering_obligations(
         self,
@@ -1036,15 +1021,10 @@ impl<'tcx> InferCtxt<'tcx> {
             _ => {}
         }
 
-        Ok(self.commit_if_ok(|_snapshot| {
-            let ty::SubtypePredicate { a_is_expected, a, b } =
-                self.instantiate_binder_with_placeholders(predicate);
+        let ty::SubtypePredicate { a_is_expected, a, b } =
+            self.instantiate_binder_with_placeholders(predicate);
 
-            let ok =
-                self.at(cause, param_env).sub_exp(DefineOpaqueTypes::No, a_is_expected, a, b)?;
-
-            Ok(ok.unit())
-        }))
+        Ok(self.at(cause, param_env).sub_exp(DefineOpaqueTypes::No, a_is_expected, a, b))
     }
 
     pub fn region_outlives_predicate(
@@ -1367,10 +1347,6 @@ impl<'tcx> InferCtxt<'tcx> {
         self.inner.borrow_mut().type_variables().root_var(var)
     }
 
-    pub fn root_region_var(&self, var: ty::RegionVid) -> ty::RegionVid {
-        self.inner.borrow_mut().unwrap_region_constraints().root_var(var)
-    }
-
     pub fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid {
         self.inner.borrow_mut().const_unification_table().find(var).vid
     }
diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs
index 45df22d44e8..47038cfd468 100644
--- a/compiler/rustc_infer/src/infer/outlives/components.rs
+++ b/compiler/rustc_infer/src/infer/outlives/components.rs
@@ -203,7 +203,9 @@ pub(super) fn compute_alias_components_recursive<'tcx>(
     out: &mut SmallVec<[Component<'tcx>; 4]>,
     visited: &mut SsoHashSet<GenericArg<'tcx>>,
 ) {
-    let ty::Alias(kind, alias_ty) = alias_ty.kind() else { bug!() };
+    let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
+        unreachable!("can only call `compute_alias_components_recursive` on an alias type")
+    };
     let opt_variances = if *kind == ty::Opaque { tcx.variances_of(alias_ty.def_id) } else { &[] };
     for (index, child) in alias_ty.args.iter().enumerate() {
         if opt_variances.get(index) == Some(&ty::Bivariant) {
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index ce619ae8a0d..bb578a482e4 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -130,12 +130,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         // see the extensive comment in projection_must_outlive
         let recursive_bound = {
             let mut components = smallvec![];
-            compute_alias_components_recursive(
-                self.tcx,
-                alias_ty_as_ty.into(),
-                &mut components,
-                visited,
-            );
+            compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components, visited);
             self.bound_from_components(&components, visited)
         };
 
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index 5c043b1d3dd..cbd8040c9f1 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -623,11 +623,6 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
         }
     }
 
-    pub fn root_var(&mut self, vid: ty::RegionVid) -> ty::RegionVid {
-        let mut ut = self.unification_table_mut(); // FIXME(rust-lang/ena#42): unnecessary mut
-        ut.find(vid).vid
-    }
-
     fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> {
         match t {
             Glb => &mut self.glbs,
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index 6608fdab9d0..dfaca3458d6 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -3,7 +3,7 @@
 //! combining two instances of various things and yielding a new instance.
 //! These combiner methods always yield a `Result<T>`. To relate two
 //! types, you can use `infcx.at(cause, param_env)` which then allows
-//! you to use the relevant methods of [At](super::at::At).
+//! you to use the relevant methods of [At](crate::infer::at::At).
 //!
 //! Combiners mostly do their specific behavior and then hand off the
 //! bulk of the work to [InferCtxt::super_combine_tys] and
@@ -23,11 +23,11 @@
 //! this should be correctly updated.
 
 use super::equate::Equate;
+use super::generalize::{self, CombineDelegate, Generalization};
 use super::glb::Glb;
 use super::lub::Lub;
 use super::sub::Sub;
-use super::{DefineOpaqueTypes, InferCtxt, TypeTrace};
-use crate::infer::generalize::{self, CombineDelegate, Generalization};
+use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::traits::{Obligation, PredicateObligations};
 use rustc_middle::infer::canonical::OriginalQueryValues;
 use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
@@ -103,15 +103,19 @@ impl<'tcx> InferCtxt<'tcx> {
             }
 
             // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
-            (
-                ty::Alias(..),
-                ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)),
-            )
-            | (
-                ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)),
-                ty::Alias(..),
-            ) if self.next_trait_solver() => {
-                bug!()
+            (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..))
+                if self.next_trait_solver() =>
+            {
+                bug!(
+                    "We do not expect to encounter `TyVar` this late in combine \
+                    -- they should have been handled earlier"
+                )
+            }
+            (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)))
+            | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _)
+                if self.next_trait_solver() =>
+            {
+                bug!("We do not expect to encounter `Fresh` variables in the new solver")
             }
 
             (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
@@ -548,7 +552,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
     }
 
     pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
-        self.obligations.extend(obligations.into_iter());
+        self.obligations.extend(obligations);
     }
 
     pub fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>) {
diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/relate/equate.rs
index 5d929394eb0..9943c638a91 100644
--- a/compiler/rustc_infer/src/infer/equate.rs
+++ b/compiler/rustc_infer/src/infer/relate/equate.rs
@@ -1,8 +1,6 @@
-use crate::infer::DefineOpaqueTypes;
-use crate::traits::PredicateObligations;
-
 use super::combine::{CombineFields, ObligationEmittingRelation};
-use super::Subtype;
+use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
+use crate::traits::PredicateObligations;
 
 use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::GenericArgsRef;
@@ -133,7 +131,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
         b: ty::Region<'tcx>,
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
         debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
-        let origin = Subtype(Box::new(self.fields.trace.clone()));
+        let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
         self.fields
             .infcx
             .inner
diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index 383f3bdbe23..66f7b08ee12 100644
--- a/compiler/rustc_infer/src/infer/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -16,7 +16,7 @@ use crate::infer::{InferCtxt, RegionVariableOrigin};
 /// Attempts to generalize `term` for the type variable `for_vid`.
 /// This checks for cycles -- that is, whether the type `term`
 /// references `for_vid`.
-pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>> + Relate<'tcx>>(
+pub fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>> + Relate<'tcx>>(
     infcx: &InferCtxt<'tcx>,
     delegate: &mut D,
     term: T,
@@ -54,7 +54,7 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
 
 /// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
 /// in the generalizer code.
-pub(super) trait GeneralizerDelegate<'tcx> {
+pub trait GeneralizerDelegate<'tcx> {
     fn param_env(&self) -> ty::ParamEnv<'tcx>;
 
     fn forbid_inference_vars() -> bool;
@@ -64,7 +64,7 @@ pub(super) trait GeneralizerDelegate<'tcx> {
     fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
 }
 
-pub(super) struct CombineDelegate<'cx, 'tcx> {
+pub struct CombineDelegate<'cx, 'tcx> {
     pub infcx: &'cx InferCtxt<'tcx>,
     pub param_env: ty::ParamEnv<'tcx>,
     pub span: Span,
@@ -515,7 +515,7 @@ where
 /// not only the generalized type, but also a bool flag
 /// indicating whether further WF checks are needed.
 #[derive(Debug)]
-pub(super) struct Generalization<T> {
+pub struct Generalization<T> {
     /// When generalizing `<?0 as Trait>::Assoc` or
     /// `<T as Bar<<?0 as Foo>::Assoc>>::Assoc`
     /// for `?0` generalization returns an inference
@@ -524,7 +524,7 @@ pub(super) struct Generalization<T> {
     /// This has to be handled wotj care as it can
     /// otherwise very easily result in infinite
     /// recursion.
-    pub(super) value_may_be_infer: T,
+    pub value_may_be_infer: T,
 
     /// If true, then the generalized type may not be well-formed,
     /// even if the source type is well-formed, so we should add an
@@ -551,5 +551,5 @@ pub(super) struct Generalization<T> {
     /// will force the calling code to check that `WF(Foo<?C, ?D>)`
     /// holds, which in turn implies that `?C::Item == ?D`. So once
     /// `?C` is constrained, that should suffice to restrict `?D`.
-    pub(super) needs_wf: bool,
+    pub needs_wf: bool,
 }
diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index 2f659d9a665..6a3413879c4 100644
--- a/compiler/rustc_infer/src/infer/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -1,13 +1,12 @@
 //! Greatest lower bound. See [`lattice`].
 
+use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
+
 use super::combine::{CombineFields, ObligationEmittingRelation};
 use super::lattice::{self, LatticeDir};
-use super::Subtype;
-use super::{DefineOpaqueTypes, InferCtxt};
-
+use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
 use crate::traits::{ObligationCause, PredicateObligations};
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 
 /// "Greatest lower bound" (common subtype)
 pub struct Glb<'combine, 'infcx, 'tcx> {
@@ -68,7 +67,7 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
         debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
 
-        let origin = Subtype(Box::new(self.fields.trace.clone()));
+        let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
         // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8
         Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions(
             self.tcx(),
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
index 510b1797d3c..440df8c8936 100644
--- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
@@ -2,8 +2,8 @@
 //! the end of the file for details.
 
 use super::combine::CombineFields;
-use super::{HigherRankedType, InferCtxt};
 use crate::infer::CombinedSnapshot;
+use crate::infer::{HigherRankedType, InferCtxt};
 use rustc_middle::ty::fold::FnMutDelegate;
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable};
diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs
index 9ef35429fe3..744e2dfa380 100644
--- a/compiler/rustc_infer/src/infer/lattice.rs
+++ b/compiler/rustc_infer/src/infer/relate/lattice.rs
@@ -18,10 +18,10 @@
 //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
 
 use super::combine::ObligationEmittingRelation;
-use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use super::{DefineOpaqueTypes, InferCtxt};
-
+use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use crate::infer::{DefineOpaqueTypes, InferCtxt};
 use crate::traits::ObligationCause;
+
 use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::TyVar;
 use rustc_middle::ty::{self, Ty};
diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index e41ec7e6c01..41cd98ed0cf 100644
--- a/compiler/rustc_infer/src/infer/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -2,10 +2,9 @@
 
 use super::combine::{CombineFields, ObligationEmittingRelation};
 use super::lattice::{self, LatticeDir};
-use super::Subtype;
-use super::{DefineOpaqueTypes, InferCtxt};
-
+use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
 use crate::traits::{ObligationCause, PredicateObligations};
+
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 
@@ -68,7 +67,7 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
         debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
 
-        let origin = Subtype(Box::new(self.fields.trace.clone()));
+        let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
         // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8
         Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions(
             self.tcx(),
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
new file mode 100644
index 00000000000..f688c2b74a6
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -0,0 +1,12 @@
+//! This module contains the definitions of most `TypeRelation`s in the type system
+//! (except for some relations used for diagnostics and heuristics in the compiler).
+
+pub(super) mod combine;
+mod equate;
+pub(super) mod generalize;
+mod glb;
+mod higher_ranked;
+mod lattice;
+mod lub;
+pub mod nll;
+mod sub;
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/relate/nll.rs
index d707c30206d..afc2a8b2f62 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/nll.rs
@@ -30,8 +30,8 @@ use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
 use rustc_span::{Span, Symbol};
 use std::fmt::Debug;
 
-use crate::infer::combine::ObligationEmittingRelation;
-use crate::infer::generalize::{self, Generalization};
+use super::combine::ObligationEmittingRelation;
+use super::generalize::{self, Generalization};
 use crate::infer::InferCtxt;
 use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::traits::{Obligation, PredicateObligations};
@@ -247,7 +247,9 @@ where
         let (a, b) = match (a.kind(), b.kind()) {
             (&ty::Alias(ty::Opaque, ..), _) => (a, generalize(b, false)?),
             (_, &ty::Alias(ty::Opaque, ..)) => (generalize(a, true)?, b),
-            _ => unreachable!(),
+            _ => unreachable!(
+                "expected at least one opaque type in `relate_opaques`, got {a} and {b}."
+            ),
         };
         let cause = ObligationCause::dummy_with_span(self.delegate.span());
         let obligations = self
@@ -707,7 +709,9 @@ where
             ),
             // FIXME(deferred_projection_equality): Implement this when we trigger it.
             // Probably just need to do nothing here.
-            ty::Variance::Bivariant => unreachable!(),
+            ty::Variance::Bivariant => {
+                unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)")
+            }
         })]);
     }
 }
diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/relate/sub.rs
index 0c3bb633b53..5a623e48c93 100644
--- a/compiler/rustc_infer/src/infer/sub.rs
+++ b/compiler/rustc_infer/src/infer/relate/sub.rs
@@ -1,7 +1,7 @@
 use super::combine::CombineFields;
-use super::{DefineOpaqueTypes, ObligationEmittingRelation, SubregionOrigin};
-
+use crate::infer::{DefineOpaqueTypes, ObligationEmittingRelation, SubregionOrigin};
 use crate::traits::{Obligation, PredicateObligations};
+
 use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::TyVar;
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 6527e87d396..d58d60fc8be 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::registry::Registry;
-use rustc_errors::{ErrorGuaranteed, Handler};
+use rustc_errors::{DiagCtxt, ErrorGuaranteed};
 use rustc_lint::LintStore;
 use rustc_middle::ty;
 use rustc_middle::util::Providers;
@@ -18,7 +18,7 @@ use rustc_query_system::query::print_query_stack;
 use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
 use rustc_session::filesearch::sysroot_candidates;
 use rustc_session::parse::ParseSess;
-use rustc_session::{lint, CompilerIO, EarlyErrorHandler, Session};
+use rustc_session::{lint, CompilerIO, EarlyDiagCtxt, Session};
 use rustc_span::source_map::FileLoader;
 use rustc_span::symbol::sym;
 use rustc_span::FileName;
@@ -42,7 +42,7 @@ pub struct Compiler {
 }
 
 /// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
-pub(crate) fn parse_cfg(handler: &Handler, cfgs: Vec<String>) -> Cfg {
+pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg {
     cfgs.into_iter()
         .map(|s| {
             let sess = ParseSess::with_silent_emitter(Some(format!(
@@ -54,12 +54,11 @@ pub(crate) fn parse_cfg(handler: &Handler, cfgs: Vec<String>) -> Cfg {
                 ($reason: expr) => {
                     #[allow(rustc::untranslatable_diagnostic)]
                     #[allow(rustc::diagnostic_outside_of_impl)]
-                    handler
-                        .struct_fatal(format!(
-                            concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
-                            s
-                        ))
-                        .emit();
+                    dcx.struct_fatal(format!(
+                        concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
+                        s
+                    ))
+                    .emit();
                 };
             }
 
@@ -101,7 +100,7 @@ pub(crate) fn parse_cfg(handler: &Handler, cfgs: Vec<String>) -> Cfg {
 }
 
 /// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
-pub(crate) fn parse_check_cfg(handler: &Handler, specs: Vec<String>) -> CheckCfg {
+pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg {
     // If any --check-cfg is passed then exhaustive_values and exhaustive_names
     // are enabled by default.
     let exhaustive_names = !specs.is_empty();
@@ -118,12 +117,11 @@ pub(crate) fn parse_check_cfg(handler: &Handler, specs: Vec<String>) -> CheckCfg
             ($reason:expr) => {
                 #[allow(rustc::untranslatable_diagnostic)]
                 #[allow(rustc::diagnostic_outside_of_impl)]
-                handler
-                    .struct_fatal(format!(
-                        concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
-                        s
-                    ))
-                    .emit()
+                dcx.struct_fatal(format!(
+                    concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
+                    s
+                ))
+                .emit()
             };
         }
 
@@ -317,8 +315,8 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
     rustc_data_structures::sync::set_dyn_thread_safe_mode(config.opts.unstable_opts.threads > 1);
 
     // Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread
-    let early_handler = EarlyErrorHandler::new(config.opts.error_format);
-    early_handler.initialize_checked_jobserver();
+    let early_dcx = EarlyDiagCtxt::new(config.opts.error_format);
+    early_dcx.initialize_checked_jobserver();
 
     util::run_in_thread_pool_with_globals(
         config.opts.edition,
@@ -326,13 +324,13 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
         || {
             crate::callbacks::setup_callbacks();
 
-            let early_handler = EarlyErrorHandler::new(config.opts.error_format);
+            let early_dcx = EarlyDiagCtxt::new(config.opts.error_format);
 
             let codegen_backend = if let Some(make_codegen_backend) = config.make_codegen_backend {
                 make_codegen_backend(&config.opts)
             } else {
                 util::get_codegen_backend(
-                    &early_handler,
+                    &early_dcx,
                     &config.opts.maybe_sysroot,
                     config.opts.unstable_opts.codegen_backend.as_deref(),
                 )
@@ -349,7 +347,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             ) {
                 Ok(bundle) => bundle,
                 Err(e) => {
-                    early_handler.early_error(format!("failed to load fluent bundle: {e}"));
+                    early_dcx.early_error(format!("failed to load fluent bundle: {e}"));
                 }
             };
 
@@ -360,7 +358,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             let target_override = codegen_backend.target_override(&config.opts);
 
             let mut sess = rustc_session::build_session(
-                early_handler,
+                early_dcx,
                 config.opts,
                 CompilerIO {
                     input: config.input,
@@ -382,12 +380,12 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
 
             codegen_backend.init(&sess);
 
-            let cfg = parse_cfg(&sess.diagnostic(), config.crate_cfg);
+            let cfg = parse_cfg(&sess.dcx(), config.crate_cfg);
             let mut cfg = config::build_configuration(&sess, cfg);
             util::add_configuration(&mut cfg, &mut sess, &*codegen_backend);
             sess.parse_sess.config = cfg;
 
-            let mut check_cfg = parse_check_cfg(&sess.diagnostic(), config.crate_check_cfg);
+            let mut check_cfg = parse_check_cfg(&sess.dcx(), config.crate_check_cfg);
             check_cfg.fill_well_known(&sess.target);
             sess.parse_sess.check_config = check_cfg;
 
@@ -433,21 +431,21 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
 }
 
 pub fn try_print_query_stack(
-    handler: &Handler,
+    dcx: &DiagCtxt,
     num_frames: Option<usize>,
     file: Option<std::fs::File>,
 ) {
     eprintln!("query stack during panic:");
 
     // Be careful relying on global state here: this code is called from
-    // a panic hook, which means that the global `Handler` may be in a weird
+    // a panic hook, which means that the global `DiagCtxt` may be in a weird
     // state if it was responsible for triggering the panic.
     let i = ty::tls::with_context_opt(|icx| {
         if let Some(icx) = icx {
             ty::print::with_no_queries!(print_query_stack(
                 QueryCtxt::new(icx.tcx),
                 icx.query,
-                handler,
+                dcx,
                 num_frames,
                 file,
             ))
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 9b59ead0463..21beb90d73f 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -56,7 +56,7 @@ pub fn parse<'a>(sess: &'a Session) -> PResult<'a, ast::Crate> {
     }
 
     if let Some(ref s) = sess.opts.unstable_opts.show_span {
-        rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate);
+        rustc_ast_passes::show_span::run(sess.dcx(), s, &krate);
     }
 
     if sess.opts.unstable_opts.hir_stats {
@@ -267,7 +267,7 @@ fn configure_and_expand(
             is_proc_macro_crate,
             has_proc_macro_decls,
             is_test_crate,
-            sess.diagnostic(),
+            sess.dcx(),
         )
     });
 
@@ -526,7 +526,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
     match result {
         Ok(_) => {
             if sess.opts.json_artifact_notifications {
-                sess.diagnostic().emit_artifact_notification(deps_filename, "dep-info");
+                sess.dcx().emit_artifact_notification(deps_filename, "dep-info");
             }
         }
         Err(error) => {
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index e9611c74a68..8a553b95e8e 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -228,7 +228,7 @@ impl<'tcx> Queries<'tcx> {
 
             // If we have any delayed bugs, for example because we created TyKind::Error earlier,
             // it's likely that codegen will only cause more ICEs, obscuring the original problem
-            self.compiler.sess.diagnostic().flush_delayed();
+            self.compiler.sess.dcx().flush_delayed();
 
             // Hook for UI tests.
             Self::check_for_rustc_errors_attr(tcx);
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index d96b1ba88f5..04a7714d413 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -13,7 +13,7 @@ use rustc_session::config::{
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
 use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
-use rustc_session::{build_session, getopts, CompilerIO, EarlyErrorHandler, Session};
+use rustc_session::{build_session, getopts, CompilerIO, EarlyDiagCtxt, Session};
 use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::symbol::sym;
 use rustc_span::{FileName, SourceFileHashAlgorithm};
@@ -25,11 +25,11 @@ use std::path::{Path, PathBuf};
 use std::sync::Arc;
 
 fn mk_session(matches: getopts::Matches) -> (Session, Cfg) {
-    let mut early_handler = EarlyErrorHandler::new(ErrorOutputType::default());
-    early_handler.initialize_checked_jobserver();
+    let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
+    early_dcx.initialize_checked_jobserver();
 
     let registry = registry::Registry::new(&[]);
-    let sessopts = build_session_options(&mut early_handler, &matches);
+    let sessopts = build_session_options(&mut early_dcx, &matches);
     let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
     let io = CompilerIO {
         input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
@@ -38,7 +38,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, Cfg) {
         temps_dir,
     };
     let sess = build_session(
-        early_handler,
+        early_dcx,
         sessopts,
         io,
         None,
@@ -52,7 +52,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, Cfg) {
         Arc::default(),
         Default::default(),
     );
-    let cfg = parse_cfg(&sess.diagnostic(), matches.opt_strs("cfg"));
+    let cfg = parse_cfg(&sess.dcx(), matches.opt_strs("cfg"));
     (sess, cfg)
 }
 
@@ -143,20 +143,20 @@ fn test_can_print_warnings() {
     rustc_span::create_default_session_globals_then(|| {
         let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
         let (sess, _) = mk_session(matches);
-        assert!(!sess.diagnostic().can_emit_warnings());
+        assert!(!sess.dcx().can_emit_warnings());
     });
 
     rustc_span::create_default_session_globals_then(|| {
         let matches =
             optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
         let (sess, _) = mk_session(matches);
-        assert!(sess.diagnostic().can_emit_warnings());
+        assert!(sess.dcx().can_emit_warnings());
     });
 
     rustc_span::create_default_session_globals_then(|| {
         let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
         let (sess, _) = mk_session(matches);
-        assert!(sess.diagnostic().can_emit_warnings());
+        assert!(sess.dcx().can_emit_warnings());
     });
 }
 
@@ -301,36 +301,36 @@ fn test_search_paths_tracking_hash_different_order() {
     let mut v3 = Options::default();
     let mut v4 = Options::default();
 
-    let handler = EarlyErrorHandler::new(JSON);
+    let early_dcx = EarlyDiagCtxt::new(JSON);
     const JSON: ErrorOutputType = ErrorOutputType::Json {
         pretty: false,
         json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
     };
 
     // Reference
-    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
-
-    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
-
-    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
-
-    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
+
+    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
+
+    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
+
+    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
 
     assert_same_hash(&v1, &v2);
     assert_same_hash(&v1, &v3);
@@ -854,9 +854,9 @@ fn test_edition_parsing() {
     let options = Options::default();
     assert!(options.edition == DEFAULT_EDITION);
 
-    let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+    let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
     let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap();
-    let sessopts = build_session_options(&mut handler, &matches);
+    let sessopts = build_session_options(&mut early_dcx, &matches);
     assert!(sessopts.edition == Edition::Edition2018)
 }
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index c9c7ffdd937..bffa743218f 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -14,7 +14,7 @@ use rustc_session::{filesearch, output, Session};
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
 use rustc_span::symbol::{sym, Symbol};
-use session::EarlyErrorHandler;
+use session::EarlyDiagCtxt;
 use std::env;
 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
 use std::mem;
@@ -161,16 +161,16 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
     })
 }
 
-fn load_backend_from_dylib(handler: &EarlyErrorHandler, path: &Path) -> MakeBackendFn {
+fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
     let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
         let err = format!("couldn't load codegen backend {path:?}: {err}");
-        handler.early_error(err);
+        early_dcx.early_error(err);
     });
 
     let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
         .unwrap_or_else(|e| {
             let err = format!("couldn't load codegen backend: {e}");
-            handler.early_error(err);
+            early_dcx.early_error(err);
         });
 
     // Intentionally leak the dynamic library. We can't ever unload it
@@ -185,7 +185,7 @@ fn load_backend_from_dylib(handler: &EarlyErrorHandler, path: &Path) -> MakeBack
 ///
 /// A name of `None` indicates that the default backend should be used.
 pub fn get_codegen_backend(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     maybe_sysroot: &Option<PathBuf>,
     backend_name: Option<&str>,
 ) -> Box<dyn CodegenBackend> {
@@ -196,11 +196,11 @@ pub fn get_codegen_backend(
 
         match backend_name.unwrap_or(default_codegen_backend) {
             filename if filename.contains('.') => {
-                load_backend_from_dylib(handler, filename.as_ref())
+                load_backend_from_dylib(early_dcx, filename.as_ref())
             }
             #[cfg(feature = "llvm")]
             "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new,
-            backend_name => get_codegen_sysroot(handler, maybe_sysroot, backend_name),
+            backend_name => get_codegen_sysroot(early_dcx, maybe_sysroot, backend_name),
         }
     });
 
@@ -233,7 +233,7 @@ fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
 }
 
 fn get_codegen_sysroot(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     maybe_sysroot: &Option<PathBuf>,
     backend_name: &str,
 ) -> MakeBackendFn {
@@ -271,7 +271,7 @@ fn get_codegen_sysroot(
             "failed to find a `codegen-backends` folder \
                            in the sysroot candidates:\n* {candidates}"
         );
-        handler.early_error(err);
+        early_dcx.early_error(err);
     });
     info!("probing {} for a codegen backend", sysroot.display());
 
@@ -282,7 +282,7 @@ fn get_codegen_sysroot(
             sysroot.display(),
             e
         );
-        handler.early_error(err);
+        early_dcx.early_error(err);
     });
 
     let mut file: Option<PathBuf> = None;
@@ -310,16 +310,16 @@ fn get_codegen_sysroot(
                 prev.display(),
                 path.display()
             );
-            handler.early_error(err);
+            early_dcx.early_error(err);
         }
         file = Some(path.clone());
     }
 
     match file {
-        Some(ref s) => load_backend_from_dylib(handler, s),
+        Some(ref s) => load_backend_from_dylib(early_dcx, s),
         None => {
             let err = format!("unsupported builtin codegen backend `{backend_name}`");
-            handler.early_error(err);
+            early_dcx.early_error(err);
         }
     }
 }
diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs
index 249126a269e..abec12f52a6 100644
--- a/compiler/rustc_lexer/src/unescape.rs
+++ b/compiler/rustc_lexer/src/unescape.rs
@@ -92,8 +92,8 @@ where
             let res = unescape_char_or_byte(&mut chars, mode);
             callback(0..(src.len() - chars.as_str().len()), res);
         }
-        Str | ByteStr => unescape_str_common(src, mode, callback),
-        RawStr | RawByteStr => unescape_raw_str_or_raw_byte_str(src, mode, callback),
+        Str | ByteStr => unescape_non_raw_common(src, mode, callback),
+        RawStr | RawByteStr => check_raw_common(src, mode, callback),
         CStr | RawCStr => unreachable!(),
     }
 }
@@ -122,12 +122,10 @@ where
 {
     match mode {
         CStr => {
-            unescape_str_common(src, mode, callback);
+            unescape_non_raw_common(src, mode, callback);
         }
         RawCStr => {
-            unescape_raw_str_or_raw_byte_str(src, mode, &mut |r, result| {
-                callback(r, result.map(CStrUnit::Char))
-            });
+            check_raw_common(src, mode, &mut |r, result| callback(r, result.map(CStrUnit::Char)));
         }
         Char | Byte | Str | RawStr | ByteStr | RawByteStr => unreachable!(),
     }
@@ -191,8 +189,9 @@ impl Mode {
     /// Byte literals do not allow unicode escape.
     fn is_unicode_escape_disallowed(self) -> bool {
         match self {
-            Byte | ByteStr | RawByteStr => true,
-            Char | Str | RawStr | CStr | RawCStr => false,
+            Byte | ByteStr => true,
+            Char | Str | CStr => false,
+            RawByteStr | RawStr | RawCStr => unreachable!(),
         }
     }
 
@@ -324,7 +323,7 @@ fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result<char, Esca
 
 /// Takes a contents of a string literal (without quotes) and produces a
 /// sequence of escaped characters or errors.
-fn unescape_str_common<F, T: From<u8> + From<char>>(src: &str, mode: Mode, callback: &mut F)
+fn unescape_non_raw_common<F, T: From<u8> + From<char>>(src: &str, mode: Mode, callback: &mut F)
 where
     F: FnMut(Range<usize>, Result<T, EscapeError>),
 {
@@ -391,7 +390,7 @@ where
 /// sequence of characters or errors.
 /// NOTE: Raw strings do not perform any explicit character escaping, here we
 /// only produce errors on bare CR.
-fn unescape_raw_str_or_raw_byte_str<F>(src: &str, mode: Mode, callback: &mut F)
+fn check_raw_common<F>(src: &str, mode: Mode, callback: &mut F)
 where
     F: FnMut(Range<usize>, Result<char, EscapeError>),
 {
@@ -399,7 +398,7 @@ where
     let chars_should_be_ascii = mode.chars_should_be_ascii(); // get this outside the loop
 
     // The `start` and `end` computation here matches the one in
-    // `unescape_str_common` for consistency, even though this function
+    // `unescape_non_raw_common` for consistency, even though this function
     // doesn't have to worry about skipping any chars.
     while let Some(c) = chars.next() {
         let start = src.len() - chars.as_str().len() - c.len_utf8();
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 32eb67ba3a1..045ff38c056 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2800,7 +2800,7 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
                             NAMED_ASM_LABELS,
                             Some(target_spans),
                             fluent::lint_builtin_asm_labels,
-                            |lint| lint,
+                            |_| {},
                             BuiltinLintDiagnostics::NamedAsmLabel(
                                 "only local labels of the form `<number>:` should be used in inline asm"
                                     .to_string(),
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 0911fa70f97..c7a9d5e80ac 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -520,9 +520,6 @@ pub trait LintContext {
     /// Emit a lint at the appropriate level, with an optional associated span and an existing
     /// diagnostic.
     ///
-    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed
-    /// explanation.
-    ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     #[rustc_lint_diagnostics]
     fn lookup_with_diagnostics(
@@ -530,9 +527,7 @@ pub trait LintContext {
         lint: &'static Lint,
         span: Option<impl Into<MultiSpan>>,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
         diagnostic: BuiltinLintDiagnostics,
     ) {
         // We first generate a blank diagnostic.
@@ -986,8 +981,6 @@ pub trait LintContext {
     // set the span in their `decorate` function (preferably using set_span).
     /// Emit a lint at the appropriate level, with an optional associated span.
     ///
-    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
-    ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     #[rustc_lint_diagnostics]
     fn lookup<S: Into<MultiSpan>>(
@@ -995,9 +988,7 @@ pub trait LintContext {
         lint: &'static Lint,
         span: Option<S>,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     );
 
     /// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`,
@@ -1008,13 +999,13 @@ pub trait LintContext {
         span: S,
         decorator: impl for<'a> DecorateLint<'a, ()>,
     ) {
-        self.lookup(lint, Some(span), decorator.msg(), |diag| decorator.decorate_lint(diag));
+        self.lookup(lint, Some(span), decorator.msg(), |diag| {
+            decorator.decorate_lint(diag);
+        });
     }
 
     /// Emit a lint at the appropriate level, with an associated span.
     ///
-    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
-    ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     #[rustc_lint_diagnostics]
     fn struct_span_lint<S: Into<MultiSpan>>(
@@ -1022,9 +1013,7 @@ pub trait LintContext {
         lint: &'static Lint,
         span: S,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         self.lookup(lint, Some(span), msg, decorate);
     }
@@ -1033,23 +1022,19 @@ pub trait LintContext {
     /// generated by `#[derive(LintDiagnostic)]`).
     fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> DecorateLint<'a, ()>) {
         self.lookup(lint, None as Option<Span>, decorator.msg(), |diag| {
-            decorator.decorate_lint(diag)
+            decorator.decorate_lint(diag);
         });
     }
 
     /// Emit a lint at the appropriate level, with no associated span.
     ///
-    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
-    ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     #[rustc_lint_diagnostics]
     fn lint(
         &self,
         lint: &'static Lint,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         self.lookup(lint, None as Option<Span>, msg, decorate);
     }
@@ -1113,9 +1098,7 @@ impl<'tcx> LintContext for LateContext<'tcx> {
         lint: &'static Lint,
         span: Option<S>,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         let hir_id = self.last_node_with_lint_attrs;
 
@@ -1142,9 +1125,7 @@ impl LintContext for EarlyContext<'_> {
         lint: &'static Lint,
         span: Option<S>,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         self.builder.struct_lint(lint, span.map(|s| s.into()), msg, decorate)
     }
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 4c7f9eeff8c..b9add9e9f06 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -45,13 +45,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
     fn inlined_check_id(&mut self, id: ast::NodeId) {
         for early_lint in self.context.buffered.take(id) {
             let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;
-            self.context.lookup_with_diagnostics(
-                lint_id.lint,
-                Some(span),
-                msg,
-                |lint| lint,
-                diagnostic,
-            );
+            self.context.lookup_with_diagnostics(lint_id.lint, Some(span), msg, |_| {}, diagnostic);
         }
     }
 
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index 047a214a8b2..5dcc1bce5ff 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -16,7 +16,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
     }
 
     let lint_expectations = tcx.lint_expectations(());
-    let fulfilled_expectations = tcx.sess.diagnostic().steal_fulfilled_expectation_ids();
+    let fulfilled_expectations = tcx.sess.dcx().steal_fulfilled_expectation_ids();
 
     tracing::debug!(?lint_expectations, ?fulfilled_expectations);
 
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index c1d4ed37627..6eff2bfe13c 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -144,7 +144,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp
     builder.add_id(hir::CRATE_HIR_ID);
     tcx.hir().walk_toplevel_module(&mut builder);
 
-    tcx.sess.diagnostic().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids);
+    tcx.sess.dcx().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids);
 
     builder.provider.expectations
 }
@@ -1077,7 +1077,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                             GateIssue::Language,
                             lint_from_cli,
                         );
-                        lint
                     },
                 );
                 return false;
@@ -1094,8 +1093,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     /// Used to emit a lint-related diagnostic based on the current state of
     /// this lint context.
     ///
-    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
-    ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -1104,9 +1101,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         lint: &'static Lint,
         span: Option<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         let (level, src) = self.lint_level(lint);
         struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
@@ -1121,7 +1116,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     ) {
         let (level, src) = self.lint_level(lint);
         struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
-            decorate.decorate_lint(lint)
+            decorate.decorate_lint(lint);
         });
     }
 
@@ -1129,7 +1124,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) {
         let (level, src) = self.lint_level(lint);
         struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
-            decorate.decorate_lint(lint)
+            decorate.decorate_lint(lint);
         });
     }
 }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 83414ee702f..a4ab5527e3f 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -509,6 +509,11 @@ fn register_builtins(store: &mut LintStore) {
         "converted into hard error, see PR #104616 \
          <https://github.com/rust-lang/rust/pull/104616> for more information",
     );
+    store.register_removed(
+        "implied_bounds_entailment",
+        "converted into hard error, see PR #117984 \
+        <https://github.com/rust-lang/rust/pull/117984> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index d0b895f7354..ca6408bdf3d 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -134,12 +134,8 @@ pub struct BuiltinMissingDebugImpl<'a> {
 
 // Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for BuiltinMissingDebugImpl<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.set_arg("debug", self.tcx.def_path_str(self.def_id));
-        diag
     }
 
     fn msg(&self) -> DiagnosticMessage {
@@ -243,17 +239,13 @@ pub struct BuiltinUngatedAsyncFnTrackCaller<'a> {
 }
 
 impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.span_label(self.label, fluent::lint_label);
         rustc_session::parse::add_feature_diagnostics(
             diag,
             self.parse_sess,
             sym::async_fn_track_caller,
         );
-        diag
     }
 
     fn msg(&self) -> DiagnosticMessage {
@@ -433,10 +425,7 @@ pub struct BuiltinUnpermittedTypeInit<'a> {
 }
 
 impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.set_arg("ty", self.ty);
         diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label);
         if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) {
@@ -447,7 +436,6 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
             );
         }
         self.sub.add_to_diagnostic(diag);
-        diag
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
@@ -1159,10 +1147,7 @@ pub struct NonFmtPanicUnused {
 
 // Used because of two suggestions based on one Option<Span>
 impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.set_arg("count", self.count);
         diag.note(fluent::lint_note);
         if let Some(span) = self.suggestion {
@@ -1179,7 +1164,6 @@ impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
                 Applicability::MachineApplicable,
             );
         }
-        diag
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
@@ -1358,12 +1342,9 @@ pub struct DropTraitConstraintsDiag<'a> {
 
 // Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for DropTraitConstraintsDiag<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.set_arg("predicate", self.predicate);
-        diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id))
+        diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id));
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
@@ -1378,11 +1359,8 @@ pub struct DropGlue<'a> {
 
 // Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for DropGlue<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id))
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
+        diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id));
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
@@ -1655,10 +1633,7 @@ pub struct ImproperCTypes<'a> {
 
 // Used because of the complexity of Option<DiagnosticMessage>, DiagnosticMessage, and Option<Span>
 impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.set_arg("ty", self.ty);
         diag.set_arg("desc", self.desc);
         diag.span_label(self.label, fluent::lint_label);
@@ -1669,7 +1644,6 @@ impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
         if let Some(note) = self.span_note {
             diag.span_note(note, fluent::lint_note);
         }
-        diag
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
@@ -1802,10 +1776,7 @@ pub enum UnusedDefSuggestion {
 
 // Needed because of def_path_str
 impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.set_arg("pre", self.pre);
         diag.set_arg("post", self.post);
         diag.set_arg("def", self.cx.tcx.def_path_str(self.def_id));
@@ -1816,7 +1787,6 @@ impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
         if let Some(sugg) = self.suggestion {
             diag.subdiagnostic(sugg);
         }
-        diag
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
@@ -1889,15 +1859,11 @@ pub struct AsyncFnInTraitDiag {
 }
 
 impl<'a> DecorateLint<'a, ()> for AsyncFnInTraitDiag {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.note(fluent::lint_note);
         if let Some(sugg) = self.sugg {
             diag.multipart_suggestion(fluent::lint_suggestion, sugg, Applicability::MaybeIncorrect);
         }
-        diag
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index f0e415492ae..9fcd70ba0b5 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -126,7 +126,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
         lint.note(fluent::lint_more_info_note);
         if !is_arg_inside_call(arg_span, span) {
             // No clue where this argument is coming from.
-            return lint;
+            return;
         }
         if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
             // A case of `panic!(format!(..))`.
@@ -207,7 +207,6 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 }
             }
         }
-        lint
     });
 }
 
diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs
index 82483ac7dc0..96290288f07 100644
--- a/compiler/rustc_lint/src/reference_casting.rs
+++ b/compiler/rustc_lint/src/reference_casting.rs
@@ -37,59 +37,73 @@ declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
 
 impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
-            return;
-        };
-
-        let init = cx.expr_or_init(e);
-
-        let Some(ty_has_interior_mutability) = is_cast_from_const_to_mut(cx, init) else {
-            return;
-        };
-        let orig_cast = if init.span != e.span { Some(init.span) } else { None };
-        let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());
-
-        cx.emit_spanned_lint(
-            INVALID_REFERENCE_CASTING,
-            expr.span,
-            if is_assignment {
-                InvalidReferenceCastingDiag::AssignToRef { orig_cast, ty_has_interior_mutability }
-            } else {
-                InvalidReferenceCastingDiag::BorrowAsMut { orig_cast, ty_has_interior_mutability }
-            },
-        );
+        if let Some((e, pat)) = borrow_or_assign(cx, expr) {
+            if matches!(pat, PatternKind::Borrow { mutbl: Mutability::Mut } | PatternKind::Assign) {
+                let init = cx.expr_or_init(e);
+
+                let Some(ty_has_interior_mutability) = is_cast_from_ref_to_mut_ptr(cx, init) else {
+                    return;
+                };
+                let orig_cast = if init.span != e.span { Some(init.span) } else { None };
+                let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());
+
+                cx.emit_spanned_lint(
+                    INVALID_REFERENCE_CASTING,
+                    expr.span,
+                    if pat == PatternKind::Assign {
+                        InvalidReferenceCastingDiag::AssignToRef {
+                            orig_cast,
+                            ty_has_interior_mutability,
+                        }
+                    } else {
+                        InvalidReferenceCastingDiag::BorrowAsMut {
+                            orig_cast,
+                            ty_has_interior_mutability,
+                        }
+                    },
+                );
+            }
+        }
     }
 }
 
-fn is_operation_we_care_about<'tcx>(
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum PatternKind {
+    Borrow { mutbl: Mutability },
+    Assign,
+}
+
+fn borrow_or_assign<'tcx>(
     cx: &LateContext<'tcx>,
     e: &'tcx Expr<'tcx>,
-) -> Option<(bool, &'tcx Expr<'tcx>)> {
-    fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
-        // &mut <expr>
-        let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
-            expr
+) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
+    fn deref_assign_or_addr_of<'tcx>(
+        expr: &'tcx Expr<'tcx>,
+    ) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
+        // &(mut) <expr>
+        let (inner, pat) = if let ExprKind::AddrOf(_, mutbl, expr) = expr.kind {
+            (expr, PatternKind::Borrow { mutbl })
         // <expr> = ...
         } else if let ExprKind::Assign(expr, _, _) = expr.kind {
-            expr
+            (expr, PatternKind::Assign)
         // <expr> += ...
         } else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
-            expr
+            (expr, PatternKind::Assign)
         } else {
             return None;
         };
 
-        if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
-            Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
-        } else {
-            None
-        }
+        // *<inner>
+        let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
+            return None;
+        };
+        Some((e, pat))
     }
 
     fn ptr_write<'tcx>(
         cx: &LateContext<'tcx>,
         e: &'tcx Expr<'tcx>,
-    ) -> Option<(bool, &'tcx Expr<'tcx>)> {
+    ) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
         if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
             && let ExprKind::Path(ref qpath) = path.kind
             && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
@@ -98,7 +112,7 @@ fn is_operation_we_care_about<'tcx>(
                 Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned)
             )
         {
-            Some((true, arg_ptr))
+            Some((arg_ptr, PatternKind::Assign))
         } else {
             None
         }
@@ -107,13 +121,10 @@ fn is_operation_we_care_about<'tcx>(
     deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
 }
 
-fn is_cast_from_const_to_mut<'tcx>(
+fn is_cast_from_ref_to_mut_ptr<'tcx>(
     cx: &LateContext<'tcx>,
     orig_expr: &'tcx Expr<'tcx>,
 ) -> Option<bool> {
-    let mut need_check_freeze = false;
-    let mut e = orig_expr;
-
     let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
 
     // Bail out early if the end type is **not** a mutable pointer.
@@ -121,6 +132,28 @@ fn is_cast_from_const_to_mut<'tcx>(
         return None;
     }
 
+    let (e, need_check_freeze) = peel_casts(cx, orig_expr);
+
+    let start_ty = cx.typeck_results().node_type(e.hir_id);
+    if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
+        // If an UnsafeCell method is involved, we need to additionally check the
+        // inner type for the presence of the Freeze trait (ie does NOT contain
+        // an UnsafeCell), since in that case we would incorrectly lint on valid casts.
+        //
+        // Except on the presence of non concrete skeleton types (ie generics)
+        // since there is no way to make it safe for arbitrary types.
+        let inner_ty_has_interior_mutability =
+            !inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
+        (!need_check_freeze || !inner_ty_has_interior_mutability)
+            .then_some(inner_ty_has_interior_mutability)
+    } else {
+        None
+    }
+}
+
+fn peel_casts<'tcx>(cx: &LateContext<'tcx>, mut e: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
+    let mut gone_trough_unsafe_cell_raw_get = false;
+
     loop {
         e = e.peel_blocks();
         // <expr> as ...
@@ -145,27 +178,18 @@ fn is_cast_from_const_to_mut<'tcx>(
             )
         {
             if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
-                need_check_freeze = true;
+                gone_trough_unsafe_cell_raw_get = true;
             }
             arg
         } else {
-            break;
+            let init = cx.expr_or_init(e);
+            if init.hir_id != e.hir_id {
+                init
+            } else {
+                break;
+            }
         };
     }
 
-    let start_ty = cx.typeck_results().node_type(e.hir_id);
-    if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
-        // If an UnsafeCell method is involved we need to additionally check the
-        // inner type for the presence of the Freeze trait (ie does NOT contain
-        // an UnsafeCell), since in that case we would incorrectly lint on valid casts.
-        //
-        // We also consider non concrete skeleton types (ie generics)
-        // to be an issue since there is no way to make it safe for abitrary types.
-        let inner_ty_has_interior_mutability =
-            !inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
-        (!need_check_freeze || !inner_ty_has_interior_mutability)
-            .then_some(inner_ty_has_interior_mutability)
-    } else {
-        None
-    }
+    (e, gone_trough_unsafe_cell_raw_get)
 }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index e6e445c54b1..f9b66239bf9 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -47,7 +47,6 @@ declare_lint_pass! {
         HIDDEN_GLOB_REEXPORTS,
         ILL_FORMED_ATTRIBUTE_INPUT,
         ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
-        IMPLIED_BOUNDS_ENTAILMENT,
         INCOMPLETE_INCLUDE,
         INDIRECT_STRUCTURAL_MATCH,
         INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
@@ -4236,47 +4235,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `implied_bounds_entailment` lint detects cases where the arguments of an impl method
-    /// have stronger implied bounds than those from the trait method it's implementing.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,compile_fail
-    /// #![deny(implied_bounds_entailment)]
-    ///
-    /// trait Trait {
-    ///     fn get<'s>(s: &'s str, _: &'static &'static ()) -> &'static str;
-    /// }
-    ///
-    /// impl Trait for () {
-    ///     fn get<'s>(s: &'s str, _: &'static &'s ()) -> &'static str {
-    ///         s
-    ///     }
-    /// }
-    ///
-    /// let val = <() as Trait>::get(&String::from("blah blah blah"), &&());
-    /// println!("{}", val);
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// Neither the trait method, which provides no implied bounds about `'s`, nor the impl,
-    /// requires the main function to prove that 's: 'static, but the impl method is allowed
-    /// to assume that `'s: 'static` within its own body.
-    ///
-    /// This can be used to implement an unsound API if used incorrectly.
-    pub IMPLIED_BOUNDS_ENTAILMENT,
-    Deny,
-    "impl method assumes more implied bounds than its corresponding trait method",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
-        reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
-    };
-}
-
-declare_lint! {
     /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field
     /// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or
     /// more built-in traits.
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
index d61ec0b641c..373bc5cc581 100644
--- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -139,6 +139,9 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
            RustMappingRegions, NumMappingRegions)) {
     MappingRegions.emplace_back(
         fromRust(Region.Count), fromRust(Region.FalseCount),
+#if LLVM_VERSION_GE(18, 0)
+        coverage::CounterMappingRegion::MCDCParameters{},
+#endif
         Region.FileID, Region.ExpandedFileID,
         Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
         fromRust(Region.Kind));
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 556dc890a84..cf3f526400d 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -468,6 +468,14 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
     // it prevents control flow from "falling through" into whatever code
     // happens to be laid out next in memory.
     Options.TrapUnreachable = true;
+    // But don't emit traps after other traps or no-returns unnecessarily.
+    // ...except for when targeting WebAssembly, because the NoTrapAfterNoreturn
+    // option causes bugs in the LLVM WebAssembly backend. You should be able to
+    // remove this check when Rust's minimum supported LLVM version is >= 18
+    // https://github.com/llvm/llvm-project/pull/65876
+    if (!Trip.isWasm()) {
+      Options.NoTrapAfterNoreturn = true;
+    }
   }
 
   if (Singlethread) {
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 31ad9cdb216..5de0203fc1d 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -17,11 +17,11 @@ pub(crate) struct DiagnosticDerive<'a> {
 }
 
 impl<'a> DiagnosticDerive<'a> {
-    pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
+    pub(crate) fn new(diag: syn::Ident, dcx: syn::Ident, structure: Structure<'a>) -> Self {
         Self {
             builder: DiagnosticDeriveBuilder {
                 diag,
-                kind: DiagnosticDeriveKind::Diagnostic { handler },
+                kind: DiagnosticDeriveKind::Diagnostic { dcx },
             },
             structure,
         }
@@ -36,7 +36,7 @@ impl<'a> DiagnosticDerive<'a> {
             let body = builder.body(variant);
 
             let diag = &builder.parent.diag;
-            let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
+            let DiagnosticDeriveKind::Diagnostic { dcx } = &builder.parent.kind else {
                 unreachable!()
             };
             let init = match builder.slug.value_ref() {
@@ -62,7 +62,7 @@ impl<'a> DiagnosticDerive<'a> {
                 Some(slug) => {
                     slugs.borrow_mut().push(slug.clone());
                     quote! {
-                        let mut #diag = #handler.struct_diagnostic(crate::fluent_generated::#slug);
+                        let mut #diag = #dcx.struct_diagnostic(crate::fluent_generated::#slug);
                     }
                 }
             };
@@ -77,11 +77,12 @@ impl<'a> DiagnosticDerive<'a> {
             }
         });
 
-        let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
+        let DiagnosticDeriveKind::Diagnostic { dcx } = &builder.kind else { unreachable!() };
 
+        // A lifetime of `'a` causes conflicts, but `_sess` is fine.
         let mut imp = structure.gen_impl(quote! {
-            gen impl<'__diagnostic_handler_sess, G>
-                    rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
+            gen impl<'_sess, G>
+                    rustc_errors::IntoDiagnostic<'_sess, G>
                     for @Self
                 where G: rustc_errors::EmissionGuarantee
             {
@@ -89,8 +90,8 @@ impl<'a> DiagnosticDerive<'a> {
                 #[track_caller]
                 fn into_diagnostic(
                     self,
-                    #handler: &'__diagnostic_handler_sess rustc_errors::Handler
-                ) -> rustc_errors::DiagnosticBuilder<'__diagnostic_handler_sess, G> {
+                    #dcx: &'_sess rustc_errors::DiagCtxt
+                ) -> rustc_errors::DiagnosticBuilder<'_sess, G> {
                     use rustc_errors::IntoDiagnosticArg;
                     #implementation
                 }
@@ -175,9 +176,9 @@ impl<'a> LintDiagnosticDerive<'a> {
                 fn decorate_lint<'__b>(
                     self,
                     #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>
-                ) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
+                ) {
                     use rustc_errors::IntoDiagnosticArg;
-                    #implementation
+                    #implementation;
                 }
 
                 fn msg(&self) -> rustc_errors::DiagnosticMessage {
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 2755a161d91..511654d9949 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -19,7 +19,7 @@ use super::utils::SubdiagnosticVariant;
 /// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
 #[derive(Clone, PartialEq, Eq)]
 pub(crate) enum DiagnosticDeriveKind {
-    Diagnostic { handler: syn::Ident },
+    Diagnostic { dcx: syn::Ident },
     LintDiagnostic,
 }
 
@@ -348,11 +348,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             }
             (Meta::Path(_), "subdiagnostic") => {
                 if FieldInnerTy::from_type(&info.binding.ast().ty).will_iterate() {
-                    let DiagnosticDeriveKind::Diagnostic { handler } = &self.parent.kind else {
+                    let DiagnosticDeriveKind::Diagnostic { dcx } = &self.parent.kind else {
                         // No eager translation for lints.
                         return Ok(quote! { #diag.subdiagnostic(#binding); });
                     };
-                    return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); });
+                    return Ok(quote! { #diag.eager_subdiagnostic(#dcx, #binding); });
                 } else {
                     return Ok(quote! { #diag.subdiagnostic(#binding); });
                 }
@@ -376,15 +376,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                     return Ok(quote! {});
                 }
 
-                let handler = match &self.parent.kind {
-                    DiagnosticDeriveKind::Diagnostic { handler } => handler,
+                let dcx = match &self.parent.kind {
+                    DiagnosticDeriveKind::Diagnostic { dcx } => dcx,
                     DiagnosticDeriveKind::LintDiagnostic => {
                         throw_invalid_attr!(attr, |diag| {
                             diag.help("eager subdiagnostics are not supported on lints")
                         })
                     }
                 };
-                return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); });
+                return Ok(quote! { #diag.eager_subdiagnostic(#dcx, #binding); });
             }
             _ => (),
         }
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 301e3f2620d..972c84b10f4 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -267,7 +267,7 @@ impl CStore {
             let unused_externs =
                 self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
             let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();
-            tcx.sess.diagnostic().emit_unused_externs(
+            tcx.sess.dcx().emit_unused_externs(
                 level,
                 json_unused_externs.is_loud(),
                 &unused_externs,
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index edc8d8532d3..206c15edd78 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -498,9 +498,9 @@ pub(crate) struct MultipleCandidates {
 impl IntoDiagnostic<'_> for MultipleCandidates {
     fn into_diagnostic(
         self,
-        handler: &'_ rustc_errors::Handler,
+        dcx: &'_ rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::metadata_multiple_candidates);
+        let mut diag = dcx.struct_err(fluent::metadata_multiple_candidates);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("flavor", self.flavor);
         diag.code(error_code!(E0464));
@@ -597,9 +597,9 @@ impl IntoDiagnostic<'_> for InvalidMetadataFiles {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'_ rustc_errors::Handler,
+        dcx: &'_ rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::metadata_invalid_meta_files);
+        let mut diag = dcx.struct_err(fluent::metadata_invalid_meta_files);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("add_info", self.add_info);
         diag.code(error_code!(E0786));
@@ -627,9 +627,9 @@ impl IntoDiagnostic<'_> for CannotFindCrate {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'_ rustc_errors::Handler,
+        dcx: &'_ rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::metadata_cannot_find_crate);
+        let mut diag = dcx.struct_err(fluent::metadata_cannot_find_crate);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("current_crate", self.current_crate);
         diag.set_arg("add_info", self.add_info);
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index c95ef01faa7..e80afcc482e 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -91,7 +91,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
             }
         };
         if tcx.sess.opts.json_artifact_notifications {
-            tcx.sess.diagnostic().emit_artifact_notification(out_filename.as_path(), "metadata");
+            tcx.sess.dcx().emit_artifact_notification(out_filename.as_path(), "metadata");
         }
         (filename, None)
     } else {
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 598c26de23f..81f34c7b8b0 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -1142,7 +1142,10 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh {
         tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
         tcx.stable_crate_id(LOCAL_CRATE).hash_stable(&mut hcx, &mut stable_hasher);
         // Hash visibility information since it does not appear in HIR.
-        resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher);
+        // FIXME: Figure out how to remove `visibilities_for_hashing` by hashing visibilities on
+        // the fly in the resolver, storing only their accumulated hash in `ResolverGlobalCtxt`,
+        // and combining it with other hashes here.
+        resolutions.visibilities_for_hashing.hash_stable(&mut hcx, &mut stable_hasher);
         stable_hasher.finish()
     });
 
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 59593ae1c63..e544c2a26e8 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -341,13 +341,13 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> {
             Entry::Occupied(e) => {
                 let (canonical, var_values) = e.get();
                 state.var_values.extend_from_slice(var_values);
-                canonical.clone()
+                *canonical
             }
             Entry::Vacant(e) => {
                 let canonical = canonicalize_op(tcx, key, state);
                 let OriginalQueryValues { var_values, universe_map } = state;
                 assert_eq!(universe_map.len(), 1);
-                e.insert((canonical.clone(), tcx.arena.alloc_slice(var_values)));
+                e.insert((canonical, tcx.arena.alloc_slice(var_values)));
                 canonical
             }
         }
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index eada116f896..c49c4ee819c 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -253,29 +253,10 @@ pub fn explain_lint_level_source(
 /// - [`TyCtxt::struct_lint_node`]
 /// - `LintContext::lookup`
 ///
-/// ## `decorate` signature
+/// ## `decorate`
 ///
-/// The return value of `decorate` is ignored by this function. So what is the
-/// point of returning `&'b mut DiagnosticBuilder<'a, ()>`?
-///
-/// There are 2 reasons for this signature.
-///
-/// First of all, it prevents accidental use of `.emit()` -- it's clear that the
-/// builder will be later used and shouldn't be emitted right away (this is
-/// especially important because the old API expected you to call `.emit()` in
-/// the closure).
-///
-/// Second of all, it makes the most common case of adding just a single label
-/// /suggestion much nicer, since [`DiagnosticBuilder`] methods return
-/// `&mut DiagnosticBuilder`, you can just chain methods, without needed
-/// awkward `{ ...; }`:
-/// ```ignore pseudo-code
-/// struct_lint_level(
-///     ...,
-///     |lint| lint.span_label(sp, "lbl")
-///     //          ^^^^^^^^^^^^^^^^^^^^^ returns `&mut DiagnosticBuilder` by default
-/// )
-/// ```
+/// It is not intended to call `emit`/`cancel` on the `DiagnosticBuilder` passed
+/// in the `decorate` callback.
 #[track_caller]
 pub fn struct_lint_level(
     sess: &Session,
@@ -284,9 +265,7 @@ pub fn struct_lint_level(
     src: LintLevelSource,
     span: Option<MultiSpan>,
     msg: impl Into<DiagnosticMessage>,
-    decorate: impl for<'a, 'b> FnOnce(
-        &'b mut DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut DiagnosticBuilder<'a, ()>,
+    decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
 ) {
     // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
     // the "real" work.
@@ -298,12 +277,7 @@ pub fn struct_lint_level(
         src: LintLevelSource,
         span: Option<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
-        decorate: Box<
-            dyn '_
-                + for<'a, 'b> FnOnce(
-                    &'b mut DiagnosticBuilder<'a, ()>,
-                ) -> &'b mut DiagnosticBuilder<'a, ()>,
-        >,
+        decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>)>,
     ) {
         // Check for future incompatibility lints and issue a stronger warning.
         let future_incompatible = lint.future_incompatible;
@@ -334,7 +308,7 @@ pub fn struct_lint_level(
             (Level::Expect(expect_id), _) => {
                 // This case is special as we actually allow the lint itself in this context, but
                 // we can't return early like in the case for `Level::Allow` because we still
-                // need the lint diagnostic to be emitted to `rustc_error::HandlerInner`.
+                // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
                 //
                 // We can also not mark the lint expectation as fulfilled here right away, as it
                 // can still be cancelled in the decorate function. All of this means that we simply
@@ -350,11 +324,11 @@ pub fn struct_lint_level(
             (Level::Warn | Level::ForceWarn(None), Some(span)) => sess.struct_span_warn(span, ""),
             (Level::Warn | Level::ForceWarn(None), None) => sess.struct_warn(""),
             (Level::Deny | Level::Forbid, Some(span)) => {
-                let mut builder = sess.diagnostic().struct_err_lint("");
+                let mut builder = sess.dcx().struct_err_lint("");
                 builder.set_span(span);
                 builder
             }
-            (Level::Deny | Level::Forbid, None) => sess.diagnostic().struct_err_lint(""),
+            (Level::Deny | Level::Forbid, None) => sess.dcx().struct_err_lint(""),
         };
 
         err.set_is_lint();
diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs
index 9a633e04ce7..2899e629d79 100644
--- a/compiler/rustc_middle/src/middle/lang_items.rs
+++ b/compiler/rustc_middle/src/middle/lang_items.rs
@@ -36,6 +36,17 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
+    /// Given a [`ty::ClosureKind`], get the [`DefId`] of its corresponding `Fn`-family
+    /// trait, if it is defined.
+    pub fn fn_trait_kind_to_def_id(self, kind: ty::ClosureKind) -> Option<DefId> {
+        let items = self.lang_items();
+        match kind {
+            ty::ClosureKind::Fn => items.fn_trait(),
+            ty::ClosureKind::FnMut => items.fn_mut_trait(),
+            ty::ClosureKind::FnOnce => items.fn_once_trait(),
+        }
+    }
+
     /// Returns `true` if `id` is a `DefId` of [`Fn`], [`FnMut`] or [`FnOnce`] traits.
     pub fn is_fn_trait(self, id: DefId) -> bool {
         self.fn_trait_kind_from_def_id(id).is_some()
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 564a1fad14d..0cba6d5b52a 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -223,7 +223,6 @@ fn late_report_deprecation(
             let kind = tcx.def_descr(def_id);
             deprecation_suggestion(diag, kind, suggestion, method_span);
         }
-        diag
     });
 }
 
@@ -587,7 +586,7 @@ impl<'tcx> TyCtxt<'tcx> {
         unmarked: impl FnOnce(Span, DefId),
     ) -> bool {
         let soft_handler = |lint, span, msg: String| {
-            self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg, |lint| lint)
+            self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg, |_| {})
         };
         let eval_result =
             self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable);
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 34068a9da37..092b59deeff 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -113,7 +113,7 @@ impl<'tcx> TyCtxt<'tcx> {
                                 self.local_def_id_to_hir_id(local_def_id),
                                 self.def_span(ct.def),
                                 "cannot use constants which depend on generic parameters in types",
-                                |err| err,
+                                |_| {},
                             )
                         }
                     }
diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs
index ea6b44d62b0..93a9bbf64c9 100644
--- a/compiler/rustc_middle/src/mir/type_foldable.rs
+++ b/compiler/rustc_middle/src/mir/type_foldable.rs
@@ -15,7 +15,6 @@ TrivialTypeTraversalImpls! {
     UserTypeAnnotationIndex,
     BorrowKind,
     CastKind,
-    hir::Movability,
     BasicBlock,
     SwitchTargets,
     CoroutineKind,
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index e8e2907eb33..734c2b61c07 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -125,10 +125,8 @@ pub enum SelectionCandidate<'tcx> {
 
     /// This is a trait matching with a projected type as `Self`, and we found
     /// an applicable bound in the trait definition. The `usize` is an index
-    /// into the list returned by `tcx.item_bounds`. The constness is the
-    /// constness of the bound in the trait.
-    // FIXME(effects) do we need this constness
-    ProjectionCandidate(usize, ty::BoundConstness),
+    /// into the list returned by `tcx.item_bounds`.
+    ProjectionCandidate(usize),
 
     /// Implementation of a `Fn`-family trait by one of the anonymous types
     /// generated for an `||` expression.
@@ -155,7 +153,7 @@ pub enum SelectionCandidate<'tcx> {
     /// Implementation of a `Fn`-family trait by one of the anonymous
     /// types generated for a fn pointer type (e.g., `fn(int) -> int`)
     FnPointerCandidate {
-        is_const: bool,
+        fn_host_effect: ty::Const<'tcx>,
     },
 
     TraitAliasCandidate,
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index eaf5da130dd..8c29bc5a428 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -7,14 +7,13 @@ use std::fmt::Write;
 
 use crate::query::Providers;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, LangItem};
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
 use rustc_span::def_id::LocalDefIdMap;
 use rustc_span::symbol::Ident;
 use rustc_span::{Span, Symbol};
 
-use super::{Ty, TyCtxt};
+use super::TyCtxt;
 
 use self::BorrowKind::*;
 
@@ -73,72 +72,6 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis
 /// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
 pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
 
-/// Represents the various closure traits in the language. This
-/// will determine the type of the environment (`self`, in the
-/// desugaring) argument that the closure expects.
-///
-/// You can get the environment type of a closure using
-/// `tcx.closure_env_ty()`.
-#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable)]
-pub enum ClosureKind {
-    // Warning: Ordering is significant here! The ordering is chosen
-    // because the trait Fn is a subtrait of FnMut and so in turn, and
-    // hence we order it so that Fn < FnMut < FnOnce.
-    Fn,
-    FnMut,
-    FnOnce,
-}
-
-impl ClosureKind {
-    /// This is the initial value used when doing upvar inference.
-    pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
-
-    pub const fn as_str(self) -> &'static str {
-        match self {
-            ClosureKind::Fn => "Fn",
-            ClosureKind::FnMut => "FnMut",
-            ClosureKind::FnOnce => "FnOnce",
-        }
-    }
-
-    /// Returns `true` if a type that impls this closure kind
-    /// must also implement `other`.
-    pub fn extends(self, other: ty::ClosureKind) -> bool {
-        self <= other
-    }
-
-    /// Converts `self` to a [`DefId`] of the corresponding trait.
-    ///
-    /// Note: the inverse of this function is [`TyCtxt::fn_trait_kind_from_def_id`].
-    pub fn to_def_id(&self, tcx: TyCtxt<'_>) -> DefId {
-        tcx.require_lang_item(
-            match self {
-                ClosureKind::Fn => LangItem::Fn,
-                ClosureKind::FnMut => LangItem::FnMut,
-                ClosureKind::FnOnce => LangItem::FnOnce,
-            },
-            None,
-        )
-    }
-
-    /// Returns the representative scalar type for this closure kind.
-    /// See `Ty::to_opt_closure_kind` for more details.
-    pub fn to_ty<'tcx>(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        match self {
-            ClosureKind::Fn => tcx.types.i8,
-            ClosureKind::FnMut => tcx.types.i16,
-            ClosureKind::FnOnce => tcx.types.i32,
-        }
-    }
-}
-
-impl IntoDiagnosticArg for ClosureKind {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(self.as_str().into())
-    }
-}
-
 /// A composite describing a `Place` that is captured by a closure.
 #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
 #[derive(TypeFoldable, TypeVisitable)]
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index a3fa568114f..2d3ee33489e 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -59,7 +59,7 @@ impl<'tcx> Const<'tcx> {
 
     #[inline]
     pub fn kind(self) -> ConstKind<'tcx> {
-        self.0.kind.clone()
+        self.0.kind
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs
index 9d99344d5bd..310cf113b11 100644
--- a/compiler/rustc_middle/src/ty/consts/int.rs
+++ b/compiler/rustc_middle/src/ty/consts/int.rs
@@ -71,7 +71,7 @@ impl std::fmt::Debug for ConstInt {
                         (4, _) => write!(fmt, "_i32")?,
                         (8, _) => write!(fmt, "_i64")?,
                         (16, _) => write!(fmt, "_i128")?,
-                        _ => bug!(),
+                        (sz, _) => bug!("unexpected int size i{sz}"),
                     }
                 }
                 Ok(())
@@ -105,7 +105,7 @@ impl std::fmt::Debug for ConstInt {
                         (4, _) => write!(fmt, "_u32")?,
                         (8, _) => write!(fmt, "_u64")?,
                         (16, _) => write!(fmt, "_u128")?,
-                        _ => bug!(),
+                        (sz, _) => bug!("unexpected unsigned int size u{sz}"),
                     }
                 }
                 Ok(())
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 8faf5811343..6a6ed59fabf 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -28,7 +28,7 @@ use crate::ty::{
     self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind,
     ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate,
     PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid,
-    TypeAndMut, Visibility,
+    Visibility,
 };
 use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
 use rustc_ast::{self as ast, attr};
@@ -88,7 +88,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type Term = ty::Term<'tcx>;
 
     type Binder<T> = Binder<'tcx, T>;
-    type TypeAndMut = TypeAndMut<'tcx>;
     type CanonicalVars = CanonicalVarInfos<'tcx>;
 
     type Ty = Ty<'tcx>;
@@ -128,12 +127,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type CoercePredicate = ty::CoercePredicate<'tcx>;
     type ClosureKind = ty::ClosureKind;
 
-    fn ty_and_mut_to_parts(
-        TypeAndMut { ty, mutbl }: TypeAndMut<'tcx>,
-    ) -> (Self::Ty, ty::Mutability) {
-        (ty, mutbl)
-    }
-
     fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
         self.mk_canonical_var_infos(infos)
     }
@@ -2037,14 +2030,12 @@ impl<'tcx> TyCtxt<'tcx> {
         let msg = decorator.msg();
         let (level, src) = self.lint_level_at_node(lint, hir_id);
         struct_lint_level(self.sess, lint, level, src, Some(span.into()), msg, |diag| {
-            decorator.decorate_lint(diag)
+            decorator.decorate_lint(diag);
         })
     }
 
     /// Emit a lint at the appropriate level for a hir node, with an associated span.
     ///
-    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
-    ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -2054,9 +2045,7 @@ impl<'tcx> TyCtxt<'tcx> {
         hir_id: HirId,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         let (level, src) = self.lint_level_at_node(lint, hir_id);
         struct_lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate);
@@ -2071,13 +2060,13 @@ impl<'tcx> TyCtxt<'tcx> {
         id: HirId,
         decorator: impl for<'a> DecorateLint<'a, ()>,
     ) {
-        self.struct_lint_node(lint, id, decorator.msg(), |diag| decorator.decorate_lint(diag))
+        self.struct_lint_node(lint, id, decorator.msg(), |diag| {
+            decorator.decorate_lint(diag);
+        })
     }
 
     /// Emit a lint at the appropriate level for a hir node.
     ///
-    /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
-    ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -2086,9 +2075,7 @@ impl<'tcx> TyCtxt<'tcx> {
         lint: &'static Lint,
         id: HirId,
         msg: impl Into<DiagnosticMessage>,
-        decorate: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         let (level, src) = self.lint_level_at_node(lint, id);
         struct_lint_level(self.sess, lint, level, src, None, msg, decorate);
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 5fcdd5d4d2e..225dd217807 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -5,7 +5,7 @@ use crate::ty::normalize_erasing_regions::NormalizationError;
 use crate::ty::{self, ConstKind, Ty, TyCtxt, TypeVisitableExt};
 use rustc_error_messages::DiagnosticMessage;
 use rustc_errors::{
-    DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg,
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, IntoDiagnostic, IntoDiagnosticArg,
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -1273,13 +1273,13 @@ pub enum FnAbiError<'tcx> {
 }
 
 impl<'a, 'b> IntoDiagnostic<'a, !> for FnAbiError<'b> {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, !> {
         match self {
-            Self::Layout(e) => e.into_diagnostic().into_diagnostic(handler),
+            Self::Layout(e) => e.into_diagnostic().into_diagnostic(dcx),
             Self::AdjustForForeignAbi(call::AdjustForForeignAbiError::Unsupported {
                 arch,
                 abi,
-            }) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diagnostic(handler),
+            }) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diagnostic(dcx),
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 71ff7021ca5..96de9c447b6 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -75,7 +75,7 @@ pub use self::binding::BindingMode;
 pub use self::binding::BindingMode::*;
 pub use self::closure::{
     is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
-    CapturedPlace, ClosureKind, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
+    CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
     RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL,
 };
 pub use self::consts::{Const, ConstData, ConstInt, Expr, ScalarInt, UnevaluatedConst, ValTree};
@@ -152,7 +152,7 @@ pub struct ResolverOutputs {
 
 #[derive(Debug)]
 pub struct ResolverGlobalCtxt {
-    pub visibilities: FxHashMap<LocalDefId, Visibility>,
+    pub visibilities_for_hashing: Vec<(LocalDefId, Visibility)>,
     /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
     pub expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
     pub effective_visibilities: EffectiveVisibilities,
@@ -473,7 +473,7 @@ impl<'tcx> IntoKind for Ty<'tcx> {
     type Kind = TyKind<'tcx>;
 
     fn kind(self) -> TyKind<'tcx> {
-        self.kind().clone()
+        *self.kind()
     }
 }
 
@@ -1484,7 +1484,7 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
         if let Some(diag) = tcx
             .sess
-            .diagnostic()
+            .dcx()
             .steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch)
         {
             diag.cancel();
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 39adfac55ce..8e045397b0f 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1680,7 +1680,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         self.wrap_binder(&sig, |sig, cx| {
             define_scoped_cx!(cx);
 
-            p!(print(kind), "(");
+            p!(write("{kind}("));
             for (i, arg) in sig.inputs()[0].tuple_fields().iter().enumerate() {
                 if i > 0 {
                     p!(", ");
@@ -2754,6 +2754,10 @@ forward_display_to_print! {
 define_print! {
     (self, cx):
 
+    ty::TypeAndMut<'tcx> {
+        p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
+    }
+
     ty::ClauseKind<'tcx> {
         match *self {
             ty::ClauseKind::Trait(ref data) => {
@@ -2799,10 +2803,6 @@ define_print_and_forward_display! {
         p!("{{", comma_sep(self.iter()), "}}")
     }
 
-    ty::TypeAndMut<'tcx> {
-        p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
-    }
-
     ty::ExistentialTraitRef<'tcx> {
         // Use a type that can't appear in defaults of type parameters.
         let dummy_self = Ty::new_fresh(cx.tcx(),0);
@@ -2943,10 +2943,6 @@ define_print_and_forward_display! {
         }
     }
 
-    ty::ClosureKind {
-        p!(write("{}", self.as_str()))
-    }
-
     ty::Predicate<'tcx> {
         p!(print(self.kind()))
     }
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 0cff6b77eb6..1c75d73e552 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -440,7 +440,6 @@ TrivialTypeTraversalImpls! {
 // interners).
 TrivialTypeTraversalAndLiftImpls! {
     ::rustc_hir::def_id::DefId,
-    ::rustc_hir::Mutability,
     ::rustc_hir::Unsafety,
     ::rustc_target::spec::abi::Abi,
     crate::ty::ClosureKind,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 297a0be6a27..a0debc8a165 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -44,23 +44,18 @@ use rustc_type_ir::PredicateKind as IrPredicateKind;
 use rustc_type_ir::RegionKind as IrRegionKind;
 use rustc_type_ir::TyKind as IrTyKind;
 use rustc_type_ir::TyKind::*;
+use rustc_type_ir::TypeAndMut as IrTypeAndMut;
 
 use super::GenericParamDefKind;
 
-// Re-export the `TyKind` from `rustc_type_ir` here for convenience
+// Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
 #[rustc_diagnostic_item = "TyKind"]
 pub type TyKind<'tcx> = IrTyKind<TyCtxt<'tcx>>;
 pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>;
 pub type ConstKind<'tcx> = IrConstKind<TyCtxt<'tcx>>;
 pub type PredicateKind<'tcx> = IrPredicateKind<TyCtxt<'tcx>>;
 pub type ClauseKind<'tcx> = IrClauseKind<TyCtxt<'tcx>>;
-
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct TypeAndMut<'tcx> {
-    pub ty: Ty<'tcx>,
-    pub mutbl: hir::Mutability,
-}
+pub type TypeAndMut<'tcx> = IrTypeAndMut<TyCtxt<'tcx>>;
 
 #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)]
 #[derive(HashStable)]
@@ -2818,6 +2813,15 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
+    /// Inverse of [`Ty::to_opt_closure_kind`].
+    pub fn from_closure_kind(tcx: TyCtxt<'tcx>, kind: ty::ClosureKind) -> Ty<'tcx> {
+        match kind {
+            ty::ClosureKind::Fn => tcx.types.i8,
+            ty::ClosureKind::FnMut => tcx.types.i16,
+            ty::ClosureKind::FnOnce => tcx.types.i32,
+        }
+    }
+
     /// Fast path helper for testing if a type is `Sized`.
     ///
     /// Returning true means the type is known to be sized. Returning
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 8b1d33848ea..8b2b76764e6 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -421,13 +421,10 @@ impl<'tcx> TyCtxt<'tcx> {
 
         let impl_args = match *self.type_of(impl_def_id).instantiate_identity().kind() {
             ty::Adt(def_, args) if def_ == def => args,
-            _ => bug!(),
+            _ => span_bug!(self.def_span(impl_def_id), "expected ADT for self type of `Drop` impl"),
         };
 
-        let item_args = match *self.type_of(def.did()).instantiate_identity().kind() {
-            ty::Adt(def_, args) if def_ == def => args,
-            _ => bug!(),
-        };
+        let item_args = ty::GenericArgs::identity_for_item(self, def.did());
 
         let result = iter::zip(item_args, impl_args)
             .filter(|&(_, k)| {
diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs
index 21c1a93fde8..5c9dd18aeac 100644
--- a/compiler/rustc_middle/src/util/bug.rs
+++ b/compiler/rustc_middle/src/util/bug.rs
@@ -31,8 +31,8 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>(
     tls::with_opt(move |tcx| {
         let msg = format!("{location}: {args}");
         match (tcx, span) {
-            (Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, msg),
-            (Some(tcx), None) => tcx.sess.diagnostic().bug(msg),
+            (Some(tcx), Some(span)) => tcx.sess.dcx().span_bug(span, msg),
+            (Some(tcx), None) => tcx.sess.dcx().bug(msg),
             (None, _) => panic_any(msg),
         }
     })
diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs
index 8c68a58d406..ead20539e25 100644
--- a/compiler/rustc_mir_build/src/build/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/build/custom/mod.rs
@@ -92,7 +92,7 @@ pub(super) fn build_custom_mir<'tcx>(
         pctxt.parse_body(expr)?;
     };
     if let Err(err) = res {
-        tcx.sess.diagnostic().span_fatal(
+        tcx.sess.dcx().span_fatal(
             err.span,
             format!("Could not parse {}, found: {:?}", err.expected, err.item_description),
         )
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index c63c7987f5e..dae83d4b41b 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -640,7 +640,9 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -
         }
         DefKind::Closure if coroutine_kind.is_some() => {
             let coroutine_ty = tcx.type_of(def_id).instantiate_identity();
-            let ty::Coroutine(_, args, _) = coroutine_ty.kind() else { bug!() };
+            let ty::Coroutine(_, args, _) = coroutine_ty.kind() else {
+                bug!("expected type of coroutine-like closure to be a coroutine")
+            };
             let args = args.as_coroutine();
             let yield_ty = args.yield_ty();
             let return_ty = args.return_ty();
@@ -648,7 +650,9 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -
         }
         DefKind::Closure => {
             let closure_ty = tcx.type_of(def_id).instantiate_identity();
-            let ty::Closure(_, args) = closure_ty.kind() else { bug!() };
+            let ty::Closure(_, args) = closure_ty.kind() else {
+                bug!("expected type of closure to be a closure")
+            };
             let args = args.as_closure();
             let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), args.sig());
             let self_ty = match args.kind() {
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 9baae706dff..db2624cac02 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1,8 +1,8 @@
 use crate::fluent_generated as fluent;
 use rustc_errors::DiagnosticArgValue;
 use rustc_errors::{
-    error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-    Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
+    error_code, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
+    ErrorGuaranteed, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
@@ -461,8 +461,8 @@ pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
 }
 
 impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_span_err_with_code(
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = dcx.struct_span_err_with_code(
             self.span,
             fluent::mir_build_non_exhaustive_patterns_type_not_empty,
             error_code!(E0004),
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 8c4f0257da3..a776e917de5 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -1,7 +1,6 @@
 //! Construction of MIR from HIR.
 //!
 //! This crate also contains the match exhaustiveness and usefulness checking.
-#![allow(rustc::potential_query_instability)]
 #![feature(assert_matches)]
 #![feature(associated_type_bounds)]
 #![feature(box_patterns)]
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 31874b29bb3..ab88463826a 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -782,7 +782,7 @@ impl<'tcx> Cx<'tcx> {
             hir::ExprKind::Tup(fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) },
 
             hir::ExprKind::Yield(v, _) => ExprKind::Yield { value: self.mirror_expr(v) },
-            hir::ExprKind::Err(_) => unreachable!(),
+            hir::ExprKind::Err(_) => unreachable!("cannot lower a `hir::ExprKind::Err` to THIR"),
         };
 
         Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 09e0b30a5c7..792a443c908 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -9,7 +9,7 @@ use crate::errors::*;
 
 use rustc_arena::TypedArena;
 use rustc_ast::Mutability;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
@@ -948,7 +948,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
     err.note(format!("the matched value is of type `{}`", scrut_ty));
 
     if !is_empty_match {
-        let mut non_exhaustive_tys = FxHashSet::default();
+        let mut non_exhaustive_tys = FxIndexSet::default();
         // Look at the first witness.
         collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
 
@@ -1104,7 +1104,7 @@ fn joined_uncovered_patterns<'p, 'tcx>(
 fn collect_non_exhaustive_tys<'tcx>(
     cx: &MatchCheckCtxt<'_, 'tcx>,
     pat: &WitnessPat<'tcx>,
-    non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
+    non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
 ) {
     if matches!(pat.ctor(), Constructor::NonExhaustive) {
         non_exhaustive_tys.insert(pat.ty());
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 391d65b338d..a7f23772193 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -492,8 +492,9 @@ impl<'tcx> ConstToPat<'tcx> {
                 PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) }
             }
             ty::FnPtr(..) => {
-                // Valtree construction would never succeed for these, so this is unreachable.
-                unreachable!()
+                unreachable!(
+                    "Valtree construction would never succeed for FnPtr, so this is unreachable."
+                )
             }
             _ => {
                 let err = InvalidPattern { span, non_sm_ty: ty };
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index 071024a0fa3..2802f5491be 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -956,11 +956,7 @@ impl Map {
             // The local is not tracked at all, so it does not alias anything.
             return;
         };
-        let elems = place
-            .projection
-            .iter()
-            .map(|&elem| elem.try_into())
-            .chain(tail_elem.map(Ok).into_iter());
+        let elems = place.projection.iter().map(|&elem| elem.try_into()).chain(tail_elem.map(Ok));
         for elem in elems {
             // A field aliases the parent place.
             if let Some(vi) = self.places[index].value_index {
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 580cbf7a3f8..709d1fdc21a 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -13,7 +13,6 @@ use self::spans::CoverageSpans;
 
 use crate::MirPass;
 
-use rustc_data_structures::sync::Lrc;
 use rustc_middle::hir;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::coverage::*;
@@ -22,9 +21,9 @@ use rustc_middle::mir::{
     TerminatorKind,
 };
 use rustc_middle::ty::TyCtxt;
-use rustc_span::def_id::DefId;
+use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::SourceMap;
-use rustc_span::{ExpnKind, SourceFile, Span, Symbol};
+use rustc_span::{ExpnKind, Span, Symbol};
 
 /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
 /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
@@ -39,31 +38,19 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
         let mir_source = mir_body.source;
 
-        // If the InstrumentCoverage pass is called on promoted MIRs, skip them.
-        // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
-        if mir_source.promoted.is_some() {
-            trace!(
-                "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)",
-                mir_source.def_id()
-            );
-            return;
-        }
+        // This pass runs after MIR promotion, but before promoted MIR starts to
+        // be transformed, so it should never see promoted MIR.
+        assert!(mir_source.promoted.is_none());
+
+        let def_id = mir_source.def_id().expect_local();
 
-        let is_fn_like =
-            tcx.hir_node_by_def_id(mir_source.def_id().expect_local()).fn_kind().is_some();
-
-        // Only instrument functions, methods, and closures (not constants since they are evaluated
-        // at compile time by Miri).
-        // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
-        // expressions get coverage spans, we will probably have to "carve out" space for const
-        // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
-        // be tricky if const expressions have no corresponding statements in the enclosing MIR.
-        // Closures are carved out by their initial `Assign` statement.)
-        if !is_fn_like {
-            trace!("InstrumentCoverage skipped for {:?} (not an fn-like)", mir_source.def_id());
+        if !is_eligible_for_coverage(tcx, def_id) {
+            trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)");
             return;
         }
 
+        // An otherwise-eligible function is still skipped if its start block
+        // is known to be unreachable.
         match mir_body.basic_blocks[mir::START_BLOCK].terminator().kind {
             TerminatorKind::Unreachable => {
                 trace!("InstrumentCoverage skipped for unreachable `START_BLOCK`");
@@ -72,21 +59,15 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
             _ => {}
         }
 
-        let codegen_fn_attrs = tcx.codegen_fn_attrs(mir_source.def_id());
-        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
-            return;
-        }
-
-        trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
+        trace!("InstrumentCoverage starting for {def_id:?}");
         Instrumentor::new(tcx, mir_body).inject_counters();
-        trace!("InstrumentCoverage done for {:?}", mir_source.def_id());
+        trace!("InstrumentCoverage done for {def_id:?}");
     }
 }
 
 struct Instrumentor<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     mir_body: &'a mut mir::Body<'tcx>,
-    source_file: Lrc<SourceFile>,
     fn_sig_span: Span,
     body_span: Span,
     function_source_hash: u64,
@@ -96,37 +77,17 @@ struct Instrumentor<'a, 'tcx> {
 
 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
     fn new(tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
-        let source_map = tcx.sess.source_map();
-        let def_id = mir_body.source.def_id();
-        let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, def_id);
-
-        let body_span = get_body_span(tcx, hir_body, mir_body);
+        let hir_info @ ExtractedHirInfo { function_source_hash, fn_sig_span, body_span } =
+            extract_hir_info(tcx, mir_body.source.def_id().expect_local());
 
-        let source_file = source_map.lookup_source_file(body_span.lo());
-        let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
-            fn_sig.span.eq_ctxt(body_span)
-                && Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.lo()))
-        }) {
-            Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()),
-            None => body_span.shrink_to_lo(),
-        };
-
-        debug!(
-            "instrumenting {}: {:?}, fn sig span: {:?}, body span: {:?}",
-            if tcx.is_closure(def_id) { "closure" } else { "function" },
-            def_id,
-            fn_sig_span,
-            body_span
-        );
+        debug!(?hir_info, "instrumenting {:?}", mir_body.source.def_id());
 
-        let function_source_hash = hash_mir_source(tcx, hir_body);
         let basic_coverage_blocks = CoverageGraph::from_mir(mir_body);
         let coverage_counters = CoverageCounters::new(&basic_coverage_blocks);
 
         Self {
             tcx,
             mir_body,
-            source_file,
             fn_sig_span,
             body_span,
             function_source_hash,
@@ -136,17 +97,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
     }
 
     fn inject_counters(&'a mut self) {
-        let fn_sig_span = self.fn_sig_span;
-        let body_span = self.body_span;
-
         ////////////////////////////////////////////////////
         // Compute coverage spans from the `CoverageGraph`.
-        let coverage_spans = CoverageSpans::generate_coverage_spans(
+        let Some(coverage_spans) = CoverageSpans::generate_coverage_spans(
             self.mir_body,
-            fn_sig_span,
-            body_span,
+            self.fn_sig_span,
+            self.body_span,
             &self.basic_coverage_blocks,
-        );
+        ) else {
+            // No relevant spans were found in MIR, so skip instrumenting this function.
+            return;
+        };
 
         ////////////////////////////////////////////////////
         // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
@@ -177,9 +138,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         let source_map = self.tcx.sess.source_map();
         let body_span = self.body_span;
 
+        let source_file = source_map.lookup_source_file(body_span.lo());
         use rustc_session::RemapFileNameExt;
         let file_name =
-            Symbol::intern(&self.source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
+            Symbol::intern(&source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
 
         let mut mappings = Vec::new();
 
@@ -325,27 +287,75 @@ fn make_code_region(
     }
 }
 
-fn fn_sig_and_body(
-    tcx: TyCtxt<'_>,
-    def_id: DefId,
-) -> (Option<&rustc_hir::FnSig<'_>>, &rustc_hir::Body<'_>) {
+fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
+    // Only instrument functions, methods, and closures (not constants since they are evaluated
+    // at compile time by Miri).
+    // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
+    // expressions get coverage spans, we will probably have to "carve out" space for const
+    // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
+    // be tricky if const expressions have no corresponding statements in the enclosing MIR.
+    // Closures are carved out by their initial `Assign` statement.)
+    if !tcx.def_kind(def_id).is_fn_like() {
+        trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)");
+        return false;
+    }
+
+    if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
+        return false;
+    }
+
+    true
+}
+
+/// Function information extracted from HIR by the coverage instrumentor.
+#[derive(Debug)]
+struct ExtractedHirInfo {
+    function_source_hash: u64,
+    fn_sig_span: Span,
+    body_span: Span,
+}
+
+fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo {
     // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
     // to HIR for it.
-    let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
+
+    let hir_node = tcx.hir_node_by_def_id(def_id);
     let (_, fn_body_id) =
         hir::map::associated_body(hir_node).expect("HIR node is a function with body");
-    (hir_node.fn_sig(), tcx.hir().body(fn_body_id))
+    let hir_body = tcx.hir().body(fn_body_id);
+
+    let body_span = get_body_span(tcx, hir_body, def_id);
+
+    // The actual signature span is only used if it has the same context and
+    // filename as the body, and precedes the body.
+    let maybe_fn_sig_span = hir_node.fn_sig().map(|fn_sig| fn_sig.span);
+    let fn_sig_span = maybe_fn_sig_span
+        .filter(|&fn_sig_span| {
+            let source_map = tcx.sess.source_map();
+            let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo());
+
+            fn_sig_span.eq_ctxt(body_span)
+                && fn_sig_span.hi() <= body_span.lo()
+                && file_idx(fn_sig_span) == file_idx(body_span)
+        })
+        // If so, extend it to the start of the body span.
+        .map(|fn_sig_span| fn_sig_span.with_hi(body_span.lo()))
+        // Otherwise, create a dummy signature span at the start of the body.
+        .unwrap_or_else(|| body_span.shrink_to_lo());
+
+    let function_source_hash = hash_mir_source(tcx, hir_body);
+
+    ExtractedHirInfo { function_source_hash, fn_sig_span, body_span }
 }
 
 fn get_body_span<'tcx>(
     tcx: TyCtxt<'tcx>,
     hir_body: &rustc_hir::Body<'tcx>,
-    mir_body: &mut mir::Body<'tcx>,
+    def_id: LocalDefId,
 ) -> Span {
     let mut body_span = hir_body.value.span;
-    let def_id = mir_body.source.def_id();
 
-    if tcx.is_closure(def_id) {
+    if tcx.is_closure(def_id.to_def_id()) {
         // If the MIR function is a closure, and if the closure body span
         // starts from a macro, but it's content is not in that macro, try
         // to find a non-macro callsite, and instrument the spans there
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 05ad14f1525..462e54c386c 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -15,12 +15,16 @@ pub(super) struct CoverageSpans {
 }
 
 impl CoverageSpans {
+    /// Extracts coverage-relevant spans from MIR, and associates them with
+    /// their corresponding BCBs.
+    ///
+    /// Returns `None` if no coverage-relevant spans could be extracted.
     pub(super) fn generate_coverage_spans(
         mir_body: &mir::Body<'_>,
         fn_sig_span: Span,
         body_span: Span,
         basic_coverage_blocks: &CoverageGraph,
-    ) -> Self {
+    ) -> Option<Self> {
         let coverage_spans = CoverageSpansGenerator::generate_coverage_spans(
             mir_body,
             fn_sig_span,
@@ -28,13 +32,17 @@ impl CoverageSpans {
             basic_coverage_blocks,
         );
 
+        if coverage_spans.is_empty() {
+            return None;
+        }
+
         // Group the coverage spans by BCB, with the BCBs in sorted order.
         let mut bcb_to_spans = IndexVec::from_elem_n(Vec::new(), basic_coverage_blocks.num_nodes());
         for CoverageSpan { bcb, span, .. } in coverage_spans {
             bcb_to_spans[bcb].push(span);
         }
 
-        Self { bcb_to_spans }
+        Some(Self { bcb_to_spans })
     }
 
     pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool {
diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
index 261d9dd448d..5f01b841867 100644
--- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs
+++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
@@ -22,6 +22,18 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         return false;
     }
 
+    // This just reproduces the logic from Instance::requires_inline.
+    match tcx.def_kind(def_id) {
+        DefKind::Ctor(..) | DefKind::Closure => return true,
+        DefKind::Fn | DefKind::AssocFn => {}
+        _ => return false,
+    }
+
+    // From this point on, it is valid to return true or false.
+    if tcx.sess.opts.unstable_opts.cross_crate_inline_threshold == InliningThreshold::Always {
+        return true;
+    }
+
     // Obey source annotations first; this is important because it means we can use
     // #[inline(never)] to force code generation.
     match codegen_fn_attrs.inline {
@@ -30,13 +42,6 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         _ => {}
     }
 
-    // This just reproduces the logic from Instance::requires_inline.
-    match tcx.def_kind(def_id) {
-        DefKind::Ctor(..) | DefKind::Closure => return true,
-        DefKind::Fn | DefKind::AssocFn => {}
-        _ => return false,
-    }
-
     // Don't do any inference when incremental compilation is enabled; the additional inlining that
     // inference permits also creates more work for small edits.
     if tcx.sess.opts.incremental.is_some() {
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 146cc33e8c6..ad12bce9b02 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -496,7 +496,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
             FlatSet::Elem(scalar) => {
                 let ty = op.ty(self.local_decls, self.tcx);
                 self.tcx.layout_of(self.param_env.and(ty)).map_or(FlatSet::Top, |layout| {
-                    FlatSet::Elem(ImmTy::from_scalar(scalar.into(), layout))
+                    FlatSet::Elem(ImmTy::from_scalar(scalar, layout))
                 })
             }
             FlatSet::Bottom => FlatSet::Bottom,
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index dbf27ea60f8..fd4af31501c 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -1,8 +1,8 @@
 use std::borrow::Cow;
 
 use rustc_errors::{
-    Applicability, DecorateLint, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage,
-    EmissionGuarantee, ErrorGuaranteed, Handler, IntoDiagnostic,
+    Applicability, DecorateLint, DiagCtxt, DiagnosticArgValue, DiagnosticBuilder,
+    DiagnosticMessage, EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
@@ -64,12 +64,12 @@ pub(crate) struct RequiresUnsafe {
 // but this would result in a lot of duplication.
 impl<'sess> IntoDiagnostic<'sess> for RequiresUnsafe {
     #[track_caller]
-    fn into_diagnostic(self, handler: &'sess Handler) -> DiagnosticBuilder<'sess, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::mir_transform_requires_unsafe);
+    fn into_diagnostic(self, dcx: &'sess DiagCtxt) -> DiagnosticBuilder<'sess, ErrorGuaranteed> {
+        let mut diag = dcx.struct_err(fluent::mir_transform_requires_unsafe);
         diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string()));
         diag.set_span(self.span);
         diag.span_label(self.span, self.details.label());
-        let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
+        let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
         diag.set_arg("details", desc);
         diag.set_arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
         self.details.add_subdiagnostics(&mut diag);
@@ -180,12 +180,9 @@ pub(crate) struct UnsafeOpInUnsafeFn {
 
 impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
     #[track_caller]
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut DiagnosticBuilder<'a, ()> {
-        let handler = diag.handler().expect("lint should not yet be emitted");
-        let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
+        let dcx = diag.dcx().expect("lint should not yet be emitted");
+        let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
         diag.set_arg("details", desc);
         diag.span_label(self.details.span, self.details.label());
         self.details.add_subdiagnostics(diag);
@@ -198,8 +195,6 @@ impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
                 Applicability::MaybeIncorrect,
             );
         }
-
-        diag
     }
 
     fn msg(&self) -> DiagnosticMessage {
@@ -213,10 +208,7 @@ pub(crate) enum AssertLint<P> {
 }
 
 impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
         let span = self.span();
         let assert_kind = self.panic();
         let message = assert_kind.diagnostic_message();
@@ -224,8 +216,6 @@ impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
             diag.set_arg(name, value);
         });
         diag.span_label(span, message);
-
-        diag
     }
 
     fn msg(&self) -> DiagnosticMessage {
@@ -284,10 +274,7 @@ pub(crate) struct MustNotSupend<'tcx, 'a> {
 
 // Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
         diag.span_label(self.yield_sp, fluent::_subdiag::label);
         if let Some(reason) = self.reason {
             diag.subdiagnostic(reason);
@@ -296,7 +283,6 @@ impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
         diag.set_arg("pre", self.pre);
         diag.set_arg("def_path", self.tcx.def_path_str(self.def_id));
         diag.set_arg("post", self.post);
-        diag
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index b21988e179d..3b8adf7e86b 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -644,12 +644,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             {
                 if let Some(offset) = self.evaluated[idx].as_ref()
                     && let Ok(offset) = self.ecx.read_target_usize(offset)
+                    && let Some(min_length) = offset.checked_add(1)
                 {
-                    projection.to_mut()[i] = ProjectionElem::ConstantIndex {
-                        offset,
-                        min_length: offset + 1,
-                        from_end: false,
-                    };
+                    projection.to_mut()[i] =
+                        ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
                 } else if let Some(new_idx) = self.try_as_local(idx, location) {
                     projection.to_mut()[i] = ProjectionElem::Index(new_idx);
                     self.reused_locals.insert(new_idx);
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 71b3754fac8..a68bfcd06d5 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -176,6 +176,7 @@ use rustc_middle::mir::visit::Visitor as MirVisitor;
 use rustc_middle::mir::{self, Location};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
+use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{
     self, AssocKind, GenericParamDefKind, Instance, InstanceDef, Ty, TyCtxt, TypeFoldable,
@@ -370,7 +371,7 @@ fn collect_items_rec<'tcx>(
     // current step of mono items collection.
     //
     // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do.
-    let error_count = tcx.sess.diagnostic().err_count();
+    let error_count = tcx.sess.dcx().err_count();
 
     match starting_item.node {
         MonoItem::Static(def_id) => {
@@ -458,7 +459,7 @@ fn collect_items_rec<'tcx>(
 
     // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
     // mono item graph.
-    if tcx.sess.diagnostic().err_count() > error_count
+    if tcx.sess.dcx().err_count() > error_count
         && starting_item.node.is_generic_fn(tcx)
         && starting_item.node.is_user_defined()
     {
@@ -923,6 +924,21 @@ fn visit_instance_use<'tcx>(
         return;
     }
 
+    // The intrinsics assert_inhabited, assert_zero_valid, and assert_mem_uninitialized_valid will
+    // be lowered in codegen to nothing or a call to panic_nounwind. So if we encounter any
+    // of those intrinsics, we need to include a mono item for panic_nounwind, else we may try to
+    // codegen a call to that function without generating code for the function itself.
+    if let ty::InstanceDef::Intrinsic(def_id) = instance.def {
+        let name = tcx.item_name(def_id);
+        if let Some(_requirement) = ValidityRequirement::from_intrinsic(name) {
+            let def_id = tcx.lang_items().get(LangItem::PanicNounwind).unwrap();
+            let panic_instance = Instance::mono(tcx, def_id);
+            if should_codegen_locally(tcx, &panic_instance) {
+                output.push(create_fn_mono_item(tcx, panic_instance, source));
+            }
+        }
+    }
+
     match instance.def {
         ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => {
             if !is_direct_call {
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index d242a7baec1..247b2245583 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -51,9 +51,9 @@ impl IntoDiagnostic<'_> for UnusedGenericParamsHint {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'_ rustc_errors::Handler,
+        dcx: &'_ rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::monomorphize_unused_generic_params);
+        let mut diag = dcx.struct_err(fluent::monomorphize_unused_generic_params);
         diag.set_span(self.span);
         for (span, name) in self.param_spans.into_iter().zip(self.param_names) {
             // FIXME: I can figure out how to do a label with a fluent string with a fixed message,
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index e06935d4e47..d47d3e5e7d3 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -212,11 +212,17 @@ where
     let cgu_name_cache = &mut FxHashMap::default();
 
     for mono_item in mono_items {
-        // Handle only root items directly here. Inlined items are handled at
-        // the bottom of the loop based on reachability.
+        // Handle only root (GloballyShared) items directly here. Inlined (LocalCopy) items
+        // are handled at the bottom of the loop based on reachability, with one exception.
+        // The #[lang = "start"] item is the program entrypoint, so there are no calls to it in MIR.
+        // So even if its mode is LocalCopy, we need to treat it like a root.
         match mono_item.instantiation_mode(cx.tcx) {
             InstantiationMode::GloballyShared { .. } => {}
-            InstantiationMode::LocalCopy => continue,
+            InstantiationMode::LocalCopy => {
+                if Some(mono_item.def_id()) != cx.tcx.lang_items().start_fn() {
+                    continue;
+                }
+            }
         }
 
         let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index cb1f328577d..ac2e8960b06 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -242,16 +242,10 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
 
             ty::ReVar(vid) => {
                 assert_eq!(
-                    self.infcx.root_lt_var(vid),
-                    vid,
-                    "region vid should have been resolved fully before canonicalization"
-                );
-                assert_eq!(
-                    self.infcx.probe_lt_var(vid),
+                    self.infcx.opportunistic_resolve_lt_var(vid),
                     None,
                     "region vid should have been resolved fully before canonicalization"
                 );
-
                 match self.canonicalize_mode {
                     CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
                     CanonicalizeMode::Response { .. } => {
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index c51a5c095ee..008adcc83d0 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1042,11 +1042,11 @@ impl<'a> IntoDiagnostic<'a> for ExpectedIdentifier {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'a rustc_errors::Handler,
+        dcx: &'a rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
         let token_descr = TokenDescription::from_token(&self.token);
 
-        let mut diag = handler.struct_err(match token_descr {
+        let mut diag = dcx.struct_err(match token_descr {
             Some(TokenDescription::ReservedIdentifier) => {
                 fluent::parse_expected_identifier_found_reserved_identifier_str
             }
@@ -1099,11 +1099,11 @@ impl<'a> IntoDiagnostic<'a> for ExpectedSemi {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'a rustc_errors::Handler,
+        dcx: &'a rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
         let token_descr = TokenDescription::from_token(&self.token);
 
-        let mut diag = handler.struct_err(match token_descr {
+        let mut diag = dcx.struct_err(match token_descr {
             Some(TokenDescription::ReservedIdentifier) => {
                 fluent::parse_expected_semi_found_reserved_identifier_str
             }
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index b1dc1f98777..c158edaac25 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -227,7 +227,7 @@ impl<'a> StringReader<'a> {
                         let string = self.str_from(suffix_start);
                         if string == "_" {
                             self.sess
-                                .span_diagnostic
+                                .dcx
                                 .emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) });
                             None
                         } else {
@@ -346,7 +346,7 @@ impl<'a> StringReader<'a> {
         c: char,
     ) -> DiagnosticBuilder<'a, !> {
         self.sess
-            .span_diagnostic
+            .dcx
             .struct_span_fatal(self.mk_sp(from_pos, to_pos), format!("{}: {}", m, escaped_char(c)))
     }
 
@@ -403,7 +403,7 @@ impl<'a> StringReader<'a> {
         match kind {
             rustc_lexer::LiteralKind::Char { terminated } => {
                 if !terminated {
-                    self.sess.span_diagnostic.span_fatal_with_code(
+                    self.sess.dcx.span_fatal_with_code(
                         self.mk_sp(start, end),
                         "unterminated character literal",
                         error_code!(E0762),
@@ -413,7 +413,7 @@ impl<'a> StringReader<'a> {
             }
             rustc_lexer::LiteralKind::Byte { terminated } => {
                 if !terminated {
-                    self.sess.span_diagnostic.span_fatal_with_code(
+                    self.sess.dcx.span_fatal_with_code(
                         self.mk_sp(start + BytePos(1), end),
                         "unterminated byte constant",
                         error_code!(E0763),
@@ -423,7 +423,7 @@ impl<'a> StringReader<'a> {
             }
             rustc_lexer::LiteralKind::Str { terminated } => {
                 if !terminated {
-                    self.sess.span_diagnostic.span_fatal_with_code(
+                    self.sess.dcx.span_fatal_with_code(
                         self.mk_sp(start, end),
                         "unterminated double quote string",
                         error_code!(E0765),
@@ -433,7 +433,7 @@ impl<'a> StringReader<'a> {
             }
             rustc_lexer::LiteralKind::ByteStr { terminated } => {
                 if !terminated {
-                    self.sess.span_diagnostic.span_fatal_with_code(
+                    self.sess.dcx.span_fatal_with_code(
                         self.mk_sp(start + BytePos(1), end),
                         "unterminated double quote byte string",
                         error_code!(E0766),
@@ -443,7 +443,7 @@ impl<'a> StringReader<'a> {
             }
             rustc_lexer::LiteralKind::CStr { terminated } => {
                 if !terminated {
-                    self.sess.span_diagnostic.span_fatal_with_code(
+                    self.sess.dcx.span_fatal_with_code(
                         self.mk_sp(start + BytePos(1), end),
                         "unterminated C string",
                         error_code!(E0767),
@@ -578,7 +578,7 @@ impl<'a> StringReader<'a> {
         possible_offset: Option<u32>,
         found_terminators: u32,
     ) -> ! {
-        let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code(
+        let mut err = self.sess.dcx.struct_span_fatal_with_code(
             self.mk_sp(start, start),
             "unterminated raw string",
             error_code!(E0748),
@@ -614,7 +614,7 @@ impl<'a> StringReader<'a> {
             None => "unterminated block comment",
         };
         let last_bpos = self.pos;
-        let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code(
+        let mut err = self.sess.dcx.struct_span_fatal_with_code(
             self.mk_sp(start, last_bpos),
             msg,
             error_code!(E0758),
@@ -719,7 +719,7 @@ impl<'a> StringReader<'a> {
                     has_fatal_err = true;
                 }
                 emit_unescape_error(
-                    &self.sess.span_diagnostic,
+                    &self.sess.dcx,
                     lit_content,
                     span_with_quotes,
                     span,
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index 8cbadc26635..2bc2789a4f7 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -73,7 +73,7 @@ impl<'a> TokenTreesReader<'a> {
 
     fn eof_err(&mut self) -> PErr<'a> {
         let msg = "this file contains an unclosed delimiter";
-        let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
+        let mut err = self.string_reader.sess.dcx.struct_span_err(self.token.span, msg);
         for &(_, sp) in &self.diag_info.open_braces {
             err.span_label(sp, "unclosed delimiter");
             self.diag_info.unmatched_delims.push(UnmatchedDelim {
@@ -290,7 +290,7 @@ impl<'a> TokenTreesReader<'a> {
         // matching opening delimiter).
         let token_str = token_to_string(&self.token);
         let msg = format!("unexpected closing delimiter: `{token_str}`");
-        let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
+        let mut err = self.string_reader.sess.dcx.struct_span_err(self.token.span, msg);
 
         report_suspicious_mismatch_block(
             &mut err,
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index b659c40b233..775082adbe8 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -3,20 +3,20 @@
 use std::iter::once;
 use std::ops::Range;
 
-use rustc_errors::{Applicability, Handler};
+use rustc_errors::{Applicability, DiagCtxt};
 use rustc_lexer::unescape::{EscapeError, Mode};
 use rustc_span::{BytePos, Span};
 
 use crate::errors::{MoreThanOneCharNote, MoreThanOneCharSugg, NoBraceUnicodeSub, UnescapeError};
 
 pub(crate) fn emit_unescape_error(
-    handler: &Handler,
-    // interior part of the literal, without quotes
+    dcx: &DiagCtxt,
+    // interior part of the literal, between quotes
     lit: &str,
-    // full span of the literal, including quotes
-    span_with_quotes: Span,
-    // interior span of the literal, without quotes
-    span: Span,
+    // full span of the literal, including quotes and any prefix
+    full_lit_span: Span,
+    // span of the error part of the literal
+    err_span: Span,
     mode: Mode,
     // range of the error inside `lit`
     range: Range<usize>,
@@ -24,19 +24,19 @@ pub(crate) fn emit_unescape_error(
 ) {
     debug!(
         "emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
-        lit, span_with_quotes, mode, range, error
+        lit, full_lit_span, mode, range, error
     );
     let last_char = || {
         let c = lit[range.clone()].chars().next_back().unwrap();
-        let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
+        let span = err_span.with_lo(err_span.hi() - BytePos(c.len_utf8() as u32));
         (c, span)
     };
     match error {
         EscapeError::LoneSurrogateUnicodeEscape => {
-            handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: true });
+            dcx.emit_err(UnescapeError::InvalidUnicodeEscape { span: err_span, surrogate: true });
         }
         EscapeError::OutOfRangeUnicodeEscape => {
-            handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: false });
+            dcx.emit_err(UnescapeError::InvalidUnicodeEscape { span: err_span, surrogate: false });
         }
         EscapeError::MoreThanOneChar => {
             use unicode_normalization::{char::is_combining_mark, UnicodeNormalization};
@@ -49,12 +49,16 @@ pub(crate) fn emit_unescape_error(
                 let normalized = lit.nfc().to_string();
                 if normalized.chars().count() == 1 {
                     let ch = normalized.chars().next().unwrap().escape_default().to_string();
-                    sugg = Some(MoreThanOneCharSugg::NormalizedForm { span, ch, normalized });
+                    sugg = Some(MoreThanOneCharSugg::NormalizedForm {
+                        span: err_span,
+                        ch,
+                        normalized,
+                    });
                 }
                 let escaped_marks =
                     rest.iter().map(|c| c.escape_default().to_string()).collect::<Vec<_>>();
                 note = Some(MoreThanOneCharNote::AllCombining {
-                    span,
+                    span: err_span,
                     chr: format!("{first}"),
                     len: escaped_marks.len(),
                     escaped_marks: escaped_marks.join(""),
@@ -69,10 +73,12 @@ pub(crate) fn emit_unescape_error(
                     .collect();
 
                 if let &[ch] = printable.as_slice() {
-                    sugg =
-                        Some(MoreThanOneCharSugg::RemoveNonPrinting { span, ch: ch.to_string() });
+                    sugg = Some(MoreThanOneCharSugg::RemoveNonPrinting {
+                        span: err_span,
+                        ch: ch.to_string(),
+                    });
                     note = Some(MoreThanOneCharNote::NonPrinting {
-                        span,
+                        span: err_span,
                         escaped: lit.escape_default().to_string(),
                     });
                 }
@@ -91,21 +97,21 @@ pub(crate) fn emit_unescape_error(
                 }
                 let sugg = format!("{prefix}\"{escaped}\"");
                 MoreThanOneCharSugg::Quotes {
-                    span: span_with_quotes,
+                    span: full_lit_span,
                     is_byte: mode == Mode::Byte,
                     sugg,
                 }
             });
-            handler.emit_err(UnescapeError::MoreThanOneChar {
-                span: span_with_quotes,
+            dcx.emit_err(UnescapeError::MoreThanOneChar {
+                span: full_lit_span,
                 note,
                 suggestion: sugg,
             });
         }
         EscapeError::EscapeOnlyChar => {
             let (c, char_span) = last_char();
-            handler.emit_err(UnescapeError::EscapeOnlyChar {
-                span,
+            dcx.emit_err(UnescapeError::EscapeOnlyChar {
+                span: err_span,
                 char_span,
                 escaped_sugg: c.escape_default().to_string(),
                 escaped_msg: escaped_char(c),
@@ -114,11 +120,11 @@ pub(crate) fn emit_unescape_error(
         }
         EscapeError::BareCarriageReturn => {
             let double_quotes = mode.in_double_quotes();
-            handler.emit_err(UnescapeError::BareCr { span, double_quotes });
+            dcx.emit_err(UnescapeError::BareCr { span: err_span, double_quotes });
         }
         EscapeError::BareCarriageReturnInRawString => {
             assert!(mode.in_double_quotes());
-            handler.emit_err(UnescapeError::BareCrRawString(span));
+            dcx.emit_err(UnescapeError::BareCrRawString(err_span));
         }
         EscapeError::InvalidEscape => {
             let (c, span) = last_char();
@@ -129,7 +135,7 @@ pub(crate) fn emit_unescape_error(
                 "unknown character escape"
             };
             let ec = escaped_char(c);
-            let mut diag = handler.struct_span_err(span, format!("{label}: `{ec}`"));
+            let mut diag = dcx.struct_span_err(span, format!("{label}: `{ec}`"));
             diag.span_label(span, label);
             if c == '{' || c == '}' && matches!(mode, Mode::Str | Mode::RawStr) {
                 diag.help(
@@ -143,7 +149,7 @@ pub(crate) fn emit_unescape_error(
             } else {
                 if mode == Mode::Str || mode == Mode::Char {
                     diag.span_suggestion(
-                        span_with_quotes,
+                        full_lit_span,
                         "if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal",
                         format!("r\"{lit}\""),
                         Applicability::MaybeIncorrect,
@@ -158,13 +164,13 @@ pub(crate) fn emit_unescape_error(
             diag.emit();
         }
         EscapeError::TooShortHexEscape => {
-            handler.emit_err(UnescapeError::TooShortHexEscape(span));
+            dcx.emit_err(UnescapeError::TooShortHexEscape(err_span));
         }
         EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
             let (c, span) = last_char();
             let is_hex = error == EscapeError::InvalidCharInHexEscape;
             let ch = escaped_char(c);
-            handler.emit_err(UnescapeError::InvalidCharInEscape { span, is_hex, ch });
+            dcx.emit_err(UnescapeError::InvalidCharInEscape { span, is_hex, ch });
         }
         EscapeError::NonAsciiCharInByte => {
             let (c, span) = last_char();
@@ -174,7 +180,7 @@ pub(crate) fn emit_unescape_error(
                 Mode::RawByteStr => "raw byte string literal",
                 _ => panic!("non-is_byte literal paired with NonAsciiCharInByte"),
             };
-            let mut err = handler.struct_span_err(span, format!("non-ASCII character in {desc}"));
+            let mut err = dcx.struct_span_err(span, format!("non-ASCII character in {desc}"));
             let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 {
                 format!(" but is {c:?}")
             } else {
@@ -210,20 +216,20 @@ pub(crate) fn emit_unescape_error(
             err.emit();
         }
         EscapeError::OutOfRangeHexEscape => {
-            handler.emit_err(UnescapeError::OutOfRangeHexEscape(span));
+            dcx.emit_err(UnescapeError::OutOfRangeHexEscape(err_span));
         }
         EscapeError::LeadingUnderscoreUnicodeEscape => {
             let (c, span) = last_char();
-            handler.emit_err(UnescapeError::LeadingUnderscoreUnicodeEscape {
+            dcx.emit_err(UnescapeError::LeadingUnderscoreUnicodeEscape {
                 span,
                 ch: escaped_char(c),
             });
         }
         EscapeError::OverlongUnicodeEscape => {
-            handler.emit_err(UnescapeError::OverlongUnicodeEscape(span));
+            dcx.emit_err(UnescapeError::OverlongUnicodeEscape(err_span));
         }
         EscapeError::UnclosedUnicodeEscape => {
-            handler.emit_err(UnescapeError::UnclosedUnicodeEscape(span, span.shrink_to_hi()));
+            dcx.emit_err(UnescapeError::UnclosedUnicodeEscape(err_span, err_span.shrink_to_hi()));
         }
         EscapeError::NoBraceInUnicodeEscape => {
             let mut suggestion = "\\u{".to_owned();
@@ -238,34 +244,34 @@ pub(crate) fn emit_unescape_error(
             let (label, sub) = if suggestion_len > 0 {
                 suggestion.push('}');
                 let hi = char_span.lo() + BytePos(suggestion_len as u32);
-                (None, NoBraceUnicodeSub::Suggestion { span: span.with_hi(hi), suggestion })
+                (None, NoBraceUnicodeSub::Suggestion { span: err_span.with_hi(hi), suggestion })
             } else {
-                (Some(span), NoBraceUnicodeSub::Help)
+                (Some(err_span), NoBraceUnicodeSub::Help)
             };
-            handler.emit_err(UnescapeError::NoBraceInUnicodeEscape { span, label, sub });
+            dcx.emit_err(UnescapeError::NoBraceInUnicodeEscape { span: err_span, label, sub });
         }
         EscapeError::UnicodeEscapeInByte => {
-            handler.emit_err(UnescapeError::UnicodeEscapeInByte(span));
+            dcx.emit_err(UnescapeError::UnicodeEscapeInByte(err_span));
         }
         EscapeError::EmptyUnicodeEscape => {
-            handler.emit_err(UnescapeError::EmptyUnicodeEscape(span));
+            dcx.emit_err(UnescapeError::EmptyUnicodeEscape(err_span));
         }
         EscapeError::ZeroChars => {
-            handler.emit_err(UnescapeError::ZeroChars(span));
+            dcx.emit_err(UnescapeError::ZeroChars(err_span));
         }
         EscapeError::LoneSlash => {
-            handler.emit_err(UnescapeError::LoneSlash(span));
+            dcx.emit_err(UnescapeError::LoneSlash(err_span));
         }
         EscapeError::UnskippedWhitespaceWarning => {
             let (c, char_span) = last_char();
-            handler.emit_warning(UnescapeError::UnskippedWhitespace {
-                span,
+            dcx.emit_warning(UnescapeError::UnskippedWhitespace {
+                span: err_span,
                 ch: escaped_char(c),
                 char_span,
             });
         }
         EscapeError::MultipleSkippedLinesWarning => {
-            handler.emit_warning(UnescapeError::MultipleSkippedLinesWarning(span));
+            dcx.emit_warning(UnescapeError::MultipleSkippedLinesWarning(err_span));
         }
     }
 }
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index 0dc60688955..dac7569e385 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -350,7 +350,7 @@ pub(super) fn check_for_substitution(
 
     let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
         let msg = format!("substitution character not found for '{ch}'");
-        reader.sess.span_diagnostic.span_bug(span, msg);
+        reader.sess.dcx.span_bug(span, msg);
     };
 
     // special help suggestion for "directed" double quotes
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 9887a85e6a4..82b0ff70c16 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -100,7 +100,7 @@ pub fn parse_stream_from_source_str(
 
 /// Creates a new parser from a source string.
 pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> {
-    panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source))
+    panictry_buffer!(&sess.dcx, maybe_new_parser_from_source_str(sess, name, source))
 }
 
 /// Creates a new parser from a source string. Returns any buffered errors from lexing the initial
@@ -121,7 +121,7 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Spa
 
 /// Given a session and a `source_file`, returns a parser.
 fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> {
-    panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file))
+    panictry_buffer!(&sess.dcx, maybe_source_file_to_parser(sess, source_file))
 }
 
 /// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing the
@@ -166,7 +166,7 @@ fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>) ->
     match try_file_to_source_file(sess, path, spanopt) {
         Ok(source_file) => source_file,
         Err(d) => {
-            sess.span_diagnostic.emit_diagnostic(d);
+            sess.dcx.emit_diagnostic(d);
             FatalError.raise();
         }
     }
@@ -178,7 +178,7 @@ pub fn source_file_to_stream(
     source_file: Lrc<SourceFile>,
     override_span: Option<Span>,
 ) -> TokenStream {
-    panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
+    panictry_buffer!(&sess.dcx, maybe_file_to_stream(sess, source_file, override_span))
 }
 
 /// Given a source file, produces a sequence of token trees. Returns any buffered errors from
@@ -189,7 +189,7 @@ pub fn maybe_file_to_stream(
     override_span: Option<Span>,
 ) -> Result<TokenStream, Vec<Diagnostic>> {
     let src = source_file.src.as_ref().unwrap_or_else(|| {
-        sess.span_diagnostic.bug(format!(
+        sess.dcx.bug(format!(
             "cannot lex `source_file` without source: {}",
             sess.source_map().filename_for_diagnostics(&source_file.name)
         ));
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index c9ce896b868..56e52baf98b 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -55,7 +55,7 @@ impl<'a> Parser<'a> {
             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
                 if attr_style != ast::AttrStyle::Outer {
                     let span = self.token.span;
-                    let mut err = self.diagnostic().struct_span_err_with_code(
+                    let mut err = self.dcx().struct_span_err_with_code(
                         span,
                         fluent::parse_inner_doc_comment_not_permitted,
                         error_code!(E0753),
@@ -417,7 +417,7 @@ impl<'a> Parser<'a> {
         }
 
         Err(InvalidMetaItem { span: self.token.span, token: self.token.clone() }
-            .into_diagnostic(self.diagnostic()))
+            .into_diagnostic(self.dcx()))
     }
 }
 
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 5e8447030f1..2307f4cfffa 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -41,7 +41,7 @@ impl AttrWrapper {
     }
 
     pub(crate) fn take_for_recovery(self, sess: &ParseSess) -> AttrVec {
-        sess.span_diagnostic.span_delayed_bug(
+        sess.dcx.span_delayed_bug(
             self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP),
             "AttrVec is taken for recovery but no error is produced",
         );
@@ -266,8 +266,7 @@ impl<'a> Parser<'a> {
             if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) {
                 inner_attr_replace_ranges.push(attr_range);
             } else {
-                self.diagnostic()
-                    .span_delayed_bug(inner_attr.span, "Missing token range for attribute");
+                self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute");
             }
         }
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 221fc70d9ff..9e3637ea9f3 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -34,8 +34,8 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    pluralize, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
-    ErrorGuaranteed, FatalError, Handler, IntoDiagnostic, MultiSpan, PResult,
+    pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
+    DiagnosticMessage, ErrorGuaranteed, FatalError, IntoDiagnostic, MultiSpan, PResult,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
@@ -246,15 +246,15 @@ impl<'a> Parser<'a> {
         sp: S,
         m: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        self.diagnostic().struct_span_err(sp, m)
+        self.dcx().struct_span_err(sp, m)
     }
 
     pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.diagnostic().span_bug(sp, msg)
+        self.dcx().span_bug(sp, msg)
     }
 
-    pub(super) fn diagnostic(&self) -> &'a Handler {
-        &self.sess.span_diagnostic
+    pub(super) fn dcx(&self) -> &'a DiagCtxt {
+        &self.sess.dcx
     }
 
     /// Replace `self` with `snapshot.parser`.
@@ -284,7 +284,7 @@ impl<'a> Parser<'a> {
                 span: self.prev_token.span,
                 missing_comma: None,
             }
-            .into_diagnostic(self.diagnostic()));
+            .into_diagnostic(self.dcx()));
         }
 
         let valid_follow = &[
@@ -347,7 +347,7 @@ impl<'a> Parser<'a> {
             suggest_remove_comma,
             help_cannot_start_number,
         };
-        let mut err = err.into_diagnostic(self.diagnostic());
+        let mut err = err.into_diagnostic(self.dcx());
 
         // if the token we have is a `<`
         // it *might* be a misplaced generic
@@ -1410,7 +1410,7 @@ impl<'a> Parser<'a> {
                                 // Not entirely sure now, but we bubble the error up with the
                                 // suggestion.
                                 self.restore_snapshot(snapshot);
-                                Err(err.into_diagnostic(self.diagnostic()))
+                                Err(err.into_diagnostic(self.dcx()))
                             }
                         }
                     } else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
@@ -1425,7 +1425,7 @@ impl<'a> Parser<'a> {
                         }
                         // Consume the fn call arguments.
                         match self.consume_fn_args() {
-                            Err(()) => Err(err.into_diagnostic(self.diagnostic())),
+                            Err(()) => Err(err.into_diagnostic(self.dcx())),
                             Ok(()) => {
                                 self.sess.emit_err(err);
                                 // FIXME: actually check that the two expressions in the binop are
@@ -1451,7 +1451,7 @@ impl<'a> Parser<'a> {
                             mk_err_expr(self, inner_op.span.to(self.prev_token.span))
                         } else {
                             // These cases cause too many knock-down errors, bail out (#61329).
-                            Err(err.into_diagnostic(self.diagnostic()))
+                            Err(err.into_diagnostic(self.dcx()))
                         }
                     };
                 }
@@ -2539,7 +2539,7 @@ impl<'a> Parser<'a> {
             Ok(Some(GenericArg::Const(self.parse_const_arg()?)))
         } else {
             let after_kw_const = self.token.span;
-            self.recover_const_arg(after_kw_const, err.into_diagnostic(self.diagnostic())).map(Some)
+            self.recover_const_arg(after_kw_const, err.into_diagnostic(self.dcx())).map(Some)
         }
     }
 
@@ -2897,7 +2897,7 @@ impl<'a> Parser<'a> {
                         span: path.span.shrink_to_hi(),
                         between: between_span,
                     }
-                    .into_diagnostic(self.diagnostic()));
+                    .into_diagnostic(self.dcx()));
                 }
             }
         }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 509cef9826b..cd3e8b92f2f 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1269,7 +1269,7 @@ impl<'a> Parser<'a> {
                                         .collect(),
                                 },
                             }
-                            .into_diagnostic(self.diagnostic());
+                            .into_diagnostic(self.dcx());
                             replacement_err.emit();
 
                             let old_err = mem::replace(err, replacement_err);
@@ -1693,8 +1693,7 @@ impl<'a> Parser<'a> {
         mk_lit_char: impl FnOnce(Symbol, Span) -> L,
         err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
     ) -> L {
-        if let Some(mut diag) =
-            self.diagnostic().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
+        if let Some(mut diag) = self.dcx().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
         {
             diag.span_suggestion_verbose(
                 lifetime.span.shrink_to_hi(),
@@ -1884,8 +1883,8 @@ impl<'a> Parser<'a> {
         self.bump(); // `#`
 
         let Some((ident, false)) = self.token.ident() else {
-            let err = errors::ExpectedBuiltinIdent { span: self.token.span }
-                .into_diagnostic(self.diagnostic());
+            let err =
+                errors::ExpectedBuiltinIdent { span: self.token.span }.into_diagnostic(self.dcx());
             return Err(err);
         };
         self.sess.gated_spans.gate(sym::builtin_syntax, ident.span);
@@ -1896,7 +1895,7 @@ impl<'a> Parser<'a> {
             Ok(res)
         } else {
             let err = errors::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident.name }
-                .into_diagnostic(self.diagnostic());
+                .into_diagnostic(self.dcx());
             return Err(err);
         };
         self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?;
@@ -1960,7 +1959,7 @@ impl<'a> Parser<'a> {
             && matches!(e.kind, ExprKind::Err)
         {
             let mut err = errors::InvalidInterpolatedExpression { span: self.token.span }
-                .into_diagnostic(self.diagnostic());
+                .into_diagnostic(self.dcx());
             err.downgrade_to_delayed_bug();
             return Err(err);
         }
@@ -2172,7 +2171,7 @@ impl<'a> Parser<'a> {
                     return Err(errors::MissingSemicolonBeforeArray {
                         open_delim: open_delim_span,
                         semicolon: prev_span.shrink_to_hi(),
-                    }.into_diagnostic(self.diagnostic()));
+                    }.into_diagnostic(self.dcx()));
                 }
                 Ok(_) => (),
                 Err(err) => err.cancel(),
@@ -2320,7 +2319,7 @@ impl<'a> Parser<'a> {
             if self.check_keyword(kw::Async) {
                 let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo);
                 Err(errors::AsyncMoveOrderIncorrect { span: move_async_span }
-                    .into_diagnostic(self.diagnostic()))
+                    .into_diagnostic(self.dcx()))
             } else {
                 Ok(CaptureBy::Value { move_kw: move_kw_span })
             }
@@ -2510,7 +2509,7 @@ impl<'a> Parser<'a> {
             };
             if self.prev_token.kind == token::BinOp(token::Or) {
                 // This was part of a closure, the that part of the parser recover.
-                return Err(err.into_diagnostic(self.diagnostic()));
+                return Err(err.into_diagnostic(self.dcx()));
             } else {
                 Some(self.sess.emit_err(err))
             }
@@ -3194,8 +3193,7 @@ impl<'a> Parser<'a> {
     fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
         let (attrs, body) = self.parse_inner_attrs_and_block()?;
         if self.eat_keyword(kw::Catch) {
-            Err(errors::CatchAfterTry { span: self.prev_token.span }
-                .into_diagnostic(self.diagnostic()))
+            Err(errors::CatchAfterTry { span: self.prev_token.span }.into_diagnostic(self.dcx()))
         } else {
             let span = span_lo.to(body.span);
             self.sess.gated_spans.gate(sym::try_blocks, span);
@@ -3537,7 +3535,7 @@ impl<'a> Parser<'a> {
                     ident_span: this.token.span,
                     token: this.look_ahead(1, |t| t.clone()),
                 }
-                .into_diagnostic(&self.sess.span_diagnostic));
+                .into_diagnostic(&self.sess.dcx));
             }
             let (ident, expr) = if is_shorthand {
                 // Mimic `x: x` for the `x` field shorthand.
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 89919247e82..bf619daba50 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -438,7 +438,7 @@ impl<'a> Parser<'a> {
             None
         };
 
-        if let Some(err) = err { Err(err.into_diagnostic(self.diagnostic())) } else { Ok(()) }
+        if let Some(err) = err { Err(err.into_diagnostic(self.dcx())) } else { Ok(()) }
     }
 
     fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemInfo>> {
@@ -759,7 +759,7 @@ impl<'a> Parser<'a> {
             if self.look_ahead(1, |tok| tok == &token::CloseDelim(Delimiter::Brace)) {
                 // FIXME: merge with `DocCommentDoesNotDocumentAnything` (E0585)
                 struct_span_err!(
-                    self.diagnostic(),
+                    self.dcx(),
                     self.token.span,
                     E0584,
                     "found a documentation comment that doesn't document anything",
@@ -923,7 +923,7 @@ impl<'a> Parser<'a> {
         );
         let where_predicates_split = before_where_clause.predicates.len();
         let mut predicates = before_where_clause.predicates;
-        predicates.extend(after_where_clause.predicates.into_iter());
+        predicates.extend(after_where_clause.predicates);
         let where_clause = WhereClause {
             has_where_token: before_where_clause.has_where_token
                 || after_where_clause.has_where_token,
@@ -1374,7 +1374,7 @@ impl<'a> Parser<'a> {
 
         let span = self.prev_token.span.shrink_to_hi();
         let err: DiagnosticBuilder<'_, ErrorGuaranteed> =
-            errors::MissingConstType { span, colon, kind }.into_diagnostic(self.diagnostic());
+            errors::MissingConstType { span, colon, kind }.into_diagnostic(self.dcx());
         err.stash(span, StashKey::ItemNoType);
 
         // The user intended that the type be inferred,
@@ -1391,7 +1391,7 @@ impl<'a> Parser<'a> {
                 self.bump();
                 self.sess.emit_err(err);
             } else {
-                return Err(err.into_diagnostic(self.diagnostic()));
+                return Err(err.into_diagnostic(self.dcx()));
             }
         }
 
@@ -1591,7 +1591,7 @@ impl<'a> Parser<'a> {
         } else {
             let err =
                 errors::UnexpectedTokenAfterStructName::new(self.token.span, self.token.clone());
-            return Err(err.into_diagnostic(self.diagnostic()));
+            return Err(err.into_diagnostic(self.dcx()));
         };
 
         Ok((class_name, ItemKind::Struct(vdata, generics)))
@@ -1787,7 +1787,7 @@ impl<'a> Parser<'a> {
                         let sp = previous_span.shrink_to_hi();
                         err.missing_comma = Some(sp);
                     }
-                    return Err(err.into_diagnostic(self.diagnostic()));
+                    return Err(err.into_diagnostic(self.dcx()));
                 }
             }
             _ => {
@@ -1837,7 +1837,7 @@ impl<'a> Parser<'a> {
                     // Make sure an error was emitted (either by recovering an angle bracket,
                     // or by finding an identifier as the next token), since we're
                     // going to continue parsing
-                    assert!(self.diagnostic().has_errors().is_some());
+                    assert!(self.dcx().has_errors().is_some());
                 } else {
                     return Err(err);
                 }
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 2baedb2766f..b91432f10c8 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -1507,7 +1507,7 @@ pub(crate) fn make_unclosed_delims_error(
         opening_candidate: unmatched.candidate_span,
         unclosed: unmatched.unclosed_span,
     }
-    .into_diagnostic(&sess.span_diagnostic);
+    .into_diagnostic(&sess.dcx);
     Some(err)
 }
 
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 4360a69e501..301a88cd077 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -114,8 +114,9 @@ impl<'a> Parser<'a> {
             NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
                 Some(item) => NtItem(item),
                 None => {
-                    return Err(UnexpectedNonterminal::Item(self.token.span)
-                        .into_diagnostic(self.diagnostic()));
+                    return Err(
+                        UnexpectedNonterminal::Item(self.token.span).into_diagnostic(self.dcx())
+                    );
                 }
             },
             NonterminalKind::Block => {
@@ -127,7 +128,7 @@ impl<'a> Parser<'a> {
                 Some(s) => NtStmt(P(s)),
                 None => {
                     return Err(UnexpectedNonterminal::Statement(self.token.span)
-                        .into_diagnostic(self.diagnostic()));
+                        .into_diagnostic(self.dcx()));
                 }
             },
             NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => {
@@ -163,7 +164,7 @@ impl<'a> Parser<'a> {
                     span: self.token.span,
                     token: self.token.clone(),
                 }
-                .into_diagnostic(self.diagnostic()));
+                .into_diagnostic(self.dcx()));
             }
             NonterminalKind::Path => {
                 NtPath(P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?))
@@ -181,7 +182,7 @@ impl<'a> Parser<'a> {
                         span: self.token.span,
                         token: self.token.clone(),
                     }
-                    .into_diagnostic(self.diagnostic()));
+                    .into_diagnostic(self.dcx()));
                 }
             }
         };
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 020b66a985a..80233eddb9b 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -873,7 +873,7 @@ impl<'a> Parser<'a> {
         // will direct us over to `parse_enum_variant()`.
         if self.token == token::OpenDelim(Delimiter::Parenthesis) {
             return Err(EnumPatternInsteadOfIdentifier { span: self.prev_token.span }
-                .into_diagnostic(self.diagnostic()));
+                .into_diagnostic(self.dcx()));
         }
 
         Ok(PatKind::Ident(binding_annotation, ident, sub))
@@ -987,7 +987,7 @@ impl<'a> Parser<'a> {
             // check that a comma comes after every field
             if !ate_comma {
                 let mut err = ExpectedCommaAfterPatternField { span: self.token.span }
-                    .into_diagnostic(self.diagnostic());
+                    .into_diagnostic(self.dcx());
                 if let Some(mut delayed) = delayed_err {
                     delayed.emit();
                 }
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 0f4ba9617c6..3b92a911983 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -123,7 +123,7 @@ impl<'a> Parser<'a> {
 
         self.bump(); // colon
 
-        self.diagnostic()
+        self.dcx()
             .struct_span_err(
                 self.prev_token.span,
                 "found single colon before projection in qualified path",
@@ -326,7 +326,7 @@ impl<'a> Parser<'a> {
                                     .is_nightly_build()
                                     .then_some(()),
                             }
-                            .into_diagnostic(self.diagnostic());
+                            .into_diagnostic(self.dcx());
                         }
                         // Attempt to find places where a missing `>` might belong.
                         else if let Some(arg) = args
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 81055431f64..9fea3826652 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -56,7 +56,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
                     let res = match res {
                         Ok(lit) => {
                             if token_lit.suffix.is_some() {
-                                let mut err = sess.span_diagnostic.struct_span_err(
+                                let mut err = sess.dcx.struct_span_err(
                                     expr.span,
                                     "suffixed literals are not allowed in attributes",
                                 );
@@ -89,7 +89,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
                     //   the error because an earlier error will have already
                     //   been reported.
                     let msg = format!("attribute value must be a literal");
-                    let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg);
+                    let mut err = sess.dcx.struct_span_err(expr.span, msg);
                     if let ast::ExprKind::Err = expr.kind {
                         err.downgrade_to_delayed_bug();
                     }
@@ -206,7 +206,7 @@ fn emit_malformed_attribute(
     if should_warn(name) {
         sess.buffer_lint(ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg);
     } else {
-        sess.span_diagnostic
+        sess.dcx
             .struct_span_err(span, error_msg)
             .span_suggestions(
                 span,
@@ -215,7 +215,7 @@ fn emit_malformed_attribute(
                 } else {
                     "the following are the possible correct uses"
                 },
-                suggestions.into_iter(),
+                suggestions,
                 Applicability::HasPlaceholders,
             )
             .emit();
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 58127445322..8f8da211d31 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -867,9 +867,9 @@ impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'_ rustc_errors::Handler,
+        dcx: &'_ rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::passes_invalid_attr_at_crate_level);
+        let mut diag = dcx.struct_err(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
@@ -1020,9 +1020,9 @@ impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &rustc_errors::Handler,
+        dcx: &rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_span_err_with_code(
+        let mut diag = dcx.struct_span_err_with_code(
             self.span,
             fluent::passes_break_non_loop,
             error_code!(E0571),
@@ -1169,9 +1169,9 @@ impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &rustc_errors::Handler,
+        dcx: &rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_span_err_with_code(
+        let mut diag = dcx.struct_span_err_with_code(
             self.span,
             fluent::passes_naked_functions_asm_block,
             error_code!(E0787),
@@ -1285,9 +1285,9 @@ impl<'a> IntoDiagnostic<'a> for NoMainErr {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'a rustc_errors::Handler,
+        dcx: &'a rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag = handler.struct_span_err_with_code(
+        let mut diag = dcx.struct_span_err_with_code(
             DUMMY_SP,
             fluent::passes_no_main_function,
             error_code!(E0601),
@@ -1348,9 +1348,9 @@ impl IntoDiagnostic<'_> for DuplicateLangItem {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &rustc_errors::Handler,
+        dcx: &rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err_with_code(
+        let mut diag = dcx.struct_err_with_code(
             match self.duplicate {
                 Duplicate::Plain => fluent::passes_duplicate_lang_item,
                 Duplicate::Crate => fluent::passes_duplicate_lang_item_crate,
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 85969b72d23..c2392620cb2 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -429,7 +429,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
         record_variants!(
             (self, b, b, Id::None, hir, GenericBound, GenericBound),
-            [Trait, LangItemTrait, Outlives]
+            [Trait, Outlives]
         );
         hir_visit::walk_param_bound(self, b)
     }
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 83b7d8d71bc..6d14a14096d 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -7,18 +7,18 @@
 //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
 //! * 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_hir as hir;
-use rustc_hir::def::DefKind;
+use rustc_ast as ast;
+use rustc_ast::visit;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::{extract, GenericRequirement};
-use rustc_hir::{LangItem, LanguageItems, Target};
-use rustc_middle::ty::TyCtxt;
+use rustc_hir::{LangItem, LanguageItems, MethodKind, Target};
+use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
 use rustc_session::cstore::ExternCrate;
 use rustc_span::symbol::kw::Empty;
 use rustc_span::Span;
@@ -31,28 +31,55 @@ pub(crate) enum Duplicate {
     CrateDepends,
 }
 
-struct LanguageItemCollector<'tcx> {
+struct LanguageItemCollector<'ast, 'tcx> {
     items: LanguageItems,
     tcx: TyCtxt<'tcx>,
+    resolver: &'ast ResolverAstLowering,
+    // FIXME(#118552): We should probably feed def_span eagerly on def-id creation
+    // so we can avoid constructing this map for local def-ids.
+    item_spans: FxHashMap<DefId, Span>,
+    parent_item: Option<&'ast ast::Item>,
 }
 
-impl<'tcx> LanguageItemCollector<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> {
-        LanguageItemCollector { tcx, items: LanguageItems::new() }
+impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
+    fn new(
+        tcx: TyCtxt<'tcx>,
+        resolver: &'ast ResolverAstLowering,
+    ) -> LanguageItemCollector<'ast, 'tcx> {
+        LanguageItemCollector {
+            tcx,
+            resolver,
+            items: LanguageItems::new(),
+            item_spans: FxHashMap::default(),
+            parent_item: None,
+        }
     }
 
-    fn check_for_lang(&mut self, actual_target: Target, def_id: LocalDefId) {
-        let attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
-        if let Some((name, span)) = extract(attrs) {
+    fn check_for_lang(
+        &mut self,
+        actual_target: Target,
+        def_id: LocalDefId,
+        attrs: &'ast [ast::Attribute],
+        item_span: Span,
+        generics: Option<&'ast ast::Generics>,
+    ) {
+        if let Some((name, attr_span)) = extract(attrs) {
             match LangItem::from_name(name) {
                 // Known lang item with attribute on correct target.
                 Some(lang_item) if actual_target == lang_item.target() => {
-                    self.collect_item_extended(lang_item, def_id, span);
+                    self.collect_item_extended(
+                        lang_item,
+                        def_id,
+                        item_span,
+                        attr_span,
+                        generics,
+                        actual_target,
+                    );
                 }
                 // Known lang item with attribute on incorrect target.
                 Some(lang_item) => {
                     self.tcx.sess.emit_err(LangItemOnIncorrectTarget {
-                        span,
+                        span: attr_span,
                         name,
                         expected_target: lang_item.target(),
                         actual_target,
@@ -60,127 +87,131 @@ impl<'tcx> LanguageItemCollector<'tcx> {
                 }
                 // Unknown lang item.
                 _ => {
-                    self.tcx.sess.emit_err(UnknownLangItem { span, name });
+                    self.tcx.sess.emit_err(UnknownLangItem { span: attr_span, name });
                 }
             }
         }
     }
 
-    fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId) {
+    fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId, item_span: Option<Span>) {
         // Check for duplicates.
-        if let Some(original_def_id) = self.items.get(lang_item) {
-            if original_def_id != item_def_id {
-                let local_span = self.tcx.hir().span_if_local(item_def_id);
-                let lang_item_name = lang_item.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(", ")
-                };
-                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 {
-                    self.tcx
-                        .crate_extern_paths(original_def_id.krate)
-                        .iter()
-                        .map(|p| p.display().to_string())
-                        .collect::<Vec<_>>()
-                        .join(", ")
-                };
-                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);
-                    }
+        if let Some(original_def_id) = self.items.get(lang_item)
+            && original_def_id != item_def_id
+        {
+            let lang_item_name = lang_item.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(", ")
+            };
+
+            let first_defined_span = self.item_spans.get(&original_def_id).copied();
+            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 {
+                self.tcx
+                    .crate_extern_paths(original_def_id.krate)
+                    .iter()
+                    .map(|p| p.display().to_string())
+                    .collect::<Vec<_>>()
+                    .join(", ")
+            };
+
+            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 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,
+            let duplicate = if item_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
                     }
-                };
-
-                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,
-                });
+                    _ => Duplicate::Crate,
+                }
+            };
+
+            self.tcx.sess.emit_err(DuplicateLangItem {
+                local_span: item_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,
+            });
+        } else {
+            // Matched.
+            self.items.set(lang_item, item_def_id);
+            // Collect span for error later
+            if let Some(item_span) = item_span {
+                self.item_spans.insert(item_def_id, item_span);
             }
         }
-
-        // Matched.
-        self.items.set(lang_item, item_def_id);
     }
 
     // Like collect_item() above, but also checks whether the lang item is declared
     // with the right number of generic arguments.
-    fn collect_item_extended(&mut self, lang_item: LangItem, item_def_id: LocalDefId, span: Span) {
+    fn collect_item_extended(
+        &mut self,
+        lang_item: LangItem,
+        item_def_id: LocalDefId,
+        item_span: Span,
+        attr_span: Span,
+        generics: Option<&'ast ast::Generics>,
+        target: Target,
+    ) {
         let name = lang_item.name();
 
-        // Now check whether the lang_item has the expected number of generic
-        // arguments. Generally speaking, binary and indexing operations have
-        // one (for the RHS/index), unary operations have none, the closure
-        // traits have one for the argument list, coroutines have one for the
-        // resume argument, and ordering/equality relations have one for the RHS
-        // Some other types like Box and various functions like drop_in_place
-        // have minimum requirements.
+        if let Some(generics) = generics {
+            // Now check whether the lang_item has the expected number of generic
+            // arguments. Generally speaking, binary and indexing operations have
+            // one (for the RHS/index), unary operations have none, the closure
+            // traits have one for the argument list, coroutines have one for the
+            // resume argument, and ordering/equality relations have one for the RHS
+            // Some other types like Box and various functions like drop_in_place
+            // have minimum requirements.
 
-        if let hir::Node::Item(hir::Item { kind, span: item_span, .. }) =
-            self.tcx.hir_node_by_def_id(item_def_id)
-        {
-            let (actual_num, generics_span) = match kind.generics() {
-                Some(generics) => (
-                    generics
-                        .params
-                        .iter()
-                        .filter(|p| {
-                            !matches!(
-                                p.kind,
-                                hir::GenericParamKind::Const { is_host_effect: true, .. }
-                            )
-                        })
-                        .count(),
-                    generics.span,
-                ),
-                None => (0, *item_span),
-            };
+            // FIXME: This still doesn't count, e.g., elided lifetimes and APITs.
+            let mut actual_num = generics.params.len();
+            if target.is_associated_item() {
+                actual_num += self
+                    .parent_item
+                    .unwrap()
+                    .opt_generics()
+                    .map_or(0, |generics| generics.params.len());
+            }
 
             let mut at_least = false;
             let required = match lang_item.required_generics() {
                 GenericRequirement::Exact(num) if num != actual_num => Some(num),
                 GenericRequirement::Minimum(num) if actual_num < num => {
                     at_least = true;
-                    Some(num)}
-                ,
+                    Some(num)
+                }
                 // If the number matches, or there is no requirement, handle it normally
                 _ => None,
             };
@@ -190,10 +221,10 @@ impl<'tcx> LanguageItemCollector<'tcx> {
                 // item kind of the target is correct, the target is still wrong
                 // because of the wrong number of generic arguments.
                 self.tcx.sess.emit_err(IncorrectTarget {
-                    span,
-                    generics_span,
+                    span: attr_span,
+                    generics_span: generics.span,
                     name: name.as_str(),
-                    kind: kind.descr(),
+                    kind: target.name(),
                     num,
                     actual_num,
                     at_least,
@@ -204,58 +235,117 @@ impl<'tcx> LanguageItemCollector<'tcx> {
             }
         }
 
-        self.collect_item(lang_item, item_def_id.to_def_id());
+        self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span));
     }
 }
 
 /// Traverses and collects all the lang items in all crates.
 fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
+    let resolver = tcx.resolver_for_lowering(()).borrow();
+    let (resolver, krate) = &*resolver;
+
     // Initialize the collector.
-    let mut collector = LanguageItemCollector::new(tcx);
+    let mut collector = LanguageItemCollector::new(tcx, resolver);
 
     // Collect lang items in other crates.
     for &cnum in tcx.crates(()).iter() {
         for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() {
-            collector.collect_item(lang_item, def_id);
+            collector.collect_item(lang_item, def_id, None);
         }
     }
 
-    // Collect lang items in this crate.
-    let crate_items = tcx.hir_crate_items(());
+    // Collect lang items local to this crate.
+    visit::Visitor::visit_crate(&mut collector, krate);
 
-    for id in crate_items.items() {
-        collector
-            .check_for_lang(Target::from_def_kind(tcx.def_kind(id.owner_id)), id.owner_id.def_id);
+    // Find all required but not-yet-defined lang items.
+    weak_lang_items::check_crate(tcx, &mut collector.items, krate);
 
-        if matches!(tcx.def_kind(id.owner_id), DefKind::Enum) {
-            let item = tcx.hir().item(id);
-            if let hir::ItemKind::Enum(def, ..) = &item.kind {
-                for variant in def.variants {
-                    collector.check_for_lang(Target::Variant, variant.def_id);
-                }
-            }
-        }
-    }
+    // Return all the lang items that were found.
+    collector.items
+}
+
+impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
+    fn visit_item(&mut self, i: &'ast ast::Item) {
+        let target = match &i.kind {
+            ast::ItemKind::ExternCrate(_) => Target::ExternCrate,
+            ast::ItemKind::Use(_) => Target::Use,
+            ast::ItemKind::Static(_) => Target::Static,
+            ast::ItemKind::Const(_) => Target::Const,
+            ast::ItemKind::Fn(_) => Target::Fn,
+            ast::ItemKind::Mod(_, _) => Target::Mod,
+            ast::ItemKind::ForeignMod(_) => Target::ForeignFn,
+            ast::ItemKind::GlobalAsm(_) => Target::GlobalAsm,
+            ast::ItemKind::TyAlias(_) => Target::TyAlias,
+            ast::ItemKind::Enum(_, _) => Target::Enum,
+            ast::ItemKind::Struct(_, _) => Target::Struct,
+            ast::ItemKind::Union(_, _) => Target::Union,
+            ast::ItemKind::Trait(_) => Target::Trait,
+            ast::ItemKind::TraitAlias(_, _) => Target::TraitAlias,
+            ast::ItemKind::Impl(_) => Target::Impl,
+            ast::ItemKind::MacroDef(_) => Target::MacroDef,
+            ast::ItemKind::MacCall(_) => unreachable!("macros should have been expanded"),
+        };
 
-    // FIXME: avoid calling trait_item() when possible
-    for id in crate_items.trait_items() {
-        let item = tcx.hir().trait_item(id);
-        collector.check_for_lang(Target::from_trait_item(item), item.owner_id.def_id)
+        self.check_for_lang(
+            target,
+            self.resolver.node_id_to_def_id[&i.id],
+            &i.attrs,
+            i.span,
+            i.opt_generics(),
+        );
+
+        let parent_item = self.parent_item.replace(i);
+        visit::walk_item(self, i);
+        self.parent_item = parent_item;
     }
 
-    // FIXME: avoid calling impl_item() when possible
-    for id in crate_items.impl_items() {
-        let item = tcx.hir().impl_item(id);
-        collector.check_for_lang(target_from_impl_item(tcx, item), item.owner_id.def_id)
+    fn visit_enum_def(&mut self, enum_definition: &'ast ast::EnumDef) {
+        for variant in &enum_definition.variants {
+            self.check_for_lang(
+                Target::Variant,
+                self.resolver.node_id_to_def_id[&variant.id],
+                &variant.attrs,
+                variant.span,
+                None,
+            );
+        }
+
+        visit::walk_enum_def(self, enum_definition);
     }
 
-    // Extract out the found lang items.
-    let LanguageItemCollector { mut items, .. } = collector;
+    fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) {
+        let (target, generics) = match &i.kind {
+            ast::AssocItemKind::Fn(fun) => (
+                match &self.parent_item.unwrap().kind {
+                    ast::ItemKind::Impl(i) => {
+                        if i.of_trait.is_some() {
+                            Target::Method(MethodKind::Trait { body: fun.body.is_some() })
+                        } else {
+                            Target::Method(MethodKind::Inherent)
+                        }
+                    }
+                    ast::ItemKind::Trait(_) => {
+                        Target::Method(MethodKind::Trait { body: fun.body.is_some() })
+                    }
+                    _ => unreachable!(),
+                },
+                &fun.generics,
+            ),
+            ast::AssocItemKind::Const(ct) => (Target::AssocConst, &ct.generics),
+            ast::AssocItemKind::Type(ty) => (Target::AssocTy, &ty.generics),
+            ast::AssocItemKind::MacCall(_) => unreachable!("macros should have been expanded"),
+        };
 
-    // Find all required but not-yet-defined lang items.
-    weak_lang_items::check_crate(tcx, &mut items);
+        self.check_for_lang(
+            target,
+            self.resolver.node_id_to_def_id[&i.id],
+            &i.attrs,
+            i.span,
+            Some(generics),
+        );
 
-    items
+        visit::walk_assoc_item(self, i, ctxt);
+    }
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index 9a6fb88c281..b226c65e96c 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -1,5 +1,7 @@
 //! Validity checking for weak lang items
 
+use rustc_ast as ast;
+use rustc_ast::visit;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::lang_items::{self, LangItem};
 use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
@@ -11,7 +13,7 @@ use crate::errors::{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: TyCtxt<'_>, items: &mut lang_items::LanguageItems) {
+pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems, krate: &ast::Crate) {
     // These are never called by user code, they're generated by the compiler.
     // They will never implicitly be added to the `missing` array unless we do
     // so here.
@@ -22,24 +24,30 @@ pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems) {
         items.missing.push(LangItem::EhCatchTypeinfo);
     }
 
-    let crate_items = tcx.hir_crate_items(());
-    for id in crate_items.foreign_items() {
-        let attrs = tcx.hir().attrs(id.hir_id());
-        if let Some((lang_item, _)) = lang_items::extract(attrs) {
+    visit::Visitor::visit_crate(&mut WeakLangItemVisitor { tcx, items }, krate);
+
+    verify(tcx, items);
+}
+
+struct WeakLangItemVisitor<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    items: &'a mut lang_items::LanguageItems,
+}
+
+impl<'ast> visit::Visitor<'ast> for WeakLangItemVisitor<'_, '_> {
+    fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) {
+        if let Some((lang_item, _)) = lang_items::extract(&i.attrs) {
             if let Some(item) = LangItem::from_name(lang_item)
                 && item.is_weak()
             {
-                if items.get(item).is_none() {
-                    items.missing.push(item);
+                if self.items.get(item).is_none() {
+                    self.items.missing.push(item);
                 }
             } else {
-                let span = tcx.def_span(id.owner_id);
-                tcx.sess.emit_err(UnknownExternLangItem { span, lang_item });
+                self.tcx.sess.emit_err(UnknownExternLangItem { span: i.span, lang_item });
             }
         }
     }
-
-    verify(tcx, items);
 }
 
 fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) {
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index 3bca7894a29..6486ad8b483 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -923,7 +923,7 @@ impl ConstructorSet {
             }
             ConstructorSet::Integers { range_1, range_2 } => {
                 let seen_ranges: Vec<_> =
-                    seen.iter().map(|ctor| ctor.as_int_range().unwrap().clone()).collect();
+                    seen.iter().map(|ctor| *ctor.as_int_range().unwrap()).collect();
                 for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) {
                     match seen {
                         Presence::Unseen => missing.push(IntRange(splitted_range)),
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 6a8e23d9a8f..61f6a2b18ae 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -23,12 +23,11 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, CRATE_DEF_ID};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, Node, PatKind};
+use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, PatKind};
 use rustc_middle::bug;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
 use rustc_middle::query::Providers;
-use rustc_middle::span_bug;
 use rustc_middle::ty::GenericArgs;
 use rustc_middle::ty::{self, Const, GenericParamDefKind};
 use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
@@ -1766,7 +1765,15 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> {
 
 pub fn provide(providers: &mut Providers) {
     *providers = Providers {
-        visibility,
+        visibility: |tcx, def_id| {
+            // Unique types created for closures participate in type privacy checking.
+            // They have visibilities inherited from the module they are defined in.
+            // FIXME: Consider evaluating visibilities for closures eagerly, like for all
+            // other nodes. However, unlike for others, for closures it may cause a perf
+            // regression, because closure visibilities are not commonly queried.
+            assert_eq!(tcx.def_kind(def_id), DefKind::Closure);
+            ty::Visibility::Restricted(tcx.parent_module_from_def_id(def_id).to_def_id())
+        },
         effective_visibilities,
         check_private_in_public,
         check_mod_privacy,
@@ -1774,56 +1781,6 @@ pub fn provide(providers: &mut Providers) {
     };
 }
 
-fn visibility(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Visibility<DefId> {
-    local_visibility(tcx, def_id).to_def_id()
-}
-
-fn local_visibility(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Visibility {
-    match tcx.resolutions(()).visibilities.get(&def_id) {
-        Some(vis) => *vis,
-        None => {
-            let hir_id = tcx.local_def_id_to_hir_id(def_id);
-            match tcx.hir_node(hir_id) {
-                // Unique types created for closures participate in type privacy checking.
-                // They have visibilities inherited from the module they are defined in.
-                Node::Expr(hir::Expr { kind: hir::ExprKind::Closure{..}, .. })
-                // - AST lowering creates dummy `use` items which don't
-                //   get their entries in the resolver's visibility table.
-                // - AST lowering also creates opaque type items with inherited visibilities.
-                //   Visibility on them should have no effect, but to avoid the visibility
-                //   query failing on some items, we provide it for opaque types as well.
-                | Node::Item(hir::Item {
-                    kind: hir::ItemKind::Use(_, hir::UseKind::ListStem)
-                        | hir::ItemKind::OpaqueTy(..),
-                    ..
-                }) => ty::Visibility::Restricted(tcx.parent_module(hir_id).to_local_def_id()),
-                // Visibilities of trait impl items are inherited from their traits
-                // and are not filled in resolve.
-                Node::ImplItem(impl_item) => {
-                    match tcx.hir_node_by_def_id(tcx.hir().get_parent_item(hir_id).def_id) {
-                        Node::Item(hir::Item {
-                            kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(tr), .. }),
-                            ..
-                        }) => tr.path.res.opt_def_id().map_or_else(
-                            || {
-                                tcx.sess.span_delayed_bug(tr.path.span, "trait without a def-id");
-                                ty::Visibility::Public
-                            },
-                            |def_id| tcx.visibility(def_id).expect_local(),
-                        ),
-                        _ => span_bug!(impl_item.span, "the parent is not a trait impl"),
-                    }
-                }
-                _ => span_bug!(
-                    tcx.def_span(def_id),
-                    "visibility table unexpectedly missing a def-id: {:?}",
-                    def_id,
-                ),
-            }
-        }
-    }
-}
-
 fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
     // Check privacy of names not checked in previous compilation stages.
     let mut visitor = NamePrivacyVisitor {
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 3b8ccb67bbe..3556a5ec1d1 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -924,10 +924,10 @@ impl<D: Deps> DepGraphData<D> {
             // Promote the previous diagnostics to the current session.
             qcx.store_side_effects(dep_node_index, side_effects.clone());
 
-            let handle = qcx.dep_context().sess().diagnostic();
+            let dcx = qcx.dep_context().sess().dcx();
 
             for diagnostic in side_effects.diagnostics {
-                handle.emit_diagnostic(diagnostic);
+                dcx.emit_diagnostic(diagnostic);
             }
         }
     }
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index c431e966e44..2a34ffb75f2 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -5,7 +5,7 @@ use crate::query::DepKind;
 use crate::query::{QueryContext, QueryStackFrame};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
-    Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, Level,
+    DiagCtxt, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, Level,
 };
 use rustc_hir::def::DefKind;
 use rustc_session::Session;
@@ -604,18 +604,18 @@ pub(crate) fn report_cycle<'a>(
         note_span: (),
     };
 
-    cycle_diag.into_diagnostic(sess.diagnostic())
+    cycle_diag.into_diagnostic(sess.dcx())
 }
 
 pub fn print_query_stack<Qcx: QueryContext>(
     qcx: Qcx,
     mut current_query: Option<QueryJobId>,
-    handler: &Handler,
+    dcx: &DiagCtxt,
     num_frames: Option<usize>,
     mut file: Option<std::fs::File>,
 ) -> usize {
     // Be careful relying on global state here: this code is called from
-    // a panic hook, which means that the global `Handler` may be in a weird
+    // a panic hook, which means that the global `DiagCtxt` may be in a weird
     // state if it was responsible for triggering the panic.
     let mut count_printed = 0;
     let mut count_total = 0;
@@ -638,7 +638,7 @@ pub fn print_query_stack<Qcx: QueryContext>(
                 ),
             );
             diag.span = query_info.job.span.into();
-            handler.force_print_diagnostic(diag);
+            dcx.force_print_diagnostic(diag);
             count_printed += 1;
         }
 
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 213b320ed1a..7ff3c523685 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -236,7 +236,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                     // (i.e. variants, fields, and trait items) inherits from the visibility
                     // of the enum or trait.
                     ModuleKind::Def(DefKind::Enum | DefKind::Trait, def_id, _) => {
-                        self.r.visibilities[&def_id.expect_local()]
+                        self.r.tcx.visibility(def_id).expect_local()
                     }
                     // Otherwise, the visibility is restricted to the nearest parent `mod` item.
                     _ => ty::Visibility::Restricted(
@@ -404,6 +404,10 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             parent_prefix, use_tree, nested
         );
 
+        if nested {
+            self.r.feed_visibility(self.r.local_def_id(id), vis);
+        }
+
         let mut prefix_iter = parent_prefix
             .iter()
             .cloned()
@@ -442,8 +446,6 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                 let mut source = module_path.pop().unwrap();
                 let mut type_ns_only = false;
 
-                self.r.visibilities.insert(self.r.local_def_id(id), vis);
-
                 if nested {
                     // Correctly handle `self`
                     if source.ident.name == kw::SelfLower {
@@ -557,7 +559,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                     max_vis: Cell::new(None),
                     id,
                 };
-                self.r.visibilities.insert(self.r.local_def_id(id), vis);
+
                 self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis);
             }
             ast::UseTreeKind::Nested(ref items) => {
@@ -636,7 +638,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         let def_kind = self.r.tcx.def_kind(def_id);
         let res = Res::Def(def_kind, def_id);
 
-        self.r.visibilities.insert(local_def_id, vis);
+        self.r.feed_visibility(local_def_id, vis);
 
         match item.kind {
             ItemKind::Use(ref use_tree) => {
@@ -753,7 +755,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                     let ctor_def_id = self.r.local_def_id(ctor_node_id);
                     let ctor_res = self.res(ctor_def_id);
                     self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
-                    self.r.visibilities.insert(ctor_def_id, ctor_vis);
+                    self.r.feed_visibility(ctor_def_id, ctor_vis);
                     // We need the field visibility spans also for the constructor for E0603.
                     self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata);
 
@@ -897,7 +899,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         let expansion = self.parent_scope.expansion;
         let vis = self.resolve_visibility(&item.vis);
         self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
-        self.r.visibilities.insert(local_def_id, vis);
+        self.r.feed_visibility(local_def_id, vis);
     }
 
     fn build_reduced_graph_for_block(&mut self, block: &Block) {
@@ -1228,7 +1230,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                 self.r.check_reserved_macro_name(ident, res);
                 self.insert_unused_macro(ident, def_id, item.id);
             }
-            self.r.visibilities.insert(def_id, vis);
+            self.r.feed_visibility(def_id, vis);
             let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
                 self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
                     parent_macro_rules_scope: parent_scope.macro_rules,
@@ -1252,7 +1254,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                 self.insert_unused_macro(ident, def_id, item.id);
             }
             self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
-            self.r.visibilities.insert(def_id, vis);
+            self.r.feed_visibility(def_id, vis);
             self.parent_scope.macro_rules
         }
     }
@@ -1354,7 +1356,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             // Trait impl item visibility is inherited from its trait when not specified
             // explicitly. In that case we cannot determine it here in early resolve,
             // so we leave a hole in the visibility table to be filled later.
-            self.r.visibilities.insert(local_def_id, vis);
+            self.r.feed_visibility(local_def_id, vis);
         }
 
         if ctxt == AssocCtxt::Trait {
@@ -1432,7 +1434,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             self.visit_invoc(sf.id);
         } else {
             let vis = self.resolve_visibility(&sf.vis);
-            self.r.visibilities.insert(self.r.local_def_id(sf.id), vis);
+            self.r.feed_visibility(self.r.local_def_id(sf.id), vis);
             visit::walk_field_def(self, sf);
         }
     }
@@ -1453,7 +1455,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         let def_id = self.r.local_def_id(variant.id);
         let vis = self.resolve_visibility(&variant.vis);
         self.r.define(parent, ident, TypeNS, (self.res(def_id), vis, variant.span, expn_id));
-        self.r.visibilities.insert(def_id, vis);
+        self.r.feed_visibility(def_id, vis);
 
         // If the variant is marked as non_exhaustive then lower the visibility to within the crate.
         let ctor_vis =
@@ -1468,7 +1470,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             let ctor_def_id = self.r.local_def_id(ctor_node_id);
             let ctor_res = self.res(ctor_def_id);
             self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id));
-            self.r.visibilities.insert(ctor_def_id, ctor_vis);
+            self.r.feed_visibility(ctor_def_id, ctor_vis);
         }
 
         // Record field names for error reporting.
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index a71c50dd82f..50352169221 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -186,7 +186,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
     ) -> Option<Option<Visibility>> {
         match parent_id {
             ParentId::Def(def_id) => (nominal_vis != self.current_private_vis
-                && self.r.visibilities[&def_id] != self.current_private_vis)
+                && self.r.tcx.local_visibility(def_id) != self.current_private_vis)
                 .then_some(Some(self.current_private_vis)),
             ParentId::Import(_) => Some(None),
         }
@@ -222,7 +222,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
     }
 
     fn update_field(&mut self, def_id: LocalDefId, parent_id: LocalDefId) {
-        self.update_def(def_id, self.r.visibilities[&def_id], ParentId::Def(parent_id));
+        self.update_def(def_id, self.r.tcx.local_visibility(def_id), ParentId::Def(parent_id));
     }
 }
 
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 9c96e9a9bd7..037179350f0 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3112,6 +3112,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             | (DefKind::AssocFn, AssocItemKind::Fn(..))
             | (DefKind::AssocConst, AssocItemKind::Const(..)) => {
                 self.r.record_partial_res(id, PartialRes::new(res));
+                let vis = self.r.tcx.visibility(id_in_trait).expect_local();
+                self.r.feed_visibility(self.r.local_def_id(id), vis);
                 return;
             }
             _ => {}
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 70e0eb12c01..ad637472b47 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1007,8 +1007,7 @@ pub struct Resolver<'a, 'tcx> {
 
     /// Maps glob imports to the names of items actually imported.
     glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
-    /// Visibilities in "lowered" form, for all entities that have them.
-    visibilities: FxHashMap<LocalDefId, ty::Visibility>,
+    visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>,
     used_imports: FxHashSet<NodeId>,
     maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
 
@@ -1295,9 +1294,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             &mut FxHashMap::default(),
         );
 
-        let mut visibilities = FxHashMap::default();
-        visibilities.insert(CRATE_DEF_ID, ty::Visibility::Public);
-
         let mut def_id_to_node_id = IndexVec::default();
         assert_eq!(def_id_to_node_id.push(CRATE_NODE_ID), CRATE_DEF_ID);
         let mut node_id_to_def_id = FxHashMap::default();
@@ -1363,7 +1359,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             ast_transform_scopes: FxHashMap::default(),
 
             glob_map: Default::default(),
-            visibilities,
+            visibilities_for_hashing: Default::default(),
             used_imports: FxHashSet::default(),
             maybe_unused_trait_imports: Default::default(),
 
@@ -1450,6 +1446,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         let root_parent_scope = ParentScope::module(graph_root, &resolver);
         resolver.invocation_parent_scopes.insert(LocalExpnId::ROOT, root_parent_scope);
+        resolver.feed_visibility(CRATE_DEF_ID, ty::Visibility::Public);
 
         resolver
     }
@@ -1497,10 +1494,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         Default::default()
     }
 
+    fn feed_visibility(&mut self, def_id: LocalDefId, vis: ty::Visibility) {
+        self.tcx.feed_local_def_id(def_id).visibility(vis.to_def_id());
+        self.visibilities_for_hashing.push((def_id, vis));
+    }
+
     pub fn into_outputs(self) -> ResolverOutputs {
         let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
         let expn_that_defined = self.expn_that_defined;
-        let visibilities = self.visibilities;
         let extern_crate_map = self.extern_crate_map;
         let maybe_unused_trait_imports = self.maybe_unused_trait_imports;
         let glob_map = self.glob_map;
@@ -1517,7 +1518,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         let global_ctxt = ResolverGlobalCtxt {
             expn_that_defined,
-            visibilities,
+            visibilities_for_hashing: self.visibilities_for_hashing,
             effective_visibilities,
             extern_crate_map,
             module_children: self.module_children,
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index ca225416e36..1001286b6c2 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -205,10 +205,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
 
     fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) {
         if self.builtin_macros.insert(name, BuiltinMacroState::NotYetSeen(ext)).is_some() {
-            self.tcx
-                .sess
-                .diagnostic()
-                .bug(format!("built-in macro `{name}` was already registered"));
+            self.tcx.sess.dcx().bug(format!("built-in macro `{name}` was already registered"));
         }
     }
 
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index f95c0acd750..4ff4ccf5e98 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -407,8 +407,7 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
         doc,
         main_body_opts(),
         Some(&mut broken_link_callback),
-    )
-    .into_iter();
+    );
     let mut links = Vec::new();
 
     while let Some(event) = event_iter.next() {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index d46a309df55..0c21e4eb43e 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -7,11 +7,11 @@ use crate::errors::FileWriteFail;
 use crate::search_paths::SearchPath;
 use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use crate::{lint, HashStableContext};
-use crate::{EarlyErrorHandler, Session};
+use crate::{EarlyDiagCtxt, Session};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
 use rustc_errors::emitter::HumanReadableErrorType;
-use rustc_errors::{ColorConfig, DiagnosticArgValue, HandlerFlags, IntoDiagnosticArg};
+use rustc_errors::{ColorConfig, DiagCtxtFlags, DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_feature::UnstableFeatures;
 use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION};
 use rustc_span::source_map::FilePathMapping;
@@ -1155,8 +1155,8 @@ impl Options {
 }
 
 impl UnstableOptions {
-    pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags {
-        HandlerFlags {
+    pub fn dcx_flags(&self, can_emit_warnings: bool) -> DiagCtxtFlags {
+        DiagCtxtFlags {
             can_emit_warnings,
             treat_err_as_bug: self.treat_err_as_bug,
             dont_buffer_diagnostics: self.dont_buffer_diagnostics,
@@ -1579,12 +1579,12 @@ impl CheckCfg {
 pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg {
     // Combine the configuration requested by the session (command line) with
     // some default and generated configuration items.
-    user_cfg.extend(default_configuration(sess).into_iter());
+    user_cfg.extend(default_configuration(sess));
     user_cfg
 }
 
 pub(super) fn build_target_config(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     opts: &Options,
     target_override: Option<Target>,
     sysroot: &Path,
@@ -1594,17 +1594,17 @@ pub(super) fn build_target_config(
         |t| Ok((t, TargetWarnings::empty())),
     );
     let (target, target_warnings) = target_result.unwrap_or_else(|e| {
-        handler.early_error(format!(
+        early_dcx.early_error(format!(
             "Error loading target specification: {e}. \
                  Run `rustc --print target-list` for a list of built-in targets"
         ))
     });
     for warning in target_warnings.warning_messages() {
-        handler.early_warn(warning)
+        early_dcx.early_warn(warning)
     }
 
     if !matches!(target.pointer_width, 16 | 32 | 64) {
-        handler.early_error(format!(
+        early_dcx.early_error(format!(
             "target specification was invalid: unrecognized target-pointer-width {}",
             target.pointer_width
         ))
@@ -1844,7 +1844,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
 }
 
 pub fn get_cmd_lint_options(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
 ) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
     let mut lint_opts_with_position = vec![];
@@ -1869,14 +1869,14 @@ pub fn get_cmd_lint_options(
 
     let lint_cap = matches.opt_str("cap-lints").map(|cap| {
         lint::Level::from_str(&cap)
-            .unwrap_or_else(|| handler.early_error(format!("unknown lint level: `{cap}`")))
+            .unwrap_or_else(|| early_dcx.early_error(format!("unknown lint level: `{cap}`")))
     });
 
     (lint_opts, describe_lints, lint_cap)
 }
 
 /// Parses the `--color` flag.
-pub fn parse_color(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> ColorConfig {
+pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> ColorConfig {
     match matches.opt_str("color").as_deref() {
         Some("auto") => ColorConfig::Auto,
         Some("always") => ColorConfig::Always,
@@ -1884,7 +1884,7 @@ pub fn parse_color(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> C
 
         None => ColorConfig::Auto,
 
-        Some(arg) => handler.early_error(format!(
+        Some(arg) => early_dcx.early_error(format!(
             "argument for `--color` must be auto, \
                  always or never (instead was `{arg}`)"
         )),
@@ -1930,7 +1930,7 @@ impl JsonUnusedExterns {
 ///
 /// The first value returned is how to render JSON diagnostics, and the second
 /// is whether or not artifact notifications are enabled.
-pub fn parse_json(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> JsonConfig {
+pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig {
     let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
         HumanReadableErrorType::Default;
     let mut json_color = ColorConfig::Never;
@@ -1942,7 +1942,7 @@ pub fn parse_json(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Js
         // won't actually be emitting any colors and anything colorized is
         // embedded in a diagnostic message anyway.
         if matches.opt_str("color").is_some() {
-            handler.early_error("cannot specify the `--color` option with `--json`");
+            early_dcx.early_error("cannot specify the `--color` option with `--json`");
         }
 
         for sub_option in option.split(',') {
@@ -1953,7 +1953,7 @@ pub fn parse_json(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Js
                 "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
                 "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
                 "future-incompat" => json_future_incompat = true,
-                s => handler.early_error(format!("unknown `--json` option `{s}`")),
+                s => early_dcx.early_error(format!("unknown `--json` option `{s}`")),
             }
         }
     }
@@ -1968,7 +1968,7 @@ pub fn parse_json(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Js
 
 /// Parses the `--error-format` flag.
 pub fn parse_error_format(
-    handler: &mut EarlyErrorHandler,
+    early_dcx: &mut EarlyDiagCtxt,
     matches: &getopts::Matches,
     color: ColorConfig,
     json_rendered: HumanReadableErrorType,
@@ -1990,10 +1990,10 @@ pub fn parse_error_format(
             Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),
 
             Some(arg) => {
-                handler.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable(
+                early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable(
                     HumanReadableErrorType::Default(color),
                 ));
-                handler.early_error(format!(
+                early_dcx.early_error(format!(
                     "argument for `--error-format` must be `human`, `json` or \
                      `short` (instead was `{arg}`)"
                 ))
@@ -2010,7 +2010,7 @@ pub fn parse_error_format(
         // `--error-format=json`. This means that `--json` is specified we
         // should actually be emitting JSON blobs.
         _ if !matches.opt_strs("json").is_empty() => {
-            handler.early_error("using `--json` requires also using `--error-format=json`");
+            early_dcx.early_error("using `--json` requires also using `--error-format=json`");
         }
 
         _ => {}
@@ -2019,10 +2019,10 @@ pub fn parse_error_format(
     error_format
 }
 
-pub fn parse_crate_edition(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Edition {
+pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition {
     let edition = match matches.opt_str("edition") {
         Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
-            handler.early_error(format!(
+            early_dcx.early_error(format!(
                 "argument for `--edition` must be one of: \
                      {EDITION_NAME_LIST}. (instead was `{arg}`)"
             ))
@@ -2039,40 +2039,40 @@ pub fn parse_crate_edition(handler: &EarlyErrorHandler, matches: &getopts::Match
         } else {
             format!("edition {edition} is unstable and only available with -Z unstable-options")
         };
-        handler.early_error(msg)
+        early_dcx.early_error(msg)
     }
 
     edition
 }
 
 fn check_error_format_stability(
-    handler: &mut EarlyErrorHandler,
+    early_dcx: &mut EarlyDiagCtxt,
     unstable_opts: &UnstableOptions,
     error_format: ErrorOutputType,
     json_rendered: HumanReadableErrorType,
 ) {
     if !unstable_opts.unstable_options {
         if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
-            handler.abort_if_error_and_set_error_format(ErrorOutputType::Json {
+            early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::Json {
                 pretty: false,
                 json_rendered,
             });
-            handler.early_error("`--error-format=pretty-json` is unstable");
+            early_dcx.early_error("`--error-format=pretty-json` is unstable");
         }
         if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) =
             error_format
         {
-            handler.abort_if_error_and_set_error_format(ErrorOutputType::Json {
+            early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::Json {
                 pretty: false,
                 json_rendered,
             });
-            handler.early_error("`--error-format=human-annotate-rs` is unstable");
+            early_dcx.early_error("`--error-format=human-annotate-rs` is unstable");
         }
     }
 }
 
 fn parse_output_types(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     unstable_opts: &UnstableOptions,
     matches: &getopts::Matches,
 ) -> OutputTypes {
@@ -2082,7 +2082,7 @@ fn parse_output_types(
             for output_type in list.split(',') {
                 let (shorthand, path) = split_out_file_name(output_type);
                 let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
-                    handler.early_error(format!(
+                    early_dcx.early_error(format!(
                         "unknown emission type: `{shorthand}` - expected one of: {display}",
                         display = OutputType::shorthands_display(),
                     ))
@@ -2106,7 +2106,7 @@ fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) {
 }
 
 fn should_override_cgus_and_disable_thinlto(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     output_types: &OutputTypes,
     matches: &getopts::Matches,
     mut codegen_units: Option<usize>,
@@ -2126,12 +2126,12 @@ fn should_override_cgus_and_disable_thinlto(
             Some(n) if n > 1 => {
                 if matches.opt_present("o") {
                     for ot in &incompatible {
-                        handler.early_warn(format!(
+                        early_dcx.early_warn(format!(
                             "`--emit={ot}` with `-o` incompatible with \
                                  `-C codegen-units=N` for N > 1",
                         ));
                     }
-                    handler.early_warn("resetting to default -C codegen-units=1");
+                    early_dcx.early_warn("resetting to default -C codegen-units=1");
                     codegen_units = Some(1);
                     disable_local_thinlto = true;
                 }
@@ -2144,14 +2144,14 @@ fn should_override_cgus_and_disable_thinlto(
     }
 
     if codegen_units == Some(0) {
-        handler.early_error("value for codegen units must be a positive non-zero integer");
+        early_dcx.early_error("value for codegen units must be a positive non-zero integer");
     }
 
     (disable_local_thinlto, codegen_units)
 }
 
 fn collect_print_requests(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     cg: &mut CodegenOptions,
     unstable_opts: &mut UnstableOptions,
     matches: &getopts::Matches,
@@ -2204,7 +2204,7 @@ fn collect_print_requests(
                 if unstable_opts.unstable_options {
                     PrintKind::TargetSpec
                 } else {
-                    handler.early_error(
+                    early_dcx.early_error(
                         "the `-Z unstable-options` flag must also be passed to \
                          enable the target-spec-json print option",
                     );
@@ -2214,7 +2214,7 @@ fn collect_print_requests(
                 if unstable_opts.unstable_options {
                     PrintKind::AllTargetSpecs
                 } else {
-                    handler.early_error(
+                    early_dcx.early_error(
                         "the `-Z unstable-options` flag must also be passed to \
                          enable the all-target-specs-json print option",
                     );
@@ -2225,7 +2225,7 @@ fn collect_print_requests(
                 let prints =
                     PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
                 let prints = prints.join(", ");
-                handler.early_error(format!(
+                early_dcx.early_error(format!(
                     "unknown print request `{req}`. Valid print requests are: {prints}"
                 ));
             }
@@ -2234,7 +2234,7 @@ fn collect_print_requests(
         let out = out.unwrap_or(OutFileName::Stdout);
         if let OutFileName::Real(path) = &out {
             if !printed_paths.insert(path.clone()) {
-                handler.early_error(format!(
+                early_dcx.early_error(format!(
                     "cannot print multiple outputs to the same path: {}",
                     path.display(),
                 ));
@@ -2247,15 +2247,12 @@ fn collect_print_requests(
     prints
 }
 
-pub fn parse_target_triple(
-    handler: &EarlyErrorHandler,
-    matches: &getopts::Matches,
-) -> TargetTriple {
+pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTriple {
     match matches.opt_str("target") {
         Some(target) if target.ends_with(".json") => {
             let path = Path::new(&target);
             TargetTriple::from_path(path).unwrap_or_else(|_| {
-                handler.early_error(format!("target file {path:?} does not exist"))
+                early_dcx.early_error(format!("target file {path:?} does not exist"))
             })
         }
         Some(target) => TargetTriple::TargetTriple(target),
@@ -2264,7 +2261,7 @@ pub fn parse_target_triple(
 }
 
 fn parse_opt_level(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
     cg: &CodegenOptions,
 ) -> OptLevel {
@@ -2294,7 +2291,7 @@ fn parse_opt_level(
             "s" => OptLevel::Size,
             "z" => OptLevel::SizeMin,
             arg => {
-                handler.early_error(format!(
+                early_dcx.early_error(format!(
                     "optimization level needs to be \
                             between 0-3, s or z (instead was `{arg}`)"
                 ));
@@ -2317,21 +2314,21 @@ fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInf
 }
 
 fn parse_assert_incr_state(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     opt_assertion: &Option<String>,
 ) -> Option<IncrementalStateAssertion> {
     match opt_assertion {
         Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
         Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
         Some(s) => {
-            handler.early_error(format!("unexpected incremental state assertion value: {s}"))
+            early_dcx.early_error(format!("unexpected incremental state assertion value: {s}"))
         }
         None => None,
     }
 }
 
 fn parse_native_lib_kind(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
     kind: &str,
 ) -> (NativeLibKind, Option<bool>) {
@@ -2351,22 +2348,22 @@ fn parse_native_lib_kind(
                 } else {
                     ", the `-Z unstable-options` flag must also be passed to use it"
                 };
-                handler.early_error(format!("library kind `link-arg` is unstable{why}"))
+                early_dcx.early_error(format!("library kind `link-arg` is unstable{why}"))
             }
             NativeLibKind::LinkArg
         }
-        _ => handler.early_error(format!(
+        _ => early_dcx.early_error(format!(
             "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
         )),
     };
     match modifiers {
         None => (kind, None),
-        Some(modifiers) => parse_native_lib_modifiers(handler, kind, modifiers, matches),
+        Some(modifiers) => parse_native_lib_modifiers(early_dcx, kind, modifiers, matches),
     }
 }
 
 fn parse_native_lib_modifiers(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     mut kind: NativeLibKind,
     modifiers: &str,
     matches: &getopts::Matches,
@@ -2375,7 +2372,7 @@ fn parse_native_lib_modifiers(
     for modifier in modifiers.split(',') {
         let (modifier, value) = match modifier.strip_prefix(['+', '-']) {
             Some(m) => (m, modifier.starts_with('+')),
-            None => handler.early_error(
+            None => early_dcx.early_error(
                 "invalid linking modifier syntax, expected '+' or '-' prefix \
                  before one of: bundle, verbatim, whole-archive, as-needed",
             ),
@@ -2388,20 +2385,20 @@ fn parse_native_lib_modifiers(
                 } else {
                     ", the `-Z unstable-options` flag must also be passed to use it"
                 };
-                handler.early_error(format!("linking modifier `{modifier}` is unstable{why}"))
+                early_dcx.early_error(format!("linking modifier `{modifier}` is unstable{why}"))
             }
         };
         let assign_modifier = |dst: &mut Option<bool>| {
             if dst.is_some() {
                 let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
-                handler.early_error(msg)
+                early_dcx.early_error(msg)
             } else {
                 *dst = Some(value);
             }
         };
         match (modifier, &mut kind) {
             ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
-            ("bundle", _) => handler.early_error(
+            ("bundle", _) => early_dcx.early_error(
                 "linking modifier `bundle` is only compatible with `static` linking kind",
             ),
 
@@ -2410,7 +2407,7 @@ fn parse_native_lib_modifiers(
             ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
                 assign_modifier(whole_archive)
             }
-            ("whole-archive", _) => handler.early_error(
+            ("whole-archive", _) => early_dcx.early_error(
                 "linking modifier `whole-archive` is only compatible with `static` linking kind",
             ),
 
@@ -2419,14 +2416,14 @@ fn parse_native_lib_modifiers(
                 report_unstable_modifier();
                 assign_modifier(as_needed)
             }
-            ("as-needed", _) => handler.early_error(
+            ("as-needed", _) => early_dcx.early_error(
                 "linking modifier `as-needed` is only compatible with \
                  `dylib` and `framework` linking kinds",
             ),
 
             // Note: this error also excludes the case with empty modifier
             // string, like `modifiers = ""`.
-            _ => handler.early_error(format!(
+            _ => early_dcx.early_error(format!(
                 "unknown linking modifier `{modifier}`, expected one \
                      of: bundle, verbatim, whole-archive, as-needed"
             )),
@@ -2436,7 +2433,7 @@ fn parse_native_lib_modifiers(
     (kind, verbatim)
 }
 
-fn parse_libs(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Vec<NativeLib> {
+fn parse_libs(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Vec<NativeLib> {
     matches
         .opt_strs("l")
         .into_iter()
@@ -2450,7 +2447,7 @@ fn parse_libs(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Vec<Na
             let (name, kind, verbatim) = match s.split_once('=') {
                 None => (s, NativeLibKind::Unspecified, None),
                 Some((kind, name)) => {
-                    let (kind, verbatim) = parse_native_lib_kind(handler, matches, kind);
+                    let (kind, verbatim) = parse_native_lib_kind(early_dcx, matches, kind);
                     (name.to_string(), kind, verbatim)
                 }
             };
@@ -2460,7 +2457,7 @@ fn parse_libs(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Vec<Na
                 Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
             };
             if name.is_empty() {
-                handler.early_error("library name must not be empty");
+                early_dcx.early_error("library name must not be empty");
             }
             NativeLib { name, new_name, kind, verbatim }
         })
@@ -2468,7 +2465,7 @@ fn parse_libs(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Vec<Na
 }
 
 pub fn parse_externs(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
     unstable_opts: &UnstableOptions,
 ) -> Externs {
@@ -2496,7 +2493,7 @@ pub fn parse_externs(
         };
 
         if !is_ascii_ident(&name) {
-            let mut error = handler.early_struct_error(format!(
+            let mut error = early_dcx.early_struct_error(format!(
                 "crate name `{name}` passed to `--extern` is not a valid ASCII identifier"
             ));
             let adjusted_name = name.replace('-', "_");
@@ -2558,7 +2555,7 @@ pub fn parse_externs(
         let mut force = false;
         if let Some(opts) = options {
             if !is_unstable_enabled {
-                handler.early_error(
+                early_dcx.early_error(
                     "the `-Z unstable-options` flag must also be passed to \
                      enable `--extern` options",
                 );
@@ -2570,14 +2567,14 @@ pub fn parse_externs(
                         if let ExternLocation::ExactPaths(_) = &entry.location {
                             add_prelude = false;
                         } else {
-                            handler.early_error(
+                            early_dcx.early_error(
                                 "the `noprelude` --extern option requires a file path",
                             );
                         }
                     }
                     "nounused" => nounused_dep = true,
                     "force" => force = true,
-                    _ => handler.early_error(format!("unknown --extern option `{opt}`")),
+                    _ => early_dcx.early_error(format!("unknown --extern option `{opt}`")),
                 }
             }
         }
@@ -2596,7 +2593,7 @@ pub fn parse_externs(
 }
 
 fn parse_remap_path_prefix(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
     unstable_opts: &UnstableOptions,
 ) -> Vec<(PathBuf, PathBuf)> {
@@ -2604,7 +2601,9 @@ fn parse_remap_path_prefix(
         .opt_strs("remap-path-prefix")
         .into_iter()
         .map(|remap| match remap.rsplit_once('=') {
-            None => handler.early_error("--remap-path-prefix must contain '=' between FROM and TO"),
+            None => {
+                early_dcx.early_error("--remap-path-prefix must contain '=' between FROM and TO")
+            }
             Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
         })
         .collect();
@@ -2619,7 +2618,7 @@ fn parse_remap_path_prefix(
 }
 
 fn parse_logical_env(
-    handler: &mut EarlyErrorHandler,
+    early_dcx: &mut EarlyDiagCtxt,
     matches: &getopts::Matches,
 ) -> FxIndexMap<String, String> {
     let mut vars = FxIndexMap::default();
@@ -2628,7 +2627,7 @@ fn parse_logical_env(
         if let Some((name, val)) = arg.split_once('=') {
             vars.insert(name.to_string(), val.to_string());
         } else {
-            handler.early_error(format!("`--env`: specify value for variable `{arg}`"));
+            early_dcx.early_error(format!("`--env`: specify value for variable `{arg}`"));
         }
     }
 
@@ -2637,87 +2636,88 @@ fn parse_logical_env(
 
 // JUSTIFICATION: before wrapper fn is available
 #[allow(rustc::bad_opt_access)]
-pub fn build_session_options(
-    handler: &mut EarlyErrorHandler,
-    matches: &getopts::Matches,
-) -> Options {
-    let color = parse_color(handler, matches);
+pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches) -> Options {
+    let color = parse_color(early_dcx, matches);
 
-    let edition = parse_crate_edition(handler, matches);
+    let edition = parse_crate_edition(early_dcx, matches);
 
     let JsonConfig {
         json_rendered,
         json_artifact_notifications,
         json_unused_externs,
         json_future_incompat,
-    } = parse_json(handler, matches);
+    } = parse_json(early_dcx, matches);
 
-    let error_format = parse_error_format(handler, matches, color, json_rendered);
+    let error_format = parse_error_format(early_dcx, matches, color, json_rendered);
 
-    handler.abort_if_error_and_set_error_format(error_format);
+    early_dcx.abort_if_error_and_set_error_format(error_format);
 
     let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
-        handler.early_error("`--diagnostic-width` must be an positive integer");
+        early_dcx.early_error("`--diagnostic-width` must be an positive integer");
     });
 
     let unparsed_crate_types = matches.opt_strs("crate-type");
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
-        .unwrap_or_else(|e| handler.early_error(e));
+        .unwrap_or_else(|e| early_dcx.early_error(e));
 
-    let mut unstable_opts = UnstableOptions::build(handler, matches);
-    let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(handler, matches);
+    let mut unstable_opts = UnstableOptions::build(early_dcx, matches);
+    let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
 
-    check_error_format_stability(handler, &unstable_opts, error_format, json_rendered);
+    check_error_format_stability(early_dcx, &unstable_opts, error_format, json_rendered);
 
     if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
-        handler.early_error(
+        early_dcx.early_error(
             "the `-Z unstable-options` flag must also be passed to enable \
             the flag `--json=unused-externs`",
         );
     }
 
-    let output_types = parse_output_types(handler, &unstable_opts, matches);
+    let output_types = parse_output_types(early_dcx, &unstable_opts, matches);
 
-    let mut cg = CodegenOptions::build(handler, matches);
-    let (disable_local_thinlto, mut codegen_units) =
-        should_override_cgus_and_disable_thinlto(handler, &output_types, matches, cg.codegen_units);
+    let mut cg = CodegenOptions::build(early_dcx, matches);
+    let (disable_local_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto(
+        early_dcx,
+        &output_types,
+        matches,
+        cg.codegen_units,
+    );
 
     if unstable_opts.threads == 0 {
-        handler.early_error("value for threads must be a positive non-zero integer");
+        early_dcx.early_error("value for threads must be a positive non-zero integer");
     }
 
     let fuel = unstable_opts.fuel.is_some() || unstable_opts.print_fuel.is_some();
     if fuel && unstable_opts.threads > 1 {
-        handler.early_error("optimization fuel is incompatible with multiple threads");
+        early_dcx.early_error("optimization fuel is incompatible with multiple threads");
     }
     if fuel && cg.incremental.is_some() {
-        handler.early_error("optimization fuel is incompatible with incremental compilation");
+        early_dcx.early_error("optimization fuel is incompatible with incremental compilation");
     }
 
     let incremental = cg.incremental.as_ref().map(PathBuf::from);
 
-    let assert_incr_state = parse_assert_incr_state(handler, &unstable_opts.assert_incr_state);
+    let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state);
 
     if unstable_opts.profile && incremental.is_some() {
-        handler.early_error("can't instrument with gcov profiling when compiling incrementally");
+        early_dcx.early_error("can't instrument with gcov profiling when compiling incrementally");
     }
     if unstable_opts.profile {
         match codegen_units {
             Some(1) => {}
             None => codegen_units = Some(1),
-            Some(_) => handler
+            Some(_) => early_dcx
                 .early_error("can't instrument with gcov profiling with multiple codegen units"),
         }
     }
 
     if cg.profile_generate.enabled() && cg.profile_use.is_some() {
-        handler.early_error("options `-C profile-generate` and `-C profile-use` are exclusive");
+        early_dcx.early_error("options `-C profile-generate` and `-C profile-use` are exclusive");
     }
 
     if unstable_opts.profile_sample_use.is_some()
         && (cg.profile_generate.enabled() || cg.profile_use.is_some())
     {
-        handler.early_error(
+        early_dcx.early_error(
             "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
         );
     }
@@ -2730,7 +2730,7 @@ pub fn build_session_options(
         // Unstable values:
         Some(SymbolManglingVersion::Legacy) => {
             if !unstable_opts.unstable_options {
-                handler.early_error(
+                early_dcx.early_error(
                     "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
                 );
             }
@@ -2747,7 +2747,7 @@ pub fn build_session_options(
         | InstrumentCoverage::ExceptUnusedFunctions
         | InstrumentCoverage::ExceptUnusedGenerics => {
             if !unstable_opts.unstable_options {
-                handler.early_error(
+                early_dcx.early_error(
                     "`-C instrument-coverage=branch` and `-C instrument-coverage=except-*` \
                     require `-Z unstable-options`",
                 );
@@ -2757,7 +2757,7 @@ pub fn build_session_options(
 
     if cg.instrument_coverage != InstrumentCoverage::Off {
         if cg.profile_generate.enabled() || cg.profile_use.is_some() {
-            handler.early_error(
+            early_dcx.early_error(
                 "option `-C instrument-coverage` is not compatible with either `-C profile-use` \
                 or `-C profile-generate`",
             );
@@ -2770,7 +2770,7 @@ pub fn build_session_options(
         match cg.symbol_mangling_version {
             None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0),
             Some(SymbolManglingVersion::Legacy) => {
-                handler.early_warn(
+                early_dcx.early_warn(
                     "-C instrument-coverage requires symbol mangling version `v0`, \
                     but `-C symbol-mangling-version=legacy` was specified",
                 );
@@ -2787,7 +2787,7 @@ pub fn build_session_options(
         match cg.lto {
             LtoCli::No | LtoCli::Unspecified => {}
             LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => {
-                handler.early_error("options `-C embed-bitcode=no` and `-C lto` are incompatible")
+                early_dcx.early_error("options `-C embed-bitcode=no` and `-C lto` are incompatible")
             }
         }
     }
@@ -2799,7 +2799,7 @@ pub fn build_session_options(
         let uses_unstable_self_contained_option =
             cg.link_self_contained.are_unstable_variants_set();
         if uses_unstable_self_contained_option {
-            handler.early_error(
+            early_dcx.early_error(
                 "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \
                 the `-Z unstable-options` flag must also be passed to use the unstable values",
             );
@@ -2807,7 +2807,7 @@ pub fn build_session_options(
 
         if let Some(flavor) = cg.linker_flavor {
             if flavor.is_unstable() {
-                handler.early_error(format!(
+                early_dcx.early_error(format!(
                     "the linker flavor `{}` is unstable, the `-Z unstable-options` \
                         flag must also be passed to use the unstable values",
                     flavor.desc()
@@ -2824,18 +2824,18 @@ pub fn build_session_options(
             .map(|c| c.as_str().unwrap())
             .intersperse(", ")
             .collect();
-        handler.early_error(format!(
+        early_dcx.early_error(format!(
             "some `-C link-self-contained` components were both enabled and disabled: {names}"
         ));
     }
 
-    let prints = collect_print_requests(handler, &mut cg, &mut unstable_opts, matches);
+    let prints = collect_print_requests(early_dcx, &mut cg, &mut unstable_opts, matches);
 
     let cg = cg;
 
     let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
-    let target_triple = parse_target_triple(handler, matches);
-    let opt_level = parse_opt_level(handler, matches, &cg);
+    let target_triple = parse_target_triple(early_dcx, matches);
+    let opt_level = parse_opt_level(early_dcx, matches, &cg);
     // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
     // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
     // for more details.
@@ -2845,35 +2845,36 @@ pub fn build_session_options(
 
     let mut search_paths = vec![];
     for s in &matches.opt_strs("L") {
-        search_paths.push(SearchPath::from_cli_opt(handler, s));
+        search_paths.push(SearchPath::from_cli_opt(early_dcx, s));
     }
 
-    let libs = parse_libs(handler, matches);
+    let libs = parse_libs(early_dcx, matches);
 
     let test = matches.opt_present("test");
 
     if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
-        handler.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations");
+        early_dcx.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations");
     }
 
     if cg.remark.is_empty() && unstable_opts.remark_dir.is_some() {
-        handler.early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all");
+        early_dcx
+            .early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all");
     }
 
-    let externs = parse_externs(handler, matches, &unstable_opts);
+    let externs = parse_externs(early_dcx, matches, &unstable_opts);
 
     let crate_name = matches.opt_str("crate-name");
 
-    let remap_path_prefix = parse_remap_path_prefix(handler, matches, &unstable_opts);
+    let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts);
 
-    let pretty = parse_pretty(handler, &unstable_opts);
+    let pretty = parse_pretty(early_dcx, &unstable_opts);
 
     // query-dep-graph is required if dump-dep-graph is given #106736
     if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
-        handler.early_error("can't dump dependency graph without `-Z query-dep-graph`");
+        early_dcx.early_error("can't dump dependency graph without `-Z query-dep-graph`");
     }
 
-    let logical_env = parse_logical_env(handler, matches);
+    let logical_env = parse_logical_env(early_dcx, matches);
 
     // Try to find a directory containing the Rust `src`, for more details see
     // the doc comment on the `real_rust_source_base_dir` field.
@@ -2904,7 +2905,7 @@ pub fn build_session_options(
     };
 
     let working_dir = std::env::current_dir().unwrap_or_else(|e| {
-        handler.early_error(format!("Current directory is invalid: {e}"));
+        early_dcx.early_error(format!("Current directory is invalid: {e}"));
     });
 
     let remap = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
@@ -2959,7 +2960,7 @@ pub fn build_session_options(
     }
 }
 
-fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) -> Option<PpMode> {
+fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> Option<PpMode> {
     use PpMode::*;
 
     let first = match unstable_opts.unpretty.as_deref()? {
@@ -2979,7 +2980,7 @@ fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) ->
         "mir" => Mir,
         "stable-mir" => StableMir,
         "mir-cfg" => MirCFG,
-        name => handler.early_error(format!(
+        name => early_dcx.early_error(format!(
             "argument to `unpretty` must be one of `normal`, `identified`, \
                             `expanded`, `expanded,identified`, `expanded,hygiene`, \
                             `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
@@ -3026,7 +3027,7 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
 
 pub mod nightly_options {
     use super::{OptionStability, RustcOptGroup};
-    use crate::EarlyErrorHandler;
+    use crate::EarlyDiagCtxt;
     use rustc_feature::UnstableFeatures;
 
     pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
@@ -3043,7 +3044,7 @@ pub mod nightly_options {
     }
 
     pub fn check_nightly_options(
-        handler: &EarlyErrorHandler,
+        early_dcx: &EarlyDiagCtxt,
         matches: &getopts::Matches,
         flags: &[RustcOptGroup],
     ) {
@@ -3059,7 +3060,7 @@ pub mod nightly_options {
                 continue;
             }
             if opt.name != "Z" && !has_z_unstable_option {
-                handler.early_error(format!(
+                early_dcx.early_error(format!(
                     "the `-Z unstable-options` flag must also be passed to enable \
                          the flag `{}`",
                     opt.name
@@ -3075,17 +3076,17 @@ pub mod nightly_options {
                         "the option `{}` is only accepted on the nightly compiler",
                         opt.name
                     );
-                    let _ = handler.early_error_no_abort(msg);
+                    let _ = early_dcx.early_error_no_abort(msg);
                 }
                 OptionStability::Stable => {}
             }
         }
         if nightly_options_on_stable > 0 {
-            handler
+            early_dcx
                 .early_help("consider switching to a nightly toolchain: `rustup default nightly`");
-            handler.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
-            handler.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
-            handler.early_error(format!(
+            early_dcx.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
+            early_dcx.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
+            early_dcx.early_error(format!(
                 "{} nightly option{} were parsed",
                 nightly_options_on_stable,
                 if nightly_options_on_stable > 1 { "s" } else { "" }
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index aab7595ef6e..c3360815ac9 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -17,9 +17,9 @@ impl<'a> IntoDiagnostic<'a> for FeatureGateError {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &'a rustc_errors::Handler,
+        dcx: &'a rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(self.explain);
+        let mut diag = dcx.struct_err(self.explain);
         diag.set_span(self.span);
         diag.code(error_code!(E0658));
         diag
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index e8ca556aa42..06b554e8e63 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2,7 +2,7 @@ use crate::config::*;
 
 use crate::search_paths::SearchPath;
 use crate::utils::NativeLib;
-use crate::{lint, EarlyErrorHandler};
+use crate::{lint, EarlyDiagCtxt};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::profiling::TimePassesFormat;
 use rustc_data_structures::stable_hasher::Hash64;
@@ -255,10 +255,10 @@ macro_rules! options {
 
     impl $struct_name {
         pub fn build(
-            handler: &EarlyErrorHandler,
+            early_dcx: &EarlyDiagCtxt,
             matches: &getopts::Matches,
         ) -> $struct_name {
-            build_options(handler, matches, $stat, $prefix, $outputname)
+            build_options(early_dcx, matches, $stat, $prefix, $outputname)
         }
 
         fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
@@ -319,7 +319,7 @@ type OptionSetter<O> = fn(&mut O, v: Option<&str>) -> bool;
 type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)];
 
 fn build_options<O: Default>(
-    handler: &EarlyErrorHandler,
+    early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
     descrs: OptionDescrs<O>,
     prefix: &str,
@@ -337,12 +337,12 @@ fn build_options<O: Default>(
             Some((_, setter, type_desc, _)) => {
                 if !setter(&mut op, value) {
                     match value {
-                        None => handler.early_error(
+                        None => early_dcx.early_error(
                             format!(
                                 "{outputname} option `{key}` requires {type_desc} ({prefix} {key}=<value>)"
                             ),
                         ),
-                        Some(value) => handler.early_error(
+                        Some(value) => early_dcx.early_error(
                             format!(
                                 "incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected"
                             ),
@@ -350,7 +350,7 @@ fn build_options<O: Default>(
                     }
                 }
             }
-            None => handler.early_error(format!("unknown {outputname} option: `{key}`")),
+            None => early_dcx.early_error(format!("unknown {outputname} option: `{key}`")),
         }
     }
     return op;
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 525f00f5cd0..2cb47e3a932 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -11,7 +11,7 @@ use crate::lint::{
 use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc};
-use rustc_errors::{emitter::SilentEmitter, Handler};
+use rustc_errors::{emitter::SilentEmitter, DiagCtxt};
 use rustc_errors::{
     fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
     ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey,
@@ -103,8 +103,7 @@ pub fn feature_err_issue(
 
     // Cancel an earlier warning for this same error, if it exists.
     if let Some(span) = span.primary_span() {
-        if let Some(err) = sess.span_diagnostic.steal_diagnostic(span, StashKey::EarlySyntaxWarning)
-        {
+        if let Some(err) = sess.dcx.steal_diagnostic(span, StashKey::EarlySyntaxWarning) {
             err.cancel()
         }
     }
@@ -138,7 +137,7 @@ pub fn feature_warn_issue(
     issue: GateIssue,
     explain: &'static str,
 ) {
-    let mut err = sess.span_diagnostic.struct_span_warn(span, explain);
+    let mut err = sess.dcx.struct_span_warn(span, explain);
     add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false);
 
     // Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level
@@ -189,7 +188,7 @@ pub fn add_feature_diagnostics_for_issue(
 
 /// Info about a parsing session.
 pub struct ParseSess {
-    pub span_diagnostic: Handler,
+    pub dcx: DiagCtxt,
     pub unstable_features: UnstableFeatures,
     pub config: Cfg,
     pub check_config: CheckCfg,
@@ -227,13 +226,13 @@ impl ParseSess {
     pub fn new(locale_resources: Vec<&'static str>, file_path_mapping: FilePathMapping) -> Self {
         let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
         let sm = Lrc::new(SourceMap::new(file_path_mapping));
-        let handler = Handler::with_tty_emitter(Some(sm.clone()), fallback_bundle);
-        ParseSess::with_span_handler(handler, sm)
+        let dcx = DiagCtxt::with_tty_emitter(Some(sm.clone()), fallback_bundle);
+        ParseSess::with_dcx(dcx, sm)
     }
 
-    pub fn with_span_handler(handler: Handler, source_map: Lrc<SourceMap>) -> Self {
+    pub fn with_dcx(dcx: DiagCtxt, source_map: Lrc<SourceMap>) -> Self {
         Self {
-            span_diagnostic: handler,
+            dcx,
             unstable_features: UnstableFeatures::from_environment(None),
             config: Cfg::default(),
             check_config: CheckCfg::default(),
@@ -256,10 +255,10 @@ impl ParseSess {
     pub fn with_silent_emitter(fatal_note: Option<String>) -> Self {
         let fallback_bundle = fallback_fluent_bundle(Vec::new(), false);
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let fatal_handler = Handler::with_tty_emitter(None, fallback_bundle).disable_warnings();
-        let handler = Handler::with_emitter(Box::new(SilentEmitter { fatal_handler, fatal_note }))
+        let fatal_dcx = DiagCtxt::with_tty_emitter(None, fallback_bundle).disable_warnings();
+        let dcx = DiagCtxt::with_emitter(Box::new(SilentEmitter { fatal_dcx, fatal_note }))
             .disable_warnings();
-        ParseSess::with_span_handler(handler, sm)
+        ParseSess::with_dcx(dcx, sm)
     }
 
     #[inline]
@@ -323,7 +322,7 @@ impl ParseSess {
         &'a self,
         err: impl IntoDiagnostic<'a>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        err.into_diagnostic(&self.span_diagnostic)
+        err.into_diagnostic(&self.dcx)
     }
 
     #[track_caller]
@@ -336,7 +335,7 @@ impl ParseSess {
         &'a self,
         warning: impl IntoDiagnostic<'a, ()>,
     ) -> DiagnosticBuilder<'a, ()> {
-        warning.into_diagnostic(&self.span_diagnostic)
+        warning.into_diagnostic(&self.dcx)
     }
 
     #[track_caller]
@@ -349,7 +348,7 @@ impl ParseSess {
         &'a self,
         note: impl IntoDiagnostic<'a, Noted>,
     ) -> DiagnosticBuilder<'a, Noted> {
-        note.into_diagnostic(&self.span_diagnostic)
+        note.into_diagnostic(&self.dcx)
     }
 
     #[track_caller]
@@ -362,7 +361,7 @@ impl ParseSess {
         &'a self,
         fatal: impl IntoDiagnostic<'a, !>,
     ) -> DiagnosticBuilder<'a, !> {
-        fatal.into_diagnostic(&self.span_diagnostic)
+        fatal.into_diagnostic(&self.dcx)
     }
 
     #[track_caller]
@@ -376,18 +375,18 @@ impl ParseSess {
         &self,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        self.span_diagnostic.struct_err(msg)
+        self.dcx.struct_err(msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        self.span_diagnostic.struct_warn(msg)
+        self.dcx.struct_warn(msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
-        self.span_diagnostic.struct_fatal(msg)
+        self.dcx.struct_fatal(msg)
     }
 }
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index 07e78d1760e..8ed50f6a14f 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -1,5 +1,5 @@
 use crate::filesearch::make_target_lib_path;
-use crate::EarlyErrorHandler;
+use crate::EarlyDiagCtxt;
 use std::path::{Path, PathBuf};
 
 #[derive(Clone, Debug)]
@@ -46,7 +46,7 @@ impl PathKind {
 }
 
 impl SearchPath {
-    pub fn from_cli_opt(handler: &EarlyErrorHandler, path: &str) -> Self {
+    pub fn from_cli_opt(early_dcx: &EarlyDiagCtxt, path: &str) -> Self {
         let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") {
             (PathKind::Native, stripped)
         } else if let Some(stripped) = path.strip_prefix("crate=") {
@@ -61,7 +61,7 @@ impl SearchPath {
             (PathKind::All, path)
         };
         if path.is_empty() {
-            handler.early_error("empty search path given via `-L`");
+            early_dcx.early_error("empty search path given via `-L`");
         }
 
         let dir = PathBuf::from(path);
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 08a9b3d9fa0..7f168572f7d 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -22,9 +22,9 @@ use rustc_errors::emitter::{DynEmitter, EmitterWriter, HumanReadableErrorType};
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
 use rustc_errors::{
-    error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
-    ErrorGuaranteed, FluentBundle, Handler, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
-    TerminalUrl,
+    error_code, fallback_fluent_bundle, DiagCtxt, DiagnosticBuilder, DiagnosticId,
+    DiagnosticMessage, ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle,
+    MultiSpan, Noted, TerminalUrl,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -289,7 +289,7 @@ impl Session {
     /// Invoked all the way at the end to finish off diagnostics printing.
     pub fn finish_diagnostics(&self, registry: &Registry) {
         self.check_miri_unleashed_features();
-        self.diagnostic().print_error_count(registry);
+        self.dcx().print_error_count(registry);
         self.emit_future_breakage();
     }
 
@@ -298,11 +298,11 @@ impl Session {
             return;
         }
 
-        let diags = self.diagnostic().take_future_breakage_diagnostics();
+        let diags = self.dcx().take_future_breakage_diagnostics();
         if diags.is_empty() {
             return;
         }
-        self.diagnostic().emit_future_breakage_report(diags);
+        self.dcx().emit_future_breakage_report(diags);
     }
 
     /// Returns true if the crate is a testing one.
@@ -317,7 +317,7 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_span_warn(sp, msg)
+        self.dcx().struct_span_warn(sp, msg)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -327,7 +327,7 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         id: lint::LintExpectationId,
     ) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_span_warn_with_expectation(sp, msg, id)
+        self.dcx().struct_span_warn_with_expectation(sp, msg, id)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -337,12 +337,12 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_span_warn_with_code(sp, msg, code)
+        self.dcx().struct_span_warn_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_warn(msg)
+        self.dcx().struct_warn(msg)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -351,7 +351,7 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         id: lint::LintExpectationId,
     ) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_warn_with_expectation(msg, id)
+        self.dcx().struct_warn_with_expectation(msg, id)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -360,12 +360,12 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_span_allow(sp, msg)
+        self.dcx().struct_span_allow(sp, msg)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_allow(msg)
+        self.dcx().struct_allow(msg)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -374,7 +374,7 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         id: lint::LintExpectationId,
     ) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_expect(msg, id)
+        self.dcx().struct_expect(msg, id)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -383,7 +383,7 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        self.diagnostic().struct_span_err(sp, msg)
+        self.dcx().struct_span_err(sp, msg)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -393,7 +393,7 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        self.diagnostic().struct_span_err_with_code(sp, msg, code)
+        self.dcx().struct_span_err_with_code(sp, msg, code)
     }
     // FIXME: This method should be removed (every error should have an associated error code).
     #[rustc_lint_diagnostics]
@@ -411,7 +411,7 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        self.diagnostic().struct_err_with_code(msg, code)
+        self.dcx().struct_err_with_code(msg, code)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -420,7 +420,7 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_warn_with_code(msg, code)
+        self.dcx().struct_warn_with_code(msg, code)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -429,7 +429,7 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, !> {
-        self.diagnostic().struct_span_fatal(sp, msg)
+        self.dcx().struct_span_fatal(sp, msg)
     }
     #[rustc_lint_diagnostics]
     pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(
@@ -438,17 +438,17 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) -> DiagnosticBuilder<'_, !> {
-        self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
+        self.dcx().struct_span_fatal_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
     pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
-        self.diagnostic().struct_fatal(msg)
+        self.dcx().struct_fatal(msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.diagnostic().span_fatal(sp, msg)
+        self.dcx().span_fatal(sp, msg)
     }
     #[rustc_lint_diagnostics]
     pub fn span_fatal_with_code<S: Into<MultiSpan>>(
@@ -457,11 +457,11 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) -> ! {
-        self.diagnostic().span_fatal_with_code(sp, msg, code)
+        self.dcx().span_fatal_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
     pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.diagnostic().fatal(msg)
+        self.dcx().fatal(msg)
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -470,7 +470,7 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
     ) -> ErrorGuaranteed {
-        self.diagnostic().span_err(sp, msg)
+        self.dcx().span_err(sp, msg)
     }
     #[rustc_lint_diagnostics]
     pub fn span_err_with_code<S: Into<MultiSpan>>(
@@ -479,13 +479,13 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) -> ErrorGuaranteed {
-        self.diagnostic().span_err_with_code(sp, msg, code)
+        self.dcx().span_err_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
-        self.diagnostic().err(msg)
+        self.dcx().err(msg)
     }
     #[track_caller]
     pub fn create_err<'a>(
@@ -546,23 +546,23 @@ impl Session {
     }
     #[inline]
     pub fn err_count(&self) -> usize {
-        self.diagnostic().err_count()
+        self.dcx().err_count()
     }
     pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
-        self.diagnostic().has_errors()
+        self.dcx().has_errors()
     }
     pub fn has_errors_or_span_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
-        self.diagnostic().has_errors_or_span_delayed_bugs()
+        self.dcx().has_errors_or_span_delayed_bugs()
     }
     pub fn is_compilation_going_to_fail(&self) -> Option<ErrorGuaranteed> {
-        self.diagnostic().is_compilation_going_to_fail()
+        self.dcx().is_compilation_going_to_fail()
     }
     pub fn abort_if_errors(&self) {
-        self.diagnostic().abort_if_errors();
+        self.dcx().abort_if_errors();
     }
     pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> {
-        if let Some(reported) = self.diagnostic().has_errors_or_lint_errors() {
-            let _ = self.diagnostic().emit_stashed_diagnostics();
+        if let Some(reported) = self.dcx().has_errors_or_lint_errors() {
+            let _ = self.dcx().emit_stashed_diagnostics();
             Err(reported)
         } else {
             Ok(())
@@ -590,7 +590,7 @@ impl Session {
     #[allow(rustc::diagnostic_outside_of_impl)]
     #[track_caller]
     pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
-        self.diagnostic().span_warn(sp, msg)
+        self.dcx().span_warn(sp, msg)
     }
 
     #[rustc_lint_diagnostics]
@@ -602,14 +602,14 @@ impl Session {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) {
-        self.diagnostic().span_warn_with_code(sp, msg, code)
+        self.dcx().span_warn_with_code(sp, msg, code)
     }
 
     #[rustc_lint_diagnostics]
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
-        self.diagnostic().warn(msg)
+        self.dcx().warn(msg)
     }
 
     /// Ensures that compilation cannot succeed.
@@ -634,7 +634,7 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
     ) -> ErrorGuaranteed {
-        self.diagnostic().span_delayed_bug(sp, msg)
+        self.dcx().span_delayed_bug(sp, msg)
     }
 
     /// Used for code paths of expensive computations that should only take place when
@@ -651,14 +651,14 @@ impl Session {
             return;
         }
 
-        self.diagnostic().good_path_delayed_bug(msg)
+        self.dcx().good_path_delayed_bug(msg)
     }
 
     #[rustc_lint_diagnostics]
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn note(&self, msg: impl Into<DiagnosticMessage>) {
-        self.diagnostic().note(msg)
+        self.dcx().note(msg)
     }
 
     #[track_caller]
@@ -666,19 +666,19 @@ impl Session {
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn span_note<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
-        self.diagnostic().span_note(sp, msg)
+        self.dcx().span_note(sp, msg)
     }
 
     #[rustc_lint_diagnostics]
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn struct_note(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        self.diagnostic().struct_note(msg)
+        self.dcx().struct_note(msg)
     }
 
     #[inline]
-    pub fn diagnostic(&self) -> &Handler {
-        &self.parse_sess.span_diagnostic
+    pub fn dcx(&self) -> &DiagCtxt {
+        &self.parse_sess.dcx
     }
 
     #[inline]
@@ -881,7 +881,7 @@ impl Session {
                 let mut fuel = self.optimization_fuel.lock();
                 ret = fuel.remaining != 0;
                 if fuel.remaining == 0 && !fuel.out_of_fuel {
-                    if self.diagnostic().can_emit_warnings() {
+                    if self.dcx().can_emit_warnings() {
                         // We only call `msg` in case we can actually emit warnings.
                         // Otherwise, this could cause a `good_path_delayed_bug` to
                         // trigger (issue #79546).
@@ -1221,7 +1221,7 @@ impl Session {
     }
 
     pub fn teach(&self, code: &DiagnosticId) -> bool {
-        self.opts.unstable_opts.teach && self.diagnostic().must_teach(code)
+        self.opts.unstable_opts.teach && self.dcx().must_teach(code)
     }
 
     pub fn edition(&self) -> Edition {
@@ -1357,7 +1357,7 @@ fn default_emitter(
 // JUSTIFICATION: literally session construction
 #[allow(rustc::bad_opt_access)]
 pub fn build_session(
-    early_handler: EarlyErrorHandler,
+    early_dcx: EarlyDiagCtxt,
     sopts: config::Options,
     io: CompilerIO,
     bundle: Option<Lrc<rustc_errors::FluentBundle>>,
@@ -1387,13 +1387,13 @@ pub fn build_session(
         None => filesearch::get_or_default_sysroot().expect("Failed finding sysroot"),
     };
 
-    let target_cfg = config::build_target_config(&early_handler, &sopts, target_override, &sysroot);
+    let target_cfg = config::build_target_config(&early_dcx, &sopts, target_override, &sysroot);
     let host_triple = TargetTriple::from_triple(config::host_triple());
     let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| {
-        early_handler.early_error(format!("Error loading host specification: {e}"))
+        early_dcx.early_error(format!("Error loading host specification: {e}"))
     });
     for warning in target_warnings.warning_messages() {
-        early_handler.early_warn(warning)
+        early_dcx.early_warn(warning)
     }
 
     let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
@@ -1416,15 +1416,15 @@ pub fn build_session(
     );
     let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle);
 
-    let mut span_diagnostic = Handler::with_emitter(emitter)
-        .with_flags(sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings));
+    let mut dcx = DiagCtxt::with_emitter(emitter)
+        .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings));
     if let Some(ice_file) = ice_file {
-        span_diagnostic = span_diagnostic.with_ice_file(ice_file);
+        dcx = dcx.with_ice_file(ice_file);
     }
 
-    // Now that the proper handler has been constructed, drop the early handler
-    // to prevent accidental use.
-    drop(early_handler);
+    // Now that the proper handler has been constructed, drop early_dcx to
+    // prevent accidental use.
+    drop(early_dcx);
 
     let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile
     {
@@ -1440,7 +1440,7 @@ pub fn build_session(
         match profiler {
             Ok(profiler) => Some(Arc::new(profiler)),
             Err(e) => {
-                span_diagnostic.emit_warning(errors::FailedToCreateProfiler { err: e.to_string() });
+                dcx.emit_warning(errors::FailedToCreateProfiler { err: e.to_string() });
                 None
             }
         }
@@ -1448,7 +1448,7 @@ pub fn build_session(
         None
     };
 
-    let mut parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map);
+    let mut parse_sess = ParseSess::with_dcx(dcx, source_map);
     parse_sess.assume_incomplete_release = sopts.unstable_opts.assume_incomplete_release;
 
     let host_triple = config::host_triple();
@@ -1725,54 +1725,54 @@ enum IncrCompSession {
     InvalidBecauseOfErrors { session_directory: PathBuf },
 }
 
-/// A wrapper around an [`Handler`] that is used for early error emissions.
-pub struct EarlyErrorHandler {
-    handler: Handler,
+/// A wrapper around an [`DiagCtxt`] that is used for early error emissions.
+pub struct EarlyDiagCtxt {
+    dcx: DiagCtxt,
 }
 
-impl EarlyErrorHandler {
+impl EarlyDiagCtxt {
     pub fn new(output: ErrorOutputType) -> Self {
         let emitter = mk_emitter(output);
-        Self { handler: Handler::with_emitter(emitter) }
+        Self { dcx: DiagCtxt::with_emitter(emitter) }
     }
 
     pub fn abort_if_errors(&self) {
-        self.handler.abort_if_errors()
+        self.dcx.abort_if_errors()
     }
 
-    /// Swap out the underlying handler once we acquire the user's preference on error emission
+    /// Swap out the underlying dcx once we acquire the user's preference on error emission
     /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the
-    /// previous handler will be emitted.
+    /// previous dcx will be emitted.
     pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) {
-        self.handler.abort_if_errors();
+        self.dcx.abort_if_errors();
 
         let emitter = mk_emitter(output);
-        self.handler = Handler::with_emitter(emitter);
+        self.dcx = DiagCtxt::with_emitter(emitter);
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_note(&self, msg: impl Into<DiagnosticMessage>) {
-        self.handler.struct_note(msg).emit()
+        self.dcx.struct_note(msg).emit()
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_help(&self, msg: impl Into<DiagnosticMessage>) {
-        self.handler.struct_help(msg).emit()
+        self.dcx.struct_help(msg).emit()
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
     pub fn early_error_no_abort(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
-        self.handler.struct_err(msg).emit()
+        self.dcx.struct_err(msg).emit()
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_error(&self, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.handler.struct_fatal(msg).emit()
+        self.dcx.struct_fatal(msg).emit()
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
@@ -1781,13 +1781,13 @@ impl EarlyErrorHandler {
         &self,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, !> {
-        self.handler.struct_fatal(msg)
+        self.dcx.struct_fatal(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_warn(&self, msg: impl Into<DiagnosticMessage>) {
-        self.handler.struct_warn(msg).emit()
+        self.dcx.struct_warn(msg).emit()
     }
 
     pub fn initialize_checked_jobserver(&self) {
@@ -1795,10 +1795,7 @@ impl EarlyErrorHandler {
         jobserver::initialize_checked(|err| {
             #[allow(rustc::untranslatable_diagnostic)]
             #[allow(rustc::diagnostic_outside_of_impl)]
-            self.handler
-                .struct_warn(err)
-                .note("the build environment is likely misconfigured")
-                .emit()
+            self.dcx.struct_warn(err).note("the build environment is likely misconfigured").emit()
         });
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 241a0c22310..2361a04a6d7 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -5,7 +5,9 @@
 
 use rustc_middle::ty;
 use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
-use rustc_middle::ty::{GenericPredicates, Instance, ParamEnv, ScalarInt, ValTree};
+use rustc_middle::ty::{
+    GenericPredicates, Instance, ParamEnv, ScalarInt, TypeVisitableExt, ValTree,
+};
 use rustc_span::def_id::LOCAL_CRATE;
 use stable_mir::compiler_interface::Context;
 use stable_mir::mir::alloc::GlobalAlloc;
@@ -156,7 +158,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
                 let crate_name = tables.tcx.crate_name(*crate_num).to_string();
                 (name == crate_name).then(|| smir_crate(tables.tcx, *crate_num))
             })
-            .into_iter()
             .flatten()
             .collect();
         crates
@@ -218,6 +219,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         def.internal(&mut *tables).repr().simd()
     }
 
+    fn adt_is_cstr(&self, def: AdtDef) -> bool {
+        let mut tables = self.0.borrow_mut();
+        let def_id = def.0.internal(&mut *tables);
+        tables.tcx.lang_items().c_str() == Some(def_id)
+    }
+
     fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig {
         let mut tables = self.0.borrow_mut();
         let def_id = def.0.internal(&mut *tables);
@@ -324,7 +331,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
     fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty {
         let mut tables = self.0.borrow_mut();
         let instance = tables.instances[def];
-        instance.ty(tables.tcx, ParamEnv::empty()).stable(&mut *tables)
+        assert!(!instance.has_non_region_param(), "{instance:?} needs further substitution");
+        instance.ty(tables.tcx, ParamEnv::reveal_all()).stable(&mut *tables)
     }
 
     fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId {
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index 8c1767501d9..41ab4007a67 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -36,6 +36,7 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
                 .collect(),
             self.arg_count,
             self.var_debug_info.iter().map(|info| info.stable(tables)).collect(),
+            self.spread_arg.stable(tables),
         )
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
index 7d8339ab503..7021bdda735 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
@@ -57,7 +57,9 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
                 stable_mir::mir::CoroutineKind::Gen(source.stable(tables))
             }
             CoroutineKind::Coroutine => stable_mir::mir::CoroutineKind::Coroutine,
-            CoroutineKind::AsyncGen(_) => todo!(),
+            CoroutineKind::AsyncGen(source) => {
+                stable_mir::mir::CoroutineKind::AsyncGen(source.stable(tables))
+            }
         }
     }
 }
diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs
index fbfc5c22fcb..4c7029c4e52 100644
--- a/compiler/rustc_span/src/caching_source_map_view.rs
+++ b/compiler/rustc_span/src/caching_source_map_view.rs
@@ -117,7 +117,7 @@ impl<'sm> CachingSourceMapView<'sm> {
         self.time_stamp += 1;
 
         // Check if lo and hi are in the cached lines.
-        let lo_cache_idx = self.cache_entry_index(span_data.lo);
+        let lo_cache_idx: isize = self.cache_entry_index(span_data.lo);
         let hi_cache_idx = self.cache_entry_index(span_data.hi);
 
         if lo_cache_idx != -1 && hi_cache_idx != -1 {
@@ -205,7 +205,9 @@ impl<'sm> CachingSourceMapView<'sm> {
                 (lo_cache_idx as usize, oldest)
             }
             _ => {
-                panic!();
+                panic!(
+                    "the case of neither value being equal to -1 was handled above and the function returns."
+                );
             }
         };
 
diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs
index 2e081e55531..ff253b6f467 100644
--- a/compiler/rustc_symbol_mangling/src/errors.rs
+++ b/compiler/rustc_symbol_mangling/src/errors.rs
@@ -16,12 +16,12 @@ pub struct TestOutput {
 impl IntoDiagnostic<'_> for TestOutput {
     fn into_diagnostic(
         self,
-        handler: &'_ rustc_errors::Handler,
+        dcx: &'_ rustc_errors::DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
         let TestOutput { span, kind, content } = self;
 
         #[allow(rustc::untranslatable_diagnostic)]
-        let mut diag = handler.struct_err(format!("{kind}({content})"));
+        let mut diag = dcx.struct_err(format!("{kind}({content})"));
         diag.set_span(span);
         diag
     }
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index c1fb287d63e..b0ec8b3a4fa 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,6 +1,6 @@
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
+    AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, ErrorGuaranteed, IntoDiagnostic,
     SubdiagnosticMessage,
 };
 use rustc_macros::Diagnostic;
@@ -61,9 +61,9 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> {
     #[track_caller]
     fn into_diagnostic(
         self,
-        handler: &Handler,
+        dcx: &DiagCtxt,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::trait_selection_negative_positive_conflict);
+        let mut diag = dcx.struct_err(fluent::trait_selection_negative_positive_conflict);
         diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
         diag.set_arg(
             "self_desc",
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index 2fe51b400ec..980ef862366 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -203,7 +203,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                     )
                     .into(),
                     ty::AssocKind::Type => Ty::new_error(tcx, guar).into(),
-                    ty::AssocKind::Fn => unreachable!(),
+                    // This makes no sense...
+                    ty::AssocKind::Fn => span_bug!(
+                        tcx.def_span(assoc_def.item.def_id),
+                        "cannot project to an associated function"
+                    ),
                 };
                 ecx.eq(goal.param_env, goal.predicate.term, error_term)
                     .expect("expected goal term to be fully unconstrained");
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 9536fde7c93..deb50e6aefd 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -256,7 +256,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                     Err(NoSolution)
                 }
             }
-            ty::ImplPolarity::Reservation => bug!(),
+            // FIXME: Goal polarity should be split from impl polarity
+            ty::ImplPolarity::Reservation => {
+                bug!("we never expect a `Reservation` polarity in a trait goal")
+            }
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 88ebea70021..533fe32f70d 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -273,7 +273,6 @@ fn overlap<'tcx>(
                                     causing the impls to overlap",
                                 infcx.resolve_vars_if_possible(failing_obligation.predicate)
                             ));
-                            lint
                         },
                     );
                 }
@@ -487,7 +486,7 @@ fn plug_infer_with_placeholders<'tcx>(
                         ),
                     )
                 else {
-                    bug!()
+                    bug!("we always expect to be able to plug an infer var with placeholder")
                 };
                 assert_eq!(obligations, &[]);
                 ControlFlow::Continue(())
@@ -510,7 +509,7 @@ fn plug_infer_with_placeholders<'tcx>(
                         ),
                     )
                 else {
-                    bug!()
+                    bug!("we always expect to be able to plug an infer var with placeholder")
                 };
                 assert_eq!(obligations, &[]);
                 ControlFlow::Continue(())
@@ -544,7 +543,7 @@ fn plug_infer_with_placeholders<'tcx>(
                             ),
                         )
                     else {
-                        bug!()
+                        bug!("we always expect to be able to plug an infer var with placeholder")
                     };
                     assert_eq!(obligations, &[]);
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 9073cd6ac47..a1b896d2251 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -4665,7 +4665,7 @@ pub trait NextTypeParamName {
 impl NextTypeParamName for &[hir::GenericParam<'_>] {
     fn next_type_param_name(&self, name: Option<&str>) -> String {
         // This is the list of possible parameter names that we might suggest.
-        let name = name.and_then(|n| n.chars().next()).map(|c| c.to_string().to_uppercase());
+        let name = name.and_then(|n| n.chars().next()).map(|c| c.to_uppercase().to_string());
         let name = name.as_deref();
         let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"];
         let used_names = self
@@ -4783,8 +4783,14 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
     };
 
     let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
-    let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else {
-        // `async fn` should always lower to a lang item bound... but don't ICE.
+    let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else {
+        // `async fn` should always lower to a single bound... but don't ICE.
+        return None;
+    };
+    let Some(hir::PathSegment { args: Some(generics), .. }) =
+        trait_ref.trait_ref.path.segments.last()
+    else {
+        // desugaring to a single path segment for `Future<...>`.
         return None;
     };
     let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) =
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index b3bb0a9fcb7..9ee091bbd1e 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -250,7 +250,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         err.emit();
 
         self.tcx.sess.abort_if_errors();
-        bug!();
+        // FIXME: this should be something like `build_overflow_error_fatal`, which returns
+        // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`.
+        unreachable!(
+            "did not expect compilation to continue after `abort_if_errors`, \
+            since an error was definitely emitted!"
+        );
     }
 
     fn build_overflow_error<T>(
@@ -3390,7 +3395,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         };
 
         if let Some(diag) =
-            self.tcx.sess.diagnostic().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle)
+            self.tcx.sess.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle)
         {
             diag.cancel();
         }
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 9cbddd2bb2b..045d7e444b6 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -116,12 +116,13 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
     fn register_predicate_obligation(
         &mut self,
         infcx: &InferCtxt<'tcx>,
-        obligation: PredicateObligation<'tcx>,
+        mut obligation: PredicateObligation<'tcx>,
     ) {
         assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
         // this helps to reduce duplicate errors, as well as making
         // debug output much nicer to read and so on.
-        let obligation = infcx.resolve_vars_if_possible(obligation);
+        debug_assert!(!obligation.param_env.has_non_region_infer());
+        obligation.predicate = infcx.resolve_vars_if_possible(obligation.predicate);
 
         debug!(?obligation, "register_predicate_obligation");
 
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index fbde7455145..7ac37315fe0 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -194,7 +194,6 @@ fn lint_object_unsafe_trait(
                 // Only provide the help if its a local trait, otherwise it's not
                 violation.solution().add_to(err);
             }
-            err
         },
     );
 }
@@ -345,7 +344,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     // Search for a predicate like `Self : Sized` amongst the trait bounds.
     let predicates = tcx.predicates_of(def_id);
     let predicates = predicates.instantiate_identity(tcx).predicates;
-    elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() {
+    elaborate(tcx, predicates).any(|pred| match pred.kind().skip_binder() {
         ty::ClauseKind::Trait(ref trait_pred) => {
             trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0)
         }
@@ -595,9 +594,7 @@ fn virtual_call_violations_for_method<'tcx>(
             // would already have reported an error at the definition of the
             // auto trait.
             if pred_trait_ref.args.len() != 1 {
-                tcx.sess
-                    .diagnostic()
-                    .span_delayed_bug(span, "auto traits cannot have generic parameters");
+                tcx.sess.dcx().span_delayed_bug(span, "auto traits cannot have generic parameters");
             }
             return false;
         }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 4c3eb05819f..a1b0ada0e8a 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -191,7 +191,9 @@ impl<'tcx> ProjectionCandidateSet<'tcx> {
                 match (current, candidate) {
                     (ParamEnv(..), ParamEnv(..)) => convert_to_ambiguous = (),
                     (ParamEnv(..), _) => return false,
-                    (_, ParamEnv(..)) => unreachable!(),
+                    (_, ParamEnv(..)) => bug!(
+                        "should never prefer non-param-env candidates over param-env candidates"
+                    ),
                     (_, _) => convert_to_ambiguous = (),
                 }
             }
@@ -2080,10 +2082,11 @@ fn confirm_coroutine_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     nested: Vec<PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
-    let ty::Coroutine(_, args, _) =
-        selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
-    else {
-        unreachable!()
+    let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let ty::Coroutine(_, args, _) = self_ty.kind() else {
+        unreachable!(
+            "expected coroutine self type for built-in coroutine candidate, found {self_ty}"
+        )
     };
     let coroutine_sig = args.as_coroutine().sig();
     let Normalized { value: coroutine_sig, obligations } = normalize_with_depth(
@@ -2113,7 +2116,10 @@ fn confirm_coroutine_candidate<'cx, 'tcx>(
     } else if name == sym::Yield {
         yield_ty
     } else {
-        bug!()
+        span_bug!(
+            tcx.def_span(obligation.predicate.def_id),
+            "unexpected associated type: `Coroutine::{name}`"
+        );
     };
 
     let predicate = ty::ProjectionPredicate {
@@ -2131,10 +2137,11 @@ fn confirm_future_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     nested: Vec<PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
-    let ty::Coroutine(_, args, _) =
-        selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
-    else {
-        unreachable!()
+    let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let ty::Coroutine(_, args, _) = self_ty.kind() else {
+        unreachable!(
+            "expected coroutine self type for built-in async future candidate, found {self_ty}"
+        )
     };
     let coroutine_sig = args.as_coroutine().sig();
     let Normalized { value: coroutine_sig, obligations } = normalize_with_depth(
@@ -2174,10 +2181,9 @@ fn confirm_iterator_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     nested: Vec<PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
-    let ty::Coroutine(_, args, _) =
-        selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
-    else {
-        unreachable!()
+    let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let ty::Coroutine(_, args, _) = self_ty.kind() else {
+        unreachable!("expected coroutine self type for built-in gen candidate, found {self_ty}")
     };
     let gen_sig = args.as_coroutine().sig();
     let Normalized { value: gen_sig, obligations } = normalize_with_depth(
@@ -2321,8 +2327,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     nested: Vec<PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
+    let tcx = selcx.tcx();
     let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
-    let sig = fn_type.fn_sig(selcx.tcx());
+    let sig = fn_type.fn_sig(tcx);
     let Normalized { value: sig, obligations } = normalize_with_depth(
         selcx,
         obligation.param_env,
@@ -2331,9 +2338,24 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
         sig,
     );
 
-    confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes)
-        .with_addl_obligations(nested)
-        .with_addl_obligations(obligations)
+    let host_effect_param = match *fn_type.kind() {
+        ty::FnDef(def_id, args) => tcx
+            .generics_of(def_id)
+            .host_effect_index
+            .map_or(tcx.consts.true_, |idx| args.const_at(idx)),
+        ty::FnPtr(_) => tcx.consts.true_,
+        _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"),
+    };
+
+    confirm_callable_candidate(
+        selcx,
+        obligation,
+        sig,
+        util::TupleArgumentsFlag::Yes,
+        host_effect_param,
+    )
+    .with_addl_obligations(nested)
+    .with_addl_obligations(obligations)
 }
 
 fn confirm_closure_candidate<'cx, 'tcx>(
@@ -2341,9 +2363,9 @@ fn confirm_closure_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     nested: Vec<PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
-    let ty::Closure(_, args) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
-    else {
-        unreachable!()
+    let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let ty::Closure(_, args) = self_ty.kind() else {
+        unreachable!("expected closure self type for closure candidate, found {self_ty}")
     };
     let closure_sig = args.as_closure().sig();
     let Normalized { value: closure_sig, obligations } = normalize_with_depth(
@@ -2356,9 +2378,16 @@ fn confirm_closure_candidate<'cx, 'tcx>(
 
     debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate");
 
-    confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No)
-        .with_addl_obligations(nested)
-        .with_addl_obligations(obligations)
+    confirm_callable_candidate(
+        selcx,
+        obligation,
+        closure_sig,
+        util::TupleArgumentsFlag::No,
+        // FIXME(effects): This doesn't handle const closures correctly!
+        selcx.tcx().consts.true_,
+    )
+    .with_addl_obligations(nested)
+    .with_addl_obligations(obligations)
 }
 
 fn confirm_callable_candidate<'cx, 'tcx>(
@@ -2366,6 +2395,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     fn_sig: ty::PolyFnSig<'tcx>,
     flag: util::TupleArgumentsFlag,
+    fn_host_effect: ty::Const<'tcx>,
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
 
@@ -2380,6 +2410,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
         obligation.predicate.self_ty(),
         fn_sig,
         flag,
+        fn_host_effect,
     )
     .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
         projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args),
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 4728fcf3301..ed55533bc55 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -285,7 +285,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                     ty::Projection => tcx.normalize_projection_ty(c_data),
                     ty::Weak => tcx.normalize_weak_ty(c_data),
                     ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
-                    _ => unreachable!(),
+                    kind => unreachable!("did not expect {kind:?} due to match arm above"),
                 }?;
                 // We don't expect ambiguity.
                 if !result.value.is_proven() {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 10528696b2d..ba6ed298774 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -184,7 +184,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
                 push_outlives_components(tcx, ty_a, &mut components);
                 implied_bounds.extend(implied_bounds_from_components(r_b, components))
             }
-            ty::GenericArgKind::Const(_) => unreachable!(),
+            ty::GenericArgKind::Const(_) => {
+                unreachable!("consts do not participate in outlives bounds")
+            }
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index c7d0ab71644..73e06b84085 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -154,10 +154,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             .infcx
             .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
 
-        // FIXME(effects) proper constness needed?
-        candidates.vec.extend(
-            result.into_iter().map(|idx| ProjectionCandidate(idx, ty::BoundConstness::NotConst)),
-        );
+        candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx)));
     }
 
     /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller
@@ -358,17 +355,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // Provide an impl, but only for suitable `fn` pointers.
             ty::FnPtr(sig) => {
                 if sig.is_fn_trait_compatible() {
-                    candidates.vec.push(FnPointerCandidate { is_const: false });
+                    candidates
+                        .vec
+                        .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ });
                 }
             }
             // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
-            ty::FnDef(def_id, _) => {
-                if self.tcx().fn_sig(def_id).skip_binder().is_fn_trait_compatible()
-                    && self.tcx().codegen_fn_attrs(def_id).target_features.is_empty()
+            ty::FnDef(def_id, args) => {
+                let tcx = self.tcx();
+                if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
+                    && tcx.codegen_fn_attrs(def_id).target_features.is_empty()
                 {
-                    candidates
-                        .vec
-                        .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) });
+                    candidates.vec.push(FnPointerCandidate {
+                        fn_host_effect: tcx
+                            .generics_of(def_id)
+                            .host_effect_index
+                            .map_or(tcx.consts.true_, |idx| args.const_at(idx)),
+                    });
                 }
             }
             _ => {}
@@ -585,7 +588,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
 
                 ty::Alias(ty::Opaque, _) => {
-                    if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(..))) {
+                    if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) {
                         // We do not generate an auto impl candidate for `impl Trait`s which already
                         // reference our auto trait.
                         //
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 4a342a7f6b1..ce3fc2185ba 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -71,7 +71,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
-            ProjectionCandidate(idx, _) => {
+            ProjectionCandidate(idx) => {
                 let obligations = self.confirm_projection_candidate(obligation, idx)?;
                 ImplSource::Param(obligations)
             }
@@ -103,8 +103,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
             }
 
-            FnPointerCandidate { is_const } => {
-                let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
+            FnPointerCandidate { fn_host_effect } => {
+                let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?;
                 ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
@@ -653,8 +653,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_fn_pointer_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-        // FIXME(effects)
-        _is_const: bool,
+        fn_host_effect: ty::Const<'tcx>,
     ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         debug!(?obligation, "confirm_fn_pointer_candidate");
 
@@ -675,6 +674,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             self_ty,
             sig,
             util::TupleArgumentsFlag::Yes,
+            fn_host_effect,
         )
         .map_bound(|(trait_ref, _)| trait_ref);
 
@@ -860,7 +860,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             bug!("closure candidate for non-closure {:?}", obligation);
         };
 
-        let trait_ref = self.closure_trait_ref_unnormalized(obligation, args);
+        let trait_ref =
+            self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_);
         let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
 
         debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
@@ -942,8 +943,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let a_ty = self.infcx.shallow_resolve(predicate.self_ty());
         let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1));
 
-        let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { bug!() };
-        let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { bug!() };
+        let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
+            bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`")
+        };
+        let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
+            bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`")
+        };
 
         let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty);
         let unnormalized_upcast_principal =
@@ -1309,7 +1314,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // If we have a projection type, make sure to normalize it so we replace it
                 // with a fresh infer variable
                 ty::Alias(ty::Projection | ty::Inherent, ..) => {
-                    // FIXME(effects) this needs constness
                     let predicate = normalize_with_depth_to(
                         self,
                         obligation.param_env,
@@ -1340,7 +1344,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // since it's either not `const Drop` (and we raise an error during selection),
                 // or it's an ADT (and we need to check for a custom impl during selection)
                 _ => {
-                    // FIXME(effects) this needs constness
                     let predicate = self_ty.rebind(ty::TraitPredicate {
                         trait_ref: ty::TraitRef::from_lang_item(
                             self.tcx(),
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 7f31a2529f5..c7a30535caa 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1865,7 +1865,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
 
             // Drop otherwise equivalent non-const fn pointer candidates
-            (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes,
+            (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => {
+                DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_)
+            }
 
             (
                 ParamCandidate(ref other_cand),
@@ -1883,7 +1885,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | BuiltinCandidate { .. }
                 | TraitAliasCandidate
                 | ObjectCandidate(_)
-                | ProjectionCandidate(..),
+                | ProjectionCandidate(_),
             ) => {
                 // We have a where clause so don't go around looking
                 // for impls. Arbitrarily give param candidates priority
@@ -1893,7 +1895,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 // here (see issue #50825).
                 DropVictim::drop_if(!is_global(other_cand))
             }
-            (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => {
+            (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => {
                 // Prefer these to a global where-clause bound
                 // (see issue #50825).
                 if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No }
@@ -1921,20 +1923,20 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 )
             }
 
-            (ProjectionCandidate(i, _), ProjectionCandidate(j, _))
+            (ProjectionCandidate(i), ProjectionCandidate(j))
             | (ObjectCandidate(i), ObjectCandidate(j)) => {
                 // Arbitrarily pick the lower numbered candidate for backwards
                 // compatibility reasons. Don't let this affect inference.
                 DropVictim::drop_if(i < j && !has_non_region_infer)
             }
-            (ObjectCandidate(_), ProjectionCandidate(..))
-            | (ProjectionCandidate(..), ObjectCandidate(_)) => {
+            (ObjectCandidate(_), ProjectionCandidate(_))
+            | (ProjectionCandidate(_), ObjectCandidate(_)) => {
                 bug!("Have both object and projection candidate")
             }
 
             // Arbitrarily give projection and object candidates priority.
             (
-                ObjectCandidate(_) | ProjectionCandidate(..),
+                ObjectCandidate(_) | ProjectionCandidate(_),
                 ImplCandidate(..)
                 | AutoImplCandidate
                 | ClosureCandidate { .. }
@@ -1964,7 +1966,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinCandidate { .. }
                 | TraitAliasCandidate,
-                ObjectCandidate(_) | ProjectionCandidate(..),
+                ObjectCandidate(_) | ProjectionCandidate(_),
             ) => DropVictim::No,
 
             (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
@@ -2660,6 +2662,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         args: GenericArgsRef<'tcx>,
+        fn_host_effect: ty::Const<'tcx>,
     ) -> ty::PolyTraitRef<'tcx> {
         let closure_sig = args.as_closure().sig();
 
@@ -2680,6 +2683,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             self_ty,
             closure_sig,
             util::TupleArgumentsFlag::No,
+            fn_host_effect,
         )
         .map_bound(|(trait_ref, _)| trait_ref)
     }
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 39f5ff52eba..71a88f5f07c 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -450,7 +450,6 @@ fn report_conflicting_impls<'tcx>(
                 msg,
                 |err| {
                     decorate(tcx, &overlap, impl_span, err);
-                    err
                 },
             );
         }
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 98da3bc2fe9..19eae93df9c 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -220,9 +220,8 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
         selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates);
     let impl_obligations = super::predicates_for_generics(cause, param_env, predicates);
 
-    let impl_obligations = impl_obligations
-        .chain(normalization_obligations1.into_iter())
-        .chain(normalization_obligations2.into_iter());
+    let impl_obligations =
+        impl_obligations.chain(normalization_obligations1).chain(normalization_obligations2);
 
     (subject, impl_obligations)
 }
@@ -265,13 +264,26 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
     self_ty: Ty<'tcx>,
     sig: ty::PolyFnSig<'tcx>,
     tuple_arguments: TupleArgumentsFlag,
+    fn_host_effect: ty::Const<'tcx>,
 ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> {
     assert!(!self_ty.has_escaping_bound_vars());
     let arguments_tuple = match tuple_arguments {
         TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
         TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
     };
-    let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]);
+    let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() {
+        ty::TraitRef::new(
+            tcx,
+            fn_trait_def_id,
+            [
+                ty::GenericArg::from(self_ty),
+                ty::GenericArg::from(arguments_tuple),
+                ty::GenericArg::from(fn_host_effect),
+            ],
+        )
+    } else {
+        ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple])
+    };
     sig.map_bound(|sig| (trait_ref, sig.output()))
 }
 
diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs
index 8998001ec20..9c8e45b4338 100644
--- a/compiler/rustc_type_ir/src/debug.rs
+++ b/compiler/rustc_type_ir/src/debug.rs
@@ -32,11 +32,7 @@ impl<I: Interner> InferCtxtLike for NoInfcx<I> {
         None
     }
 
-    fn root_lt_var(&self, vid: I::InferRegion) -> I::InferRegion {
-        vid
-    }
-
-    fn probe_lt_var(&self, _vid: I::InferRegion) -> Option<I::Region> {
+    fn opportunistic_resolve_lt_var(&self, _vid: I::InferRegion) -> Option<I::Region> {
         None
     }
 
diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs
index 681f129a50b..28b71f0ea13 100644
--- a/compiler/rustc_type_ir/src/infcx.rs
+++ b/compiler/rustc_type_ir/src/infcx.rs
@@ -18,14 +18,14 @@ pub trait InferCtxtLike {
         lt: <Self::Interner as Interner>::InferRegion,
     ) -> Option<UniverseIndex>;
 
-    /// Resolve `InferRegion` to its root `InferRegion`.
-    fn root_lt_var(
-        &self,
-        vid: <Self::Interner as Interner>::InferRegion,
-    ) -> <Self::Interner as Interner>::InferRegion;
-
-    /// Resolve `InferRegion` to its inferred region, if it has been equated with a non-infer region.
-    fn probe_lt_var(
+    /// Resolve `InferRegion` to its inferred region, if it has been equated with
+    /// a non-infer region.
+    ///
+    /// FIXME: This has slightly different semantics than `{probe,resolve}_{ty,ct}_var`,
+    /// that has to do with the fact unlike `Ty` or `Const` vars, in rustc, we may
+    /// not always be able to *name* the root region var from the universe of the
+    /// var we're trying to resolve. That's why it's called *opportunistic*.
+    fn opportunistic_resolve_lt_var(
         &self,
         vid: <Self::Interner as Interner>::InferRegion,
     ) -> Option<<Self::Interner as Interner>::Region>;
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index c262302133b..188910ecc52 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -3,8 +3,8 @@ use std::fmt::Debug;
 use std::hash::Hash;
 
 use crate::{
-    BoundVar, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, Mutability, RegionKind,
-    TyKind, UniverseIndex,
+    BoundVar, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind, TyKind,
+    UniverseIndex,
 };
 
 pub trait Interner: Sized {
@@ -20,7 +20,6 @@ pub trait Interner: Sized {
     type Term: Copy + Debug + Hash + Ord;
 
     type Binder<T>;
-    type TypeAndMut: Copy + Debug + Hash + Ord;
     type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarInfo<Self>>;
 
     // Kinds of tys
@@ -81,8 +80,6 @@ pub trait Interner: Sized {
     type CoercePredicate: Copy + Debug + Hash + Eq;
     type ClosureKind: Copy + Debug + Hash + Eq;
 
-    fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability);
-
     fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
 
     // FIXME: We should not have all these constructors on `Interner`, but as functions on some trait.
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 200963ff7c5..bff93859645 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -359,3 +359,45 @@ rustc_index::newtype_index! {
     #[gate_rustc_only]
     pub struct BoundVar {}
 }
+
+/// Represents the various closure traits in the language. This
+/// will determine the type of the environment (`self`, in the
+/// desugaring) argument that the closure expects.
+///
+/// You can get the environment type of a closure using
+/// `tcx.closure_env_ty()`.
+#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
+#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
+pub enum ClosureKind {
+    // Warning: Ordering is significant here! The ordering is chosen
+    // because the trait Fn is a subtrait of FnMut and so in turn, and
+    // hence we order it so that Fn < FnMut < FnOnce.
+    Fn,
+    FnMut,
+    FnOnce,
+}
+
+impl ClosureKind {
+    /// This is the initial value used when doing upvar inference.
+    pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
+
+    pub const fn as_str(self) -> &'static str {
+        match self {
+            ClosureKind::Fn => "Fn",
+            ClosureKind::FnMut => "FnMut",
+            ClosureKind::FnOnce => "FnOnce",
+        }
+    }
+
+    /// Returns `true` if a type that impls this closure kind
+    /// must also implement `other`.
+    pub fn extends(self, other: ClosureKind) -> bool {
+        self <= other
+    }
+}
+
+impl fmt::Display for ClosureKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.as_str().fmt(f)
+    }
+}
diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs
index cfed84a35c6..82bb1bf2916 100644
--- a/compiler/rustc_type_ir/src/macros.rs
+++ b/compiler/rustc_type_ir/src/macros.rs
@@ -51,4 +51,6 @@ TrivialTypeTraversalImpls! {
     crate::DebruijnIndex,
     crate::AliasRelationDirection,
     crate::UniverseIndex,
+    crate::Mutability,
+    crate::Movability,
 }
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 72ca9199a53..70adfbee2ed 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -4,6 +4,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::unify::{EqUnifyValue, UnifyKey};
 use std::fmt;
 
+use crate::fold::{FallibleTypeFolder, TypeFoldable};
+use crate::visit::{TypeVisitable, TypeVisitor};
 use crate::Interner;
 use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, WithInfcx};
 
@@ -158,7 +160,7 @@ pub enum TyKind<I: Interner> {
     Slice(I::Ty),
 
     /// A raw pointer. Written as `*mut T` or `*const T`
-    RawPtr(I::TypeAndMut),
+    RawPtr(TypeAndMut<I>),
 
     /// A reference; a pointer with an associated lifetime. Written as
     /// `&'a mut T` or `&'a T`.
@@ -410,8 +412,7 @@ impl<I: Interner> DebugWithInfcx<I> for TyKind<I> {
             Str => write!(f, "str"),
             Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)),
             Slice(t) => write!(f, "[{:?}]", &this.wrap(t)),
-            RawPtr(p) => {
-                let (ty, mutbl) = I::ty_and_mut_to_parts(*p);
+            RawPtr(TypeAndMut { ty, mutbl }) => {
                 match mutbl {
                     Mutability::Mut => write!(f, "*mut "),
                     Mutability::Not => write!(f, "*const "),
@@ -831,3 +832,42 @@ impl<I: Interner> DebugWithInfcx<I> for InferTy {
         }
     }
 }
+
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    PartialOrd(bound = ""),
+    Ord(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Hash(bound = ""),
+    Debug(bound = "")
+)]
+#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
+pub struct TypeAndMut<I: Interner> {
+    pub ty: I::Ty,
+    pub mutbl: Mutability,
+}
+
+impl<I: Interner> TypeFoldable<I> for TypeAndMut<I>
+where
+    I::Ty: TypeFoldable<I>,
+{
+    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
+        Ok(TypeAndMut {
+            ty: self.ty.try_fold_with(folder)?,
+            mutbl: self.mutbl.try_fold_with(folder)?,
+        })
+    }
+}
+
+impl<I: Interner> TypeVisitable<I> for TypeAndMut<I>
+where
+    I::Ty: TypeVisitable<I>,
+{
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> std::ops::ControlFlow<V::BreakTy> {
+        self.ty.visit_with(visitor)?;
+        self.mutbl.visit_with(visitor)
+    }
+}
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
index 2fac59e71fd..57c60b70d8a 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -72,6 +72,9 @@ pub trait Context {
     /// Returns whether this ADT is simd.
     fn adt_is_simd(&self, def: AdtDef) -> bool;
 
+    /// Returns whether this definition is a C string.
+    fn adt_is_cstr(&self, def: AdtDef) -> bool;
+
     /// Retrieve the function signature for the given generic arguments.
     fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig;
 
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 3dfe7096399..5023af9ab79 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -22,6 +22,11 @@ pub struct Body {
 
     /// Debug information pertaining to user variables, including captures.
     pub var_debug_info: Vec<VarDebugInfo>,
+
+    /// Mark an argument (which must be a tuple) as getting passed as its individual components.
+    ///
+    /// This is used for the "rust-call" ABI such as closures.
+    pub(super) spread_arg: Option<Local>,
 }
 
 pub type BasicBlockIdx = usize;
@@ -36,6 +41,7 @@ impl Body {
         locals: LocalDecls,
         arg_count: usize,
         var_debug_info: Vec<VarDebugInfo>,
+        spread_arg: Option<Local>,
     ) -> Self {
         // If locals doesn't contain enough entries, it can lead to panics in
         // `ret_local`, `arg_locals`, and `inner_locals`.
@@ -43,7 +49,7 @@ impl Body {
             locals.len() > arg_count,
             "A Body must contain at least a local for the return value and each of the function's arguments"
         );
-        Self { blocks, locals, arg_count, var_debug_info }
+        Self { blocks, locals, arg_count, var_debug_info, spread_arg }
     }
 
     /// Return local that holds this function's return value.
@@ -75,6 +81,11 @@ impl Body {
         self.locals.get(local)
     }
 
+    /// Get an iterator for all local declarations.
+    pub fn local_decls(&self) -> impl Iterator<Item = (Local, &LocalDecl)> {
+        self.locals.iter().enumerate()
+    }
+
     pub fn dump<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
         writeln!(w, "{}", function_body(self))?;
         self.blocks
@@ -98,6 +109,10 @@ impl Body {
             .collect::<Result<Vec<_>, _>>()?;
         Ok(())
     }
+
+    pub fn spread_arg(&self) -> Option<Local> {
+        self.spread_arg
+    }
 }
 
 type LocalDecls = Vec<LocalDecl>;
@@ -248,6 +263,57 @@ pub enum AssertMessage {
     MisalignedPointerDereference { required: Operand, found: Operand },
 }
 
+impl AssertMessage {
+    pub fn description(&self) -> Result<&'static str, Error> {
+        match self {
+            AssertMessage::Overflow(BinOp::Add, _, _) => Ok("attempt to add with overflow"),
+            AssertMessage::Overflow(BinOp::Sub, _, _) => Ok("attempt to subtract with overflow"),
+            AssertMessage::Overflow(BinOp::Mul, _, _) => Ok("attempt to multiply with overflow"),
+            AssertMessage::Overflow(BinOp::Div, _, _) => Ok("attempt to divide with overflow"),
+            AssertMessage::Overflow(BinOp::Rem, _, _) => {
+                Ok("attempt to calculate the remainder with overflow")
+            }
+            AssertMessage::OverflowNeg(_) => Ok("attempt to negate with overflow"),
+            AssertMessage::Overflow(BinOp::Shr, _, _) => Ok("attempt to shift right with overflow"),
+            AssertMessage::Overflow(BinOp::Shl, _, _) => Ok("attempt to shift left with overflow"),
+            AssertMessage::Overflow(op, _, _) => Err(error!("`{:?}` cannot overflow", op)),
+            AssertMessage::DivisionByZero(_) => Ok("attempt to divide by zero"),
+            AssertMessage::RemainderByZero(_) => {
+                Ok("attempt to calculate the remainder with a divisor of zero")
+            }
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine) => {
+                Ok("coroutine resumed after completion")
+            }
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Async(_)) => {
+                Ok("`async fn` resumed after completion")
+            }
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Gen(_)) => {
+                Ok("`async gen fn` resumed after completion")
+            }
+            AssertMessage::ResumedAfterReturn(CoroutineKind::AsyncGen(_)) => {
+                Ok("`gen fn` should just keep returning `AssertMessage::None` after completion")
+            }
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine) => {
+                Ok("coroutine resumed after panicking")
+            }
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Async(_)) => {
+                Ok("`async fn` resumed after panicking")
+            }
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Gen(_)) => {
+                Ok("`async gen fn` resumed after panicking")
+            }
+            AssertMessage::ResumedAfterPanic(CoroutineKind::AsyncGen(_)) => {
+                Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking")
+            }
+
+            AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"),
+            AssertMessage::MisalignedPointerDereference { .. } => {
+                Ok("misaligned pointer dereference")
+            }
+        }
+    }
+}
+
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum BinOp {
     Add,
@@ -325,6 +391,7 @@ pub enum CoroutineKind {
     Async(CoroutineSource),
     Coroutine,
     Gen(CoroutineSource),
+    AsyncGen(CoroutineSource),
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs
index bc5d4a3b8f4..c126de23c4b 100644
--- a/compiler/stable_mir/src/mir/mono.rs
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -1,6 +1,6 @@
 use crate::crate_def::CrateDef;
 use crate::mir::Body;
-use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, FnSig, GenericArgs, IndexedVal, Ty};
+use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
 use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque, Symbol};
 use std::fmt::{Debug, Formatter};
 
@@ -115,11 +115,6 @@ impl Instance {
         })
     }
 
-    /// Get this function signature with all types already instantiated.
-    pub fn fn_sig(&self) -> FnSig {
-        self.ty().kind().fn_sig().unwrap().skip_binder()
-    }
-
     /// Check whether this instance is an empty shim.
     ///
     /// Allow users to check if this shim can be ignored when called directly.
diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs
index 576087498ab..8b7b488d312 100644
--- a/compiler/stable_mir/src/mir/pretty.rs
+++ b/compiler/stable_mir/src/mir/pretty.rs
@@ -260,6 +260,7 @@ pub fn pretty_assert_message(msg: &AssertMessage) -> String {
             );
             pretty
         }
+        AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op),
         AssertMessage::OverflowNeg(op) => {
             let pretty_op = pretty_operand(op);
             pretty.push_str(
@@ -279,17 +280,15 @@ pub fn pretty_assert_message(msg: &AssertMessage) -> String {
             );
             pretty
         }
-        AssertMessage::ResumedAfterReturn(_) => {
-            format!("attempt to resume a generator after completion")
-        }
-        AssertMessage::ResumedAfterPanic(_) => format!("attempt to resume a panicked generator"),
         AssertMessage::MisalignedPointerDereference { required, found } => {
             let pretty_required = pretty_operand(required);
             let pretty_found = pretty_operand(found);
             pretty.push_str(format!("\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}").as_str());
             pretty
         }
-        _ => todo!(),
+        AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => {
+            msg.description().unwrap().to_string()
+        }
     }
 }
 
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
index d46caad9a01..98336a72900 100644
--- a/compiler/stable_mir/src/mir/visit.rs
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -133,7 +133,7 @@ pub trait MirVisitor {
     }
 
     fn super_body(&mut self, body: &Body) {
-        let Body { blocks, locals: _, arg_count, var_debug_info } = body;
+        let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _ } = body;
 
         for bb in blocks {
             self.visit_basic_block(bb);
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index f473fd8dbb7..4807a9028eb 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -22,9 +22,7 @@ impl Debug for Ty {
 /// Constructors for `Ty`.
 impl Ty {
     /// Create a new type from a given kind.
-    ///
-    /// Note that not all types may be supported at this point.
-    fn from_rigid_kind(kind: RigidTy) -> Ty {
+    pub fn from_rigid_kind(kind: RigidTy) -> Ty {
         with(|cx| cx.new_rigid_ty(kind))
     }
 
@@ -77,6 +75,16 @@ impl Ty {
     pub fn bool_ty() -> Ty {
         Ty::from_rigid_kind(RigidTy::Bool)
     }
+
+    /// Create a type representing a signed integer.
+    pub fn signed_ty(inner: IntTy) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Int(inner))
+    }
+
+    /// Create a type representing an unsigned integer.
+    pub fn unsigned_ty(inner: UintTy) -> Ty {
+        Ty::from_rigid_kind(RigidTy::Uint(inner))
+    }
 }
 
 impl Ty {
@@ -309,6 +317,12 @@ impl TyKind {
     }
 
     #[inline]
+    pub fn is_cstr(&self) -> bool {
+        let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else { return false };
+        with(|cx| cx.adt_is_cstr(*def))
+    }
+
+    #[inline]
     pub fn is_slice(&self) -> bool {
         matches!(self, TyKind::RigidTy(RigidTy::Slice(_)))
     }
diff --git a/config.example.toml b/config.example.toml
index 14e0b9d521f..a7d4df545a6 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -29,8 +29,8 @@
 #  - A change in the default values
 #
 # If `change-id` does not match the version that is currently running,
-# `x.py` will prompt you to update it and check the related PR for more details.
-change-id = 118703
+# `x.py` will inform you about the changes made on bootstrap.
+# change-id = <latest change id in src/bootstrap/src/utils/change_tracker.rs>
 
 # =============================================================================
 # Tweaking how LLVM is compiled
diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs
index 62856fc9a49..4a4a3abd47f 100644
--- a/library/alloc/src/ffi/c_str.rs
+++ b/library/alloc/src/ffi/c_str.rs
@@ -421,7 +421,7 @@ impl CString {
     /// Failure to call [`CString::from_raw`] will lead to a memory leak.
     ///
     /// The C side must **not** modify the length of the string (by writing a
-    /// `null` somewhere inside the string or removing the final one) before
+    /// nul byte somewhere inside the string or removing the final one) before
     /// it makes it back into Rust using [`CString::from_raw`]. See the safety section
     /// in [`CString::from_raw`].
     ///
@@ -797,7 +797,7 @@ impl From<Box<CStr>> for CString {
 #[stable(feature = "cstring_from_vec_of_nonzerou8", since = "1.43.0")]
 impl From<Vec<NonZeroU8>> for CString {
     /// Converts a <code>[Vec]<[NonZeroU8]></code> into a [`CString`] without
-    /// copying nor checking for inner null bytes.
+    /// copying nor checking for inner nul bytes.
     #[inline]
     fn from(v: Vec<NonZeroU8>) -> CString {
         unsafe {
@@ -809,7 +809,7 @@ impl From<Vec<NonZeroU8>> for CString {
                 let (ptr, len, cap): (*mut NonZeroU8, _, _) = Vec::into_raw_parts(v);
                 Vec::from_raw_parts(ptr.cast::<u8>(), len, cap)
             };
-            // SAFETY: `v` cannot contain null bytes, given the type-level
+            // SAFETY: `v` cannot contain nul bytes, given the type-level
             // invariant of `NonZeroU8`.
             Self::_from_vec_unchecked(v)
         }
diff --git a/library/core/src/any.rs b/library/core/src/any.rs
index 22777fb078a..c82984d5c6b 100644
--- a/library/core/src/any.rs
+++ b/library/core/src/any.rs
@@ -695,44 +695,41 @@ pub const fn type_name<T: ?Sized>() -> &'static str {
     intrinsics::type_name::<T>()
 }
 
-/// Returns the name of the type of the pointed-to value as a string slice.
+/// Returns the type name of the pointed-to value as a string slice.
+///
 /// This is the same as `type_name::<T>()`, but can be used where the type of a
 /// variable is not easily available.
 ///
 /// # Note
 ///
-/// This is intended for diagnostic use. The exact contents and format of the
-/// string are not specified, other than being a best-effort description of the
-/// type. For example, `type_name_of_val::<Option<String>>(None)` could return
-/// `"Option<String>"` or `"std::option::Option<std::string::String>"`, but not
-/// `"foobar"`. In addition, the output may change between versions of the
-/// compiler.
-///
-/// This function does not resolve trait objects,
-/// meaning that `type_name_of_val(&7u32 as &dyn Debug)`
-/// may return `"dyn Debug"`, but not `"u32"`.
+/// Like [`type_name`], this is intended for diagnostic use and the exact output is not
+/// guaranteed. It provides a best-effort description, but the output may change between
+/// versions of the compiler.
 ///
-/// The type name should not be considered a unique identifier of a type;
-/// multiple types may share the same type name.
+/// In short: use this for debugging, avoid using the output to affect program behavior. More
+/// information is available at [`type_name`].
 ///
-/// The current implementation uses the same infrastructure as compiler
-/// diagnostics and debuginfo, but this is not guaranteed.
+/// Additionally, this function does not resolve trait objects. This means that
+/// `type_name_of_val(&7u32 as &dyn Debug)` may return `"dyn Debug"`, but will not return `"u32"`
+/// at this time.
 ///
 /// # Examples
 ///
 /// Prints the default integer and float types.
 ///
 /// ```rust
-/// #![feature(type_name_of_val)]
 /// use std::any::type_name_of_val;
 ///
-/// let x = 1;
-/// println!("{}", type_name_of_val(&x));
-/// let y = 1.0;
-/// println!("{}", type_name_of_val(&y));
+/// let s = "foo";
+/// let x: i32 = 1;
+/// let y: f32 = 1.0;
+///
+/// assert!(type_name_of_val(&s).contains("str"));
+/// assert!(type_name_of_val(&x).contains("i32"));
+/// assert!(type_name_of_val(&y).contains("f32"));
 /// ```
 #[must_use]
-#[unstable(feature = "type_name_of_val", issue = "66359")]
+#[stable(feature = "type_name_of_val", since = "CURRENT_RUSTC_VERSION")]
 #[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
 pub const fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
     type_name::<T>()
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index e7ec1fb73cd..bb839a71e90 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -205,7 +205,7 @@ impl CStr {
     /// * The memory pointed to by `ptr` must contain a valid nul terminator at the
     ///   end of the string.
     ///
-    /// * `ptr` must be [valid] for reads of bytes up to and including the null terminator.
+    /// * `ptr` must be [valid] for reads of bytes up to and including the nul terminator.
     ///   This means in particular:
     ///
     ///     * The entire memory range of this `CStr` must be contained within a single allocated object!
@@ -415,7 +415,7 @@ impl CStr {
             let mut i = bytes.len().saturating_sub(1);
             assert!(!bytes.is_empty() && bytes[i] == 0, "input was not nul-terminated");
 
-            // Ending null byte exists, skip to the rest.
+            // Ending nul byte exists, skip to the rest.
             while i != 0 {
                 i -= 1;
                 let byte = bytes[i];
diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs
index dafc58d3a7d..8bf15c736c3 100644
--- a/library/core/src/net/ip_addr.rs
+++ b/library/core/src/net/ip_addr.rs
@@ -1437,7 +1437,7 @@ impl Ipv6Addr {
     /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified))
     /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback))
     /// - IPv4-mapped addresses
-    /// - Addresses reserved for benchmarking
+    /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv6Addr::is_benchmarking))
     /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation))
     /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local))
     /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local))
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index d6603681a0b..eed793186b9 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -720,7 +720,8 @@ where
 /// type or mutability, in particular if the code is refactored.
 #[inline(always)]
 #[must_use]
-#[unstable(feature = "ptr_from_ref", issue = "106116")]
+#[stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")]
+#[rustc_const_stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")]
 #[rustc_never_returns_null_ptr]
 #[rustc_diagnostic_item = "ptr_from_ref"]
 pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
@@ -733,7 +734,9 @@ pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
 /// type or mutability, in particular if the code is refactored.
 #[inline(always)]
 #[must_use]
-#[unstable(feature = "ptr_from_ref", issue = "106116")]
+#[stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")]
+#[rustc_const_stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")]
+#[rustc_allow_const_fn_unstable(const_mut_refs)]
 #[rustc_never_returns_null_ptr]
 pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T {
     r
diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs
index 4cfccd2e3ce..ce04a9f4089 100644
--- a/library/core/src/slice/ascii.rs
+++ b/library/core/src/slice/ascii.rs
@@ -125,6 +125,7 @@ impl [u8] {
     /// assert_eq!(b"".trim_ascii_start(), b"");
     /// ```
     #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[inline]
     pub const fn trim_ascii_start(&self) -> &[u8] {
         let mut bytes = self;
         // Note: A pattern matching based approach (instead of indexing) allows
@@ -154,6 +155,7 @@ impl [u8] {
     /// assert_eq!(b"".trim_ascii_end(), b"");
     /// ```
     #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[inline]
     pub const fn trim_ascii_end(&self) -> &[u8] {
         let mut bytes = self;
         // Note: A pattern matching based approach (instead of indexing) allows
@@ -184,6 +186,7 @@ impl [u8] {
     /// assert_eq!(b"".trim_ascii(), b"");
     /// ```
     #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[inline]
     pub const fn trim_ascii(&self) -> &[u8] {
         self.trim_ascii_start().trim_ascii_end()
     }
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index 27178328be5..a22c46edce2 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -2423,6 +2423,85 @@ impl str {
         me.make_ascii_lowercase()
     }
 
+    /// Returns a string slice with leading ASCII whitespace removed.
+    ///
+    /// 'Whitespace' refers to the definition used by
+    /// [`u8::is_ascii_whitespace`].
+    ///
+    /// [`u8::is_ascii_whitespace`]: u8::is_ascii_whitespace
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(byte_slice_trim_ascii)]
+    ///
+    /// assert_eq!(" \t \u{3000}hello world\n".trim_ascii_start(), "\u{3000}hello world\n");
+    /// assert_eq!("  ".trim_ascii_start(), "");
+    /// assert_eq!("".trim_ascii_start(), "");
+    /// ```
+    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[must_use = "this returns the trimmed string as a new slice, \
+                  without modifying the original"]
+    #[inline]
+    pub const fn trim_ascii_start(&self) -> &str {
+        // SAFETY: Removing ASCII characters from a `&str` does not invalidate
+        // UTF-8.
+        unsafe { core::str::from_utf8_unchecked(self.as_bytes().trim_ascii_start()) }
+    }
+
+    /// Returns a string slice with trailing ASCII whitespace removed.
+    ///
+    /// 'Whitespace' refers to the definition used by
+    /// [`u8::is_ascii_whitespace`].
+    ///
+    /// [`u8::is_ascii_whitespace`]: u8::is_ascii_whitespace
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(byte_slice_trim_ascii)]
+    ///
+    /// assert_eq!("\r hello world\u{3000}\n ".trim_ascii_end(), "\r hello world\u{3000}");
+    /// assert_eq!("  ".trim_ascii_end(), "");
+    /// assert_eq!("".trim_ascii_end(), "");
+    /// ```
+    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[must_use = "this returns the trimmed string as a new slice, \
+                  without modifying the original"]
+    #[inline]
+    pub const fn trim_ascii_end(&self) -> &str {
+        // SAFETY: Removing ASCII characters from a `&str` does not invalidate
+        // UTF-8.
+        unsafe { core::str::from_utf8_unchecked(self.as_bytes().trim_ascii_end()) }
+    }
+
+    /// Returns a string slice with leading and trailing ASCII whitespace
+    /// removed.
+    ///
+    /// 'Whitespace' refers to the definition used by
+    /// [`u8::is_ascii_whitespace`].
+    ///
+    /// [`u8::is_ascii_whitespace`]: u8::is_ascii_whitespace
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(byte_slice_trim_ascii)]
+    ///
+    /// assert_eq!("\r hello world\n ".trim_ascii(), "hello world");
+    /// assert_eq!("  ".trim_ascii(), "");
+    /// assert_eq!("".trim_ascii(), "");
+    /// ```
+    #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")]
+    #[must_use = "this returns the trimmed string as a new slice, \
+                  without modifying the original"]
+    #[inline]
+    pub const fn trim_ascii(&self) -> &str {
+        // SAFETY: Removing ASCII characters from a `&str` does not invalidate
+        // UTF-8.
+        unsafe { core::str::from_utf8_unchecked(self.as_bytes().trim_ascii()) }
+    }
+
     /// Return an iterator that escapes each char in `self` with [`char::escape_debug`].
     ///
     /// Note: only extended grapheme codepoints that begin the string will be
diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index 86ce5d9c6d5..75bf3329786 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -55,6 +55,7 @@ macro_rules! with_api {
         $m! {
             FreeFunctions {
                 fn drop($self: $S::FreeFunctions);
+                fn injected_env_var(var: &str) -> Option<String>;
                 fn track_env_var(var: &str, value: Option<&str>);
                 fn track_path(path: &str);
                 fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 7ebb6810b3e..6e664a162df 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -1503,7 +1503,8 @@ pub mod tracked_env {
     #[unstable(feature = "proc_macro_tracked_env", issue = "99515")]
     pub fn var<K: AsRef<OsStr> + AsRef<str>>(key: K) -> Result<String, VarError> {
         let key: &str = key.as_ref();
-        let value = env::var(key);
+        let value = crate::bridge::client::FreeFunctions::injected_env_var(key)
+            .map_or_else(|| env::var(key), Ok);
         crate::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok());
         value
     }
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index f666b18887c..fe66788b564 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -30,8 +30,13 @@ rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] }
 [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
 miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
 addr2line = { version = "0.21.0", optional = true, default-features = false }
+
+[target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies]
 object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] }
 
+[target.'cfg(target_os = "aix")'.dependencies]
+object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] }
+
 [dev-dependencies]
 rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
 rand_xorshift = "0.3.0"
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index f67f6034d34..30ac0512348 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -313,17 +313,32 @@ impl Error for VarError {
 /// Sets the environment variable `key` to the value `value` for the currently running
 /// process.
 ///
-/// Note that while concurrent access to environment variables is safe in Rust,
-/// some platforms only expose inherently unsafe non-threadsafe APIs for
-/// inspecting the environment. As a result, extra care needs to be taken when
-/// auditing calls to unsafe external FFI functions to ensure that any external
-/// environment accesses are properly synchronized with accesses in Rust.
+/// # Safety
+///
+/// Even though this function is currently not marked as `unsafe`, it needs to
+/// be because invoking it can cause undefined behaviour. The function will be
+/// marked `unsafe` in a future version of Rust. This is tracked in
+/// [rust#27970](https://github.com/rust-lang/rust/issues/27970).
+///
+/// This function is safe to call in a single-threaded program.
+///
+/// In multi-threaded programs, you must ensure that are no other threads
+/// concurrently writing or *reading*(!) from the environment through functions
+/// other than the ones in this module. You are responsible for figuring out
+/// how to achieve this, but we strongly suggest not using `set_var` or
+/// `remove_var` in multi-threaded programs at all.
+///
+/// Most C libraries, including libc itself do not advertise which functions
+/// read from the environment. Even functions from the Rust standard library do
+/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
 ///
 /// Discussion of this unsafety on Unix may be found in:
 ///
 ///  - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
 ///  - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
 ///
+/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs
+///
 /// # Panics
 ///
 /// This function may panic if `key` is empty, contains an ASCII equals sign `'='`
@@ -351,17 +366,32 @@ fn _set_var(key: &OsStr, value: &OsStr) {
 
 /// Removes an environment variable from the environment of the currently running process.
 ///
-/// Note that while concurrent access to environment variables is safe in Rust,
-/// some platforms only expose inherently unsafe non-threadsafe APIs for
-/// inspecting the environment. As a result extra care needs to be taken when
-/// auditing calls to unsafe external FFI functions to ensure that any external
-/// environment accesses are properly synchronized with accesses in Rust.
+/// # Safety
+///
+/// Even though this function is currently not marked as `unsafe`, it needs to
+/// be because invoking it can cause undefined behaviour. The function will be
+/// marked `unsafe` in a future version of Rust. This is tracked in
+/// [rust#27970](https://github.com/rust-lang/rust/issues/27970).
+///
+/// This function is safe to call in a single-threaded program.
+///
+/// In multi-threaded programs, you must ensure that are no other threads
+/// concurrently writing or *reading*(!) from the environment through functions
+/// other than the ones in this module. You are responsible for figuring out
+/// how to achieve this, but we strongly suggest not using `set_var` or
+/// `remove_var` in multi-threaded programs at all.
+///
+/// Most C libraries, including libc itself do not advertise which functions
+/// read from the environment. Even functions from the Rust standard library do
+/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
 ///
 /// Discussion of this unsafety on Unix may be found in:
 ///
 ///  - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
 ///  - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
 ///
+/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs
+///
 /// # Panics
 ///
 /// This function may panic if `key` is empty, contains an ASCII equals sign
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index d08f0b1c9d0..76081833e05 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -338,7 +338,6 @@
 #![feature(portable_simd)]
 #![feature(prelude_2024)]
 #![feature(ptr_as_uninit)]
-#![feature(ptr_from_ref)]
 #![feature(raw_os_nonzero)]
 #![feature(round_ties_even)]
 #![feature(slice_internals)]
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index 34b8b6b97b5..58df83bd79d 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -355,15 +355,15 @@ macro_rules! dbg {
     // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
     // will be malformed.
     () => {
-        $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!())
+        $crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!())
     };
     ($val:expr $(,)?) => {
         // Use of `match` here is intentional because it affects the lifetimes
         // of temporaries - https://stackoverflow.com/a/48732525/1063961
         match $val {
             tmp => {
-                $crate::eprintln!("[{}:{}] {} = {:#?}",
-                    $crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp);
+                $crate::eprintln!("[{}:{}:{}] {} = {:#?}",
+                    $crate::file!(), $crate::line!(), $crate::column!(), $crate::stringify!($val), &tmp);
                 tmp
             }
         }
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 55f4917a937..66b4ec37c8e 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -588,7 +588,7 @@ pub fn panicking() -> bool {
 }
 
 /// Entry point of panics from the core crate (`panic_impl` lang item).
-#[cfg(not(test))]
+#[cfg(not(any(test, doctest)))]
 #[panic_handler]
 pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
     struct FormatStringPayload<'a> {
@@ -669,7 +669,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
 /// panic!() and assert!(). In particular, this is the only entry point that supports
 /// arbitrary payloads, not just format strings.
 #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
-#[cfg_attr(not(test), lang = "begin_panic")]
+#[cfg_attr(not(any(test, doctest)), lang = "begin_panic")]
 // lang item for CTFE panic support
 // never inline unless panic_immediate_abort to avoid code
 // bloat at the call sites as much as possible
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 6004ed51bd1..4a7f5d8e0be 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -2311,7 +2311,7 @@ pub fn id() -> u32 {
 /// of the `main` function, this trait is likely to be available only on
 /// standard library's runtime for convenience. Other runtimes are not required
 /// to provide similar functionality.
-#[cfg_attr(not(test), lang = "termination")]
+#[cfg_attr(not(any(test, doctest)), lang = "termination")]
 #[stable(feature = "termination_trait_lib", since = "1.61.0")]
 #[rustc_on_unimplemented(on(
     cause = "MainFunctionType",
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index 5c83f72f3c1..335944845ae 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -154,8 +154,7 @@ fn lang_start_internal(
     ret_code
 }
 
-#[cfg(not(test))]
-#[inline(never)]
+#[cfg(not(any(test, doctest)))]
 #[lang = "start"]
 fn lang_start<T: crate::process::Termination + 'static>(
     main: fn() -> T,
diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs
index 0fff53f1887..d37b8ce6346 100644
--- a/library/std/src/sys/personality/mod.rs
+++ b/library/std/src/sys/personality/mod.rs
@@ -12,7 +12,7 @@
 
 mod dwarf;
 
-#[cfg(not(test))]
+#[cfg(not(any(test, doctest)))]
 cfg_if::cfg_if! {
     if #[cfg(target_os = "emscripten")] {
         mod emcc;
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 09dfb0caeec..42484543686 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -309,14 +309,16 @@ impl File {
                 && unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS
             {
                 unsafe {
-                    // Setting the allocation size to zero also sets the
-                    // EOF position to zero.
-                    let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };
+                    // This originally used `FileAllocationInfo` instead of
+                    // `FileEndOfFileInfo` but that wasn't supported by WINE.
+                    // It's arguable which fits the semantics of `OpenOptions`
+                    // better so let's just use the more widely supported method.
+                    let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 };
                     let result = c::SetFileInformationByHandle(
                         handle.as_raw_handle(),
-                        c::FileAllocationInfo,
-                        ptr::addr_of!(alloc).cast::<c_void>(),
-                        mem::size_of::<c::FILE_ALLOCATION_INFO>() as u32,
+                        c::FileEndOfFileInfo,
+                        ptr::addr_of!(eof).cast::<c_void>(),
+                        mem::size_of::<c::FILE_END_OF_FILE_INFO>() as u32,
                     );
                     if result == 0 {
                         return Err(io::Error::last_os_error());
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index f452b944e75..f8e6d629ba3 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -55,7 +55,6 @@ dependencies = [
  "cmake",
  "fd-lock",
  "filetime",
- "hex",
  "home",
  "ignore",
  "junction",
@@ -314,12 +313,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
-name = "hex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
-[[package]]
 name = "home"
 version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 24951cf20fa..077d1954b7b 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -39,7 +39,6 @@ clap = { version = "4.4.7", default-features = false, features = ["std", "usage"
 clap_complete = "4.4.3"
 cmake = "0.1.38"
 filetime = "0.2"
-hex = "0.4"
 home = "0.5.4"
 ignore = "0.4.10"
 libc = "0.2.150"
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 2328dd822af..fea194a80ef 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -616,22 +616,6 @@ class RustBuild(object):
             with output(self.rustc_stamp()) as rust_stamp:
                 rust_stamp.write(key)
 
-    def _download_component_helper(
-        self, filename, pattern, tarball_suffix, rustc_cache,
-    ):
-        key = self.stage0_compiler.date
-
-        tarball = os.path.join(rustc_cache, filename)
-        if not os.path.exists(tarball):
-            get(
-                self.download_url,
-                "dist/{}/{}".format(key, filename),
-                tarball,
-                self.checksums_sha256,
-                verbose=self.verbose,
-            )
-        unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
-
     def should_fix_bins_and_dylibs(self):
         """Whether or not `fix_bin_or_dylib` needs to be run; can only be True
         on NixOS or if config.toml has `build.patch-binaries-for-nix` set.
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 18136635c42..38c55b20344 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -16,11 +16,11 @@
 //! never get replaced.
 
 use std::env;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process::{Child, Command};
 use std::time::Instant;
 
-use dylib_util::{dylib_path, dylib_path_var};
+use dylib_util::{dylib_path, dylib_path_var, exe};
 
 #[path = "../utils/bin_helpers.rs"]
 mod bin_helpers;
@@ -29,8 +29,10 @@ mod bin_helpers;
 mod dylib_util;
 
 fn main() {
-    let args = env::args_os().skip(1).collect::<Vec<_>>();
-    let arg = |name| args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
+    let orig_args = env::args_os().skip(1).collect::<Vec<_>>();
+    let mut args = orig_args.clone();
+    let arg =
+        |name| orig_args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
 
     let stage = bin_helpers::parse_rustc_stage();
     let verbose = bin_helpers::parse_rustc_verbose();
@@ -45,7 +47,8 @@ fn main() {
     // determine the version of the compiler, the real compiler needs to be
     // used. Currently, these two states are differentiated based on whether
     // --target and -vV is/isn't passed.
-    let (rustc, libdir) = if target.is_none() && version.is_none() {
+    let is_build_script = target.is_none() && version.is_none();
+    let (rustc, libdir) = if is_build_script {
         ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
     } else {
         ("RUSTC_REAL", "RUSTC_LIBDIR")
@@ -54,12 +57,47 @@ fn main() {
     let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
     let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);
 
-    let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
+    let rustc_real = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
     let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
     let mut dylib_path = dylib_path();
     dylib_path.insert(0, PathBuf::from(&libdir));
 
-    let mut cmd = Command::new(rustc);
+    // if we're running clippy, trust cargo-clippy to set clippy-driver appropriately (and don't override it with rustc).
+    // otherwise, substitute whatever cargo thinks rustc should be with RUSTC_REAL.
+    // NOTE: this means we ignore RUSTC in the environment.
+    // FIXME: We might want to consider removing RUSTC_REAL and setting RUSTC directly?
+    // NOTE: we intentionally pass the name of the host, not the target.
+    let host = env::var("CFG_COMPILER_BUILD_TRIPLE").unwrap();
+    let is_clippy = args[0].to_string_lossy().ends_with(&exe("clippy-driver", &host));
+    let rustc_driver = if is_clippy {
+        if is_build_script {
+            // Don't run clippy on build scripts (for one thing, we may not have libstd built with
+            // the appropriate version yet, e.g. for stage 1 std).
+            // Also remove the `clippy-driver` param in addition to the RUSTC param.
+            args.drain(..2);
+            rustc_real
+        } else {
+            args.remove(0)
+        }
+    } else {
+        // Cargo doesn't respect RUSTC_WRAPPER for version information >:(
+        // don't remove the first arg if we're being run as RUSTC instead of RUSTC_WRAPPER.
+        // Cargo also sometimes doesn't pass the `.exe` suffix on Windows - add it manually.
+        let current_exe = env::current_exe().expect("couldn't get path to rustc shim");
+        let arg0 = exe(args[0].to_str().expect("only utf8 paths are supported"), &host);
+        if Path::new(&arg0) == current_exe {
+            args.remove(0);
+        }
+        rustc_real
+    };
+
+    let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER_REAL") {
+        let mut cmd = Command::new(wrapper);
+        cmd.arg(rustc_driver);
+        cmd
+    } else {
+        Command::new(rustc_driver)
+    };
     cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
 
     // Get the name of the crate we're compiling, if any.
diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index 6372db96afb..4b993945f19 100644
--- a/src/bootstrap/src/core/build_steps/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -149,8 +149,14 @@ fn clean_default(build: &Build) {
     rm_rf(&build.out.join("bootstrap-shims-dump"));
     rm_rf(&build.out.join("rustfmt.stamp"));
 
-    for host in &build.hosts {
-        let entries = match build.out.join(host.triple).read_dir() {
+    let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t.triple)).collect();
+    // After cross-compilation, artifacts of the host architecture (which may differ from build.host)
+    // might not get removed.
+    // Adding its path (linked one for easier accessibility) will solve this problem.
+    hosts.push(build.out.join("host"));
+
+    for host in hosts {
+        let entries = match host.read_dir() {
             Ok(iter) => iter,
             Err(_) => continue,
         };
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 11e789a996a..df4d1a43dab 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1809,10 +1809,6 @@ pub fn run_cargo(
     is_check: bool,
     rlib_only_metadata: bool,
 ) -> Vec<PathBuf> {
-    if builder.config.dry_run() {
-        return Vec::new();
-    }
-
     // `target_root_dir` looks like $dir/$target/release
     let target_root_dir = stamp.parent().unwrap();
     // `target_deps_dir` looks like $dir/$target/release/deps
@@ -1919,6 +1915,10 @@ pub fn run_cargo(
         crate::exit!(1);
     }
 
+    if builder.config.dry_run() {
+        return Vec::new();
+    }
+
     // Ok now we need to actually find all the files listed in `toplevel`. We've
     // got a list of prefix/extensions and we basically just need to find the
     // most recent file in the `deps` folder corresponding to each one.
@@ -1974,9 +1974,6 @@ pub fn stream_cargo(
     cb: &mut dyn FnMut(CargoMessage<'_>),
 ) -> bool {
     let mut cargo = Command::from(cargo);
-    if builder.config.dry_run() {
-        return true;
-    }
     // Instruct Cargo to give us json messages on stdout, critically leaving
     // stderr as piped so we can get those pretty colors.
     let mut message_format = if builder.config.json_output {
@@ -1995,6 +1992,11 @@ pub fn stream_cargo(
     }
 
     builder.verbose(&format!("running: {cargo:?}"));
+
+    if builder.config.dry_run() {
+        return true;
+    }
+
     let mut child = match cargo.spawn() {
         Ok(child) => child,
         Err(e) => panic!("failed to execute command: {cargo:?}\nERROR: {e}"),
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index fe84b95f90c..9c897ae1bb7 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -1,6 +1,7 @@
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::t;
 use crate::utils::change_tracker::CONFIG_CHANGE_HISTORY;
+use crate::utils::helpers::hex_encode;
 use crate::Config;
 use sha2::Digest;
 use std::env::consts::EXE_SUFFIX;
@@ -566,7 +567,7 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
     if let Ok(current) = fs::read_to_string(&vscode_settings) {
         let mut hasher = sha2::Sha256::new();
         hasher.update(&current);
-        let hash = hex::encode(hasher.finalize().as_slice());
+        let hash = hex_encode(hasher.finalize().as_slice());
         if hash == *current_hash {
             return Ok(true);
         } else if historical_hashes.contains(&hash.as_str()) {
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 28761a7ee4b..e1809644350 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1152,6 +1152,44 @@ impl<'a> Builder<'a> {
         self.ensure(tool::Rustdoc { compiler })
     }
 
+    pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> Command {
+        let initial_sysroot_bin = self.initial_rustc.parent().unwrap();
+        // Set PATH to include the sysroot bin dir so clippy can find cargo.
+        // FIXME: once rust-clippy#11944 lands on beta, set `CARGO` directly instead.
+        let path = t!(env::join_paths(
+            // The sysroot comes first in PATH to avoid using rustup's cargo.
+            std::iter::once(PathBuf::from(initial_sysroot_bin))
+                .chain(env::split_paths(&t!(env::var("PATH"))))
+        ));
+
+        if run_compiler.stage == 0 {
+            // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy.
+            let cargo_clippy = self.build.config.download_clippy();
+            let mut cmd = Command::new(cargo_clippy);
+            cmd.env("PATH", &path);
+            return cmd;
+        }
+
+        let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build);
+        self.ensure(tool::Clippy {
+            compiler: build_compiler,
+            target: self.build.build,
+            extra_features: vec![],
+        });
+        let cargo_clippy = self.ensure(tool::CargoClippy {
+            compiler: build_compiler,
+            target: self.build.build,
+            extra_features: vec![],
+        });
+        let mut dylib_path = helpers::dylib_path();
+        dylib_path.insert(0, self.sysroot(run_compiler).join("lib"));
+
+        let mut cmd = Command::new(cargo_clippy.unwrap());
+        cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
+        cmd.env("PATH", path);
+        cmd
+    }
+
     pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command {
         let mut cmd = Command::new(&self.bootstrap_out.join("rustdoc"));
         cmd.env("RUSTC_STAGE", compiler.stage.to_string())
@@ -1200,7 +1238,12 @@ impl<'a> Builder<'a> {
         target: TargetSelection,
         cmd: &str,
     ) -> Command {
-        let mut cargo = Command::new(&self.initial_cargo);
+        let mut cargo = if cmd == "clippy" {
+            self.cargo_clippy_cmd(compiler)
+        } else {
+            Command::new(&self.initial_cargo)
+        };
+
         // Run cargo from the source root so it can find .cargo/config.
         // This matters when using vendoring and the working directory is outside the repository.
         cargo.current_dir(&self.src);
@@ -1324,6 +1367,23 @@ impl<'a> Builder<'a> {
             compiler.stage
         };
 
+        // We synthetically interpret a stage0 compiler used to build tools as a
+        // "raw" compiler in that it's the exact snapshot we download. Normally
+        // the stage0 build means it uses libraries build by the stage0
+        // compiler, but for tools we just use the precompiled libraries that
+        // we've downloaded
+        let use_snapshot = mode == Mode::ToolBootstrap;
+        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
+
+        let maybe_sysroot = self.sysroot(compiler);
+        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
+        let libdir = self.rustc_libdir(compiler);
+
+        let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
+        if !matches!(self.config.dry_run, DryRun::SelfCheck) {
+            self.verbose_than(0, &format!("using sysroot {sysroot_str}"));
+        }
+
         let mut rustflags = Rustflags::new(target);
         if stage != 0 {
             if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
@@ -1335,41 +1395,16 @@ impl<'a> Builder<'a> {
                 cargo.args(s.split_whitespace());
             }
             rustflags.env("RUSTFLAGS_BOOTSTRAP");
-            if cmd == "clippy" {
-                // clippy overwrites sysroot if we pass it to cargo.
-                // Pass it directly to clippy instead.
-                // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
-                // so it has no way of knowing the sysroot.
-                rustflags.arg("--sysroot");
-                rustflags.arg(
-                    self.sysroot(compiler)
-                        .as_os_str()
-                        .to_str()
-                        .expect("sysroot must be valid UTF-8"),
-                );
-                // Only run clippy on a very limited subset of crates (in particular, not build scripts).
-                cargo.arg("-Zunstable-options");
-                // Explicitly does *not* set `--cfg=bootstrap`, since we're using a nightly clippy.
-                let host_version = Command::new("rustc").arg("--version").output().map_err(|_| ());
-                let output = host_version.and_then(|output| {
-                    if output.status.success() {
-                        Ok(output)
-                    } else {
-                        Err(())
-                    }
-                }).unwrap_or_else(|_| {
-                    eprintln!(
-                        "ERROR: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component"
-                    );
-                    eprintln!("HELP: try `rustup component add clippy`");
-                    crate::exit!(1);
-                });
-                if !t!(std::str::from_utf8(&output.stdout)).contains("nightly") {
-                    rustflags.arg("--cfg=bootstrap");
-                }
-            } else {
-                rustflags.arg("--cfg=bootstrap");
-            }
+            rustflags.arg("--cfg=bootstrap");
+        }
+
+        if cmd == "clippy" {
+            // clippy overwrites sysroot if we pass it to cargo.
+            // Pass it directly to clippy instead.
+            // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
+            // so it has no way of knowing the sysroot.
+            rustflags.arg("--sysroot");
+            rustflags.arg(sysroot_str);
         }
 
         let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
@@ -1564,18 +1599,6 @@ impl<'a> Builder<'a> {
 
         let want_rustdoc = self.doc_tests != DocTests::No;
 
-        // We synthetically interpret a stage0 compiler used to build tools as a
-        // "raw" compiler in that it's the exact snapshot we download. Normally
-        // the stage0 build means it uses libraries build by the stage0
-        // compiler, but for tools we just use the precompiled libraries that
-        // we've downloaded
-        let use_snapshot = mode == Mode::ToolBootstrap;
-        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
-
-        let maybe_sysroot = self.sysroot(compiler);
-        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
-        let libdir = self.rustc_libdir(compiler);
-
         // Clear the output directory if the real rustc we're using has changed;
         // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
         //
@@ -1611,10 +1634,19 @@ impl<'a> Builder<'a> {
             )
             .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
             .env("RUSTC_BREAK_ON_ICE", "1");
-        // Clippy support is a hack and uses the default `cargo-clippy` in path.
-        // Don't override RUSTC so that the `cargo-clippy` in path will be run.
-        if cmd != "clippy" {
-            cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
+
+        // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
+        // sysroot depending on whether we're building build scripts.
+        // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not
+        // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs.
+        cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc"));
+        // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:(
+        cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
+
+        // Someone might have set some previous rustc wrapper (e.g.
+        // sccache) before bootstrap overrode it. Respect that variable.
+        if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") {
+            cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper);
         }
 
         // Dealing with rpath here is a little special, so let's go into some
@@ -1991,7 +2023,11 @@ impl<'a> Builder<'a> {
         // Environment variables *required* throughout the build
         //
         // FIXME: should update code to not require this env var
+
+        // The host this new compiler will *run* on.
         cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple);
+        // The host this new compiler is being *built* on.
+        cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple);
 
         // Set this for all builds to make sure doc builds also get it.
         cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index 0a5844a6859..ec404ab8580 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -11,9 +11,9 @@ use std::{
 use build_helper::ci::CiEnv;
 use xz2::bufread::XzDecoder;
 
-use crate::core::build_steps::llvm::detect_llvm_sha;
 use crate::core::config::RustfmtMetadata;
 use crate::utils::helpers::{check_run, exe, program_out_of_date};
+use crate::{core::build_steps::llvm::detect_llvm_sha, utils::helpers::hex_encode};
 use crate::{t, Config};
 
 static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock<bool> = OnceLock::new();
@@ -208,7 +208,10 @@ impl Config {
             Some(other) => panic!("unsupported protocol {other} in {url}"),
             None => panic!("no protocol in {url}"),
         }
-        t!(std::fs::rename(&tempfile, dest_path));
+        t!(
+            std::fs::rename(&tempfile, dest_path),
+            format!("failed to rename {tempfile:?} to {dest_path:?}")
+        );
     }
 
     fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
@@ -342,7 +345,7 @@ impl Config {
             reader.consume(l);
         }
 
-        let checksum = hex::encode(hasher.finalize().as_slice());
+        let checksum = hex_encode(hasher.finalize().as_slice());
         let verified = checksum == expected;
 
         if !verified {
@@ -375,6 +378,32 @@ enum DownloadSource {
 
 /// Functions that are only ever called once, but named for clarify and to avoid thousand-line functions.
 impl Config {
+    pub(crate) fn download_clippy(&self) -> PathBuf {
+        self.verbose("downloading stage0 clippy artifacts");
+
+        let date = &self.stage0_metadata.compiler.date;
+        let version = &self.stage0_metadata.compiler.version;
+        let host = self.build;
+
+        let bin_root = self.out.join(host.triple).join("stage0");
+        let clippy_stamp = bin_root.join(".clippy-stamp");
+        let cargo_clippy = bin_root.join("bin").join(exe("cargo-clippy", host));
+        if cargo_clippy.exists() && !program_out_of_date(&clippy_stamp, &date) {
+            return cargo_clippy;
+        }
+
+        let filename = format!("clippy-{version}-{host}.tar.xz");
+        self.download_component(DownloadSource::Dist, filename, "clippy-preview", date, "stage0");
+        if self.should_fix_bins_and_dylibs() {
+            self.fix_bin_or_dylib(&cargo_clippy);
+            self.fix_bin_or_dylib(&cargo_clippy.with_file_name(exe("clippy-driver", host)));
+        }
+
+        cargo_clippy
+    }
+
+    /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't
+    /// reuse target directories or artifacts
     pub(crate) fn maybe_download_rustfmt(&self) -> Option<PathBuf> {
         let RustfmtMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?;
         let channel = format!("{version}-{date}");
@@ -544,6 +573,10 @@ impl Config {
         key: &str,
         destination: &str,
     ) {
+        if self.dry_run() {
+            return;
+        }
+
         let cache_dst = self.out.join("cache");
         let cache_dir = cache_dst.join(key);
         if !cache_dir.exists() {
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 9efb4990cab..871318de595 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -34,6 +34,7 @@ use filetime::FileTime;
 use sha2::digest::Digest;
 use termcolor::{ColorChoice, StandardStream, WriteColor};
 use utils::channel::GitInfo;
+use utils::helpers::hex_encode;
 
 use crate::core::builder;
 use crate::core::builder::Kind;
@@ -1871,7 +1872,7 @@ pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String {
     hasher.update(status);
     hasher.update(additional_input);
 
-    hex::encode(hasher.finalize().as_slice())
+    hex_encode(hasher.finalize().as_slice())
 }
 
 /// Ensures that the behavior dump directory is properly initialized.
diff --git a/src/bootstrap/src/tests/builder.rs b/src/bootstrap/src/tests/builder.rs
index 744015e8e82..700ebcf5e37 100644
--- a/src/bootstrap/src/tests/builder.rs
+++ b/src/bootstrap/src/tests/builder.rs
@@ -156,22 +156,6 @@ fn alias_and_path_for_library() {
     assert_eq!(first(cache.all::<doc::Std>()), &[doc_std!(A => A, stage = 0)]);
 }
 
-#[test]
-fn test_beta_rev_parsing() {
-    use crate::utils::helpers::extract_beta_rev;
-
-    // single digit revision
-    assert_eq!(extract_beta_rev("1.99.9-beta.7 (xxxxxx)"), Some("7".to_string()));
-    // multiple digits
-    assert_eq!(extract_beta_rev("1.99.9-beta.777 (xxxxxx)"), Some("777".to_string()));
-    // nightly channel (no beta revision)
-    assert_eq!(extract_beta_rev("1.99.9-nightly (xxxxxx)"), None);
-    // stable channel (no beta revision)
-    assert_eq!(extract_beta_rev("1.99.9 (xxxxxxx)"), None);
-    // invalid string
-    assert_eq!(extract_beta_rev("invalid"), None);
-}
-
 mod defaults {
     use super::{configure, first, run_build};
     use crate::core::builder::*;
diff --git a/src/bootstrap/src/tests/helpers.rs b/src/bootstrap/src/tests/helpers.rs
new file mode 100644
index 00000000000..afe18aebafa
--- /dev/null
+++ b/src/bootstrap/src/tests/helpers.rs
@@ -0,0 +1,59 @@
+use crate::utils::helpers::{extract_beta_rev, hex_encode, make};
+use std::path::PathBuf;
+
+#[test]
+fn test_make() {
+    for (host, make_path) in vec![
+        ("dragonfly", PathBuf::from("gmake")),
+        ("netbsd", PathBuf::from("gmake")),
+        ("freebsd", PathBuf::from("gmake")),
+        ("openbsd", PathBuf::from("gmake")),
+        ("linux", PathBuf::from("make")),
+        // for checking the default
+        ("_", PathBuf::from("make")),
+    ] {
+        assert_eq!(make(host), make_path);
+    }
+}
+
+#[cfg(unix)]
+#[test]
+fn test_absolute_unix() {
+    use crate::utils::helpers::absolute_unix;
+
+    // Test an absolute path
+    let path = PathBuf::from("/home/user/file.txt");
+    assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("/home/user/file.txt"));
+
+    // Test an absolute path with double leading slashes
+    let path = PathBuf::from("//root//file.txt");
+    assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("//root/file.txt"));
+
+    // Test a relative path
+    let path = PathBuf::from("relative/path");
+    assert_eq!(
+        absolute_unix(&path).unwrap(),
+        std::env::current_dir().unwrap().join("relative/path")
+    );
+}
+
+#[test]
+fn test_beta_rev_parsing() {
+    // single digit revision
+    assert_eq!(extract_beta_rev("1.99.9-beta.7 (xxxxxx)"), Some("7".to_string()));
+    // multiple digits
+    assert_eq!(extract_beta_rev("1.99.9-beta.777 (xxxxxx)"), Some("777".to_string()));
+    // nightly channel (no beta revision)
+    assert_eq!(extract_beta_rev("1.99.9-nightly (xxxxxx)"), None);
+    // stable channel (no beta revision)
+    assert_eq!(extract_beta_rev("1.99.9 (xxxxxxx)"), None);
+    // invalid string
+    assert_eq!(extract_beta_rev("invalid"), None);
+}
+
+#[test]
+fn test_string_to_hex_encode() {
+    let input_string = "Hello, World!";
+    let hex_string = hex_encode(input_string);
+    assert_eq!(hex_string, "48656c6c6f2c20576f726c6421");
+}
diff --git a/src/bootstrap/src/tests/setup.rs b/src/bootstrap/src/tests/setup.rs
index 0fe6e4a4644..3e4d66c7454 100644
--- a/src/bootstrap/src/tests/setup.rs
+++ b/src/bootstrap/src/tests/setup.rs
@@ -1,11 +1,12 @@
 use super::{RUST_ANALYZER_SETTINGS, SETTINGS_HASHES};
+use crate::utils::helpers::hex_encode;
 use sha2::Digest;
 
 #[test]
 fn check_matching_settings_hash() {
     let mut hasher = sha2::Sha256::new();
     hasher.update(&RUST_ANALYZER_SETTINGS);
-    let hash = hex::encode(hasher.finalize().as_slice());
+    let hash = hex_encode(hasher.finalize().as_slice());
     assert_eq!(
         &hash,
         SETTINGS_HASHES.last().unwrap(),
diff --git a/src/bootstrap/src/utils/dylib.rs b/src/bootstrap/src/utils/dylib.rs
index 279a6a010f1..b6e7aec1756 100644
--- a/src/bootstrap/src/utils/dylib.rs
+++ b/src/bootstrap/src/utils/dylib.rs
@@ -25,3 +25,16 @@ pub fn dylib_path() -> Vec<std::path::PathBuf> {
     };
     std::env::split_paths(&var).collect()
 }
+
+/// Given an executable called `name`, return the filename for the
+/// executable for a particular target.
+#[allow(dead_code)]
+pub fn exe(name: &str, target: &str) -> String {
+    if target.contains("windows") {
+        format!("{name}.exe")
+    } else if target.contains("uefi") {
+        format!("{name}.efi")
+    } else {
+        name.to_string()
+    }
+}
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index d96608db22c..0c4297db6cc 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -20,6 +20,10 @@ use crate::LldMode;
 
 pub use crate::utils::dylib::{dylib_path, dylib_path_var};
 
+#[cfg(test)]
+#[path = "../tests/helpers.rs"]
+mod tests;
+
 /// A helper macro to `unwrap` a result except also print out details like:
 ///
 /// * The file/line of the panic
@@ -46,16 +50,8 @@ macro_rules! t {
 }
 pub use t;
 
-/// Given an executable called `name`, return the filename for the
-/// executable for a particular target.
 pub fn exe(name: &str, target: TargetSelection) -> String {
-    if target.is_windows() {
-        format!("{name}.exe")
-    } else if target.contains("uefi") {
-        format!("{name}.efi")
-    } else {
-        name.to_string()
-    }
+    crate::utils::dylib::exe(name, &target.triple)
 }
 
 /// Returns `true` if the file name given looks like a dynamic library.
@@ -548,3 +544,11 @@ pub fn add_rustdoc_cargo_linker_args(
         cmd.env("RUSTDOCFLAGS", flags);
     }
 }
+
+/// Converts `T` into a hexadecimal `String`.
+pub fn hex_encode<T>(input: T) -> String
+where
+    T: AsRef<[u8]>,
+{
+    input.as_ref().iter().map(|x| format!("{:02x}", x)).collect()
+}
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index 82d37f0f633..f8fcda5070f 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -39,8 +39,14 @@ COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
 COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
 
 ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
+
+# Run clippy just to make sure it doesn't error out; we don't actually want to gate on the warnings
+# though.
+# Ideally we'd use stage 1, but that ICEs: https://github.com/rust-lang/rust-clippy/issues/11230
+
 ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
+           python3 ../x.py clippy --stage 0 -Awarnings && \
            python3 ../x.py build --stage 0 src/tools/build-manifest && \
            python3 ../x.py test --stage 0 src/tools/compiletest && \
            python3 ../x.py test --stage 0 core alloc std test proc_macro && \
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index cfbe1e09cde..4a4f1ae98e4 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -221,7 +221,7 @@ metrics.
 ## link-self-contained
 
 On `windows-gnu`, `linux-musl`, and `wasi` targets, this flag controls whether the
-linker will use libraries and objects shipped with Rust instead or those in the system.
+linker will use libraries and objects shipped with Rust instead of those in the system.
 It takes one of the following values:
 
 * no value: rustc will use heuristic to disable self-contained mode if system has necessary tools.
@@ -249,9 +249,6 @@ flavor. Valid options are:
 * `gcc`: use the `cc` executable, which is typically gcc or clang on many systems.
 * `ld`: use the `ld` executable.
 * `msvc`: use the `link.exe` executable from Microsoft Visual Studio MSVC.
-* `ptx`: use [`rust-ptx-linker`](https://github.com/denzp/rust-ptx-linker)
-  for Nvidia NVPTX GPGPU support.
-* `bpf`: use [`bpf-linker`](https://github.com/alessandrod/bpf-linker) for eBPF support.
 * `wasm-ld`: use the [`wasm-ld`](https://lld.llvm.org/WebAssembly.html)
   executable, a port of LLVM `lld` for WebAssembly.
 * `ld64.lld`: use the LLVM `lld` executable with the [`-flavor darwin`
diff --git a/src/doc/unstable-book/src/compiler-flags/codegen-options.md b/src/doc/unstable-book/src/compiler-flags/codegen-options.md
new file mode 100644
index 00000000000..31dfcdb199a
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/codegen-options.md
@@ -0,0 +1,56 @@
+# Unstable codegen options
+
+All of these options are passed to `rustc` via the `-C` flag, short for "codegen". The flags are
+stable but some of their values are individually unstable, and also require using `-Z
+unstable-options` to be accepted.
+
+## linker-flavor
+
+In addition to the stable set of linker flavors, the following unstable values also exist:
+- `ptx`: use [`rust-ptx-linker`](https://github.com/denzp/rust-ptx-linker)
+  for Nvidia NVPTX GPGPU support.
+- `bpf`: use [`bpf-linker`](https://github.com/alessandrod/bpf-linker) for eBPF support.
+
+Additionally, a set of more precise linker flavors also exists, for example allowing targets to
+declare that they use the LLD linker by default. The following values are currently unstable, and
+the goal is for them to become stable, and preferred in practice over the existing stable values:
+- `gnu`: unix-like linker with GNU extensions
+- `gnu-lld`: `gnu` using LLD
+- `gnu-cc`: `gnu` using a C/C++ compiler as the linker driver
+- `gnu-lld-cc`: `gnu` using LLD and a C/C++ compiler as the linker driver
+- `darwin`: unix-like linker for Apple targets
+- `darwin-lld`: `darwin` using LLD
+- `darwin-cc`: `darwin` using a C/C++ compiler as the linker driver
+- `darwin-lld-cc`: `darwin` using LLD and a C/C++ compiler as the linker driver
+- `wasm-lld`: unix-like linker for Wasm targets, with LLD
+- `wasm-lld-cc`: unix-like linker for Wasm targets, with LLD and a C/C++ compiler as the linker
+  driver
+- `unix`: basic unix-like linker for "any other Unix" targets (Solaris/illumos, L4Re, MSP430, etc),
+  not supported with LLD.
+- `unix-cc`: `unix` using a C/C++ compiler as the linker driver
+- `msvc-lld`: MSVC-style linker for Windows and UEFI, with LLD
+- `em-cc`: emscripten compiler frontend, similar to `wasm-lld-cc` with a different interface
+
+## link-self-contained
+
+This flag generally controls whether the linker will use libraries and objects shipped with Rust
+instead of those in the system. The stable boolean values for this flag are coarse-grained
+(everything or nothing), but there exists a set of unstable values with finer-grained control,
+`-Clink-self-contained` can accept a comma-separated list of components, individually enabled
+(`+component`) or disabled (`-component`):
+- `crto`: CRT objects (e.g. on `windows-gnu`, `musl`, `wasi` targets)
+- `libc`: libc static library (e.g. on `musl`, `wasi` targets)
+- `unwind`: libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets)
+- `linker`: linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for
+  `rust-lld`)
+- `sanitizers`: sanitizer runtime libraries
+- `mingw`: other MinGW libs and Windows import libs
+
+Out of the above self-contained linking components, `linker` is the only one currently implemented
+(beyond parsing the CLI options).
+
+It refers to the LLD linker, built from the same LLVM revision used by rustc (named `rust-lld` to
+avoid naming conflicts), that is distributed via `rustup` with the compiler (and is used by default
+for the wasm targets). One can also opt-in to use it by combining this flag with an appropriate
+linker flavor: for example, `-Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker` will use the
+toolchain's `rust-lld` as the linker.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 28ea662eab3..828e7f959b4 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -184,22 +184,6 @@ fn clean_generic_bound<'tcx>(
 ) -> Option<GenericBound> {
     Some(match *bound {
         hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)),
-        hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => {
-            let def_id = cx.tcx.require_lang_item(lang_item, Some(span));
-
-            let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(cx.tcx, def_id));
-
-            let generic_args = clean_generic_args(generic_args, cx);
-            let GenericArgs::AngleBracketed { bindings, .. } = generic_args else {
-                bug!("clean: parenthesized `GenericBound::LangItemTrait`");
-            };
-
-            let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, bindings);
-            GenericBound::TraitBound(
-                PolyTrait { trait_, generic_params: vec![] },
-                hir::TraitBoundModifier::None,
-            )
-        }
         hir::GenericBound::Trait(ref t, modifier) => {
             // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op.
             if modifier == hir::TraitBoundModifier::MaybeConst
@@ -743,7 +727,7 @@ pub(crate) fn clean_generics<'tcx>(
                     .into_iter()
                     .map(|(lifetime, bounds)| WherePredicate::RegionPredicate { lifetime, bounds }),
             )
-            .chain(eq_predicates.into_iter())
+            .chain(eq_predicates)
             .collect(),
     }
 }
@@ -2646,6 +2630,40 @@ fn filter_tokens_from_list(
     tokens
 }
 
+fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool {
+    if is_inline {
+        ident == sym::hidden || ident == sym::inline || ident == sym::no_inline
+    } else {
+        ident == sym::cfg
+    }
+}
+
+/// Remove attributes from `normal` that should not be inherited by `use` re-export.
+/// Before calling this function, make sure `normal` is a `#[doc]` attribute.
+fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) {
+    match normal.item.args {
+        ast::AttrArgs::Delimited(ref mut args) => {
+            let tokens = filter_tokens_from_list(&args.tokens, |token| {
+                !matches!(
+                    token,
+                    TokenTree::Token(
+                        Token {
+                            kind: TokenKind::Ident(
+                                ident,
+                                _,
+                            ),
+                            ..
+                        },
+                        _,
+                    ) if filter_doc_attr_ident(*ident, is_inline),
+                )
+            });
+            args.tokens = TokenStream::new(tokens);
+        }
+        ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {}
+    }
+}
+
 /// When inlining items, we merge their attributes (and all the reexports attributes too) with the
 /// final reexport. For example:
 ///
@@ -2672,13 +2690,6 @@ fn add_without_unwanted_attributes<'hir>(
     is_inline: bool,
     import_parent: Option<DefId>,
 ) {
-    // If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
-    if !is_inline {
-        for attr in new_attrs {
-            attrs.push((Cow::Borrowed(attr), import_parent));
-        }
-        return;
-    }
     for attr in new_attrs {
         if matches!(attr.kind, ast::AttrKind::DocComment(..)) {
             attrs.push((Cow::Borrowed(attr), import_parent));
@@ -2687,33 +2698,14 @@ fn add_without_unwanted_attributes<'hir>(
         let mut attr = attr.clone();
         match attr.kind {
             ast::AttrKind::Normal(ref mut normal) => {
-                if let [ident] = &*normal.item.path.segments
-                    && let ident = ident.ident.name
-                    && ident == sym::doc
-                {
-                    match normal.item.args {
-                        ast::AttrArgs::Delimited(ref mut args) => {
-                            let tokens = filter_tokens_from_list(&args.tokens, |token| {
-                                !matches!(
-                                    token,
-                                    TokenTree::Token(
-                                        Token {
-                                            kind: TokenKind::Ident(
-                                                sym::hidden | sym::inline | sym::no_inline,
-                                                _,
-                                            ),
-                                            ..
-                                        },
-                                        _,
-                                    ),
-                                )
-                            });
-                            args.tokens = TokenStream::new(tokens);
-                            attrs.push((Cow::Owned(attr), import_parent));
-                        }
-                        ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {
-                            attrs.push((Cow::Owned(attr), import_parent));
-                        }
+                if let [ident] = &*normal.item.path.segments {
+                    let ident = ident.ident.name;
+                    if ident == sym::doc {
+                        filter_doc_attr(normal, is_inline);
+                        attrs.push((Cow::Owned(attr), import_parent));
+                    } else if ident != sym::cfg {
+                        // If it's not a `cfg()` attribute, we keep it.
+                        attrs.push((Cow::Owned(attr), import_parent));
                     }
                 }
             }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 8e8b7ab346b..fb09d399b98 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -15,11 +15,11 @@ use rustc_session::config::{
 use rustc_session::getopts;
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
-use rustc_session::EarlyErrorHandler;
+use rustc_session::EarlyDiagCtxt;
 use rustc_span::edition::Edition;
 use rustc_target::spec::TargetTriple;
 
-use crate::core::new_handler;
+use crate::core::new_dcx;
 use crate::externalfiles::ExternalHtml;
 use crate::html;
 use crate::html::markdown::IdMap;
@@ -320,38 +320,38 @@ impl Options {
     /// Parses the given command-line for options. If an error message or other early-return has
     /// been printed, returns `Err` with the exit code.
     pub(crate) fn from_matches(
-        handler: &mut EarlyErrorHandler,
+        early_dcx: &mut EarlyDiagCtxt,
         matches: &getopts::Matches,
         args: Vec<String>,
     ) -> Result<(Options, RenderOptions), i32> {
         // Check for unstable options.
-        nightly_options::check_nightly_options(handler, matches, &opts());
+        nightly_options::check_nightly_options(early_dcx, matches, &opts());
 
         if args.is_empty() || matches.opt_present("h") || matches.opt_present("help") {
             crate::usage("rustdoc");
             return Err(0);
         } else if matches.opt_present("version") {
-            rustc_driver::version!(&handler, "rustdoc", matches);
+            rustc_driver::version!(&early_dcx, "rustdoc", matches);
             return Err(0);
         }
 
-        if rustc_driver::describe_flag_categories(handler, &matches) {
+        if rustc_driver::describe_flag_categories(early_dcx, &matches) {
             return Err(0);
         }
 
-        let color = config::parse_color(handler, matches);
+        let color = config::parse_color(early_dcx, matches);
         let config::JsonConfig { json_rendered, json_unused_externs, .. } =
-            config::parse_json(handler, matches);
-        let error_format = config::parse_error_format(handler, matches, color, json_rendered);
+            config::parse_json(early_dcx, matches);
+        let error_format = config::parse_error_format(early_dcx, matches, color, json_rendered);
         let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default();
 
-        let codegen_options = CodegenOptions::build(handler, matches);
-        let unstable_opts = UnstableOptions::build(handler, matches);
+        let codegen_options = CodegenOptions::build(early_dcx, matches);
+        let unstable_opts = UnstableOptions::build(early_dcx, matches);
 
-        let diag = new_handler(error_format, None, diagnostic_width, &unstable_opts);
+        let dcx = new_dcx(error_format, None, diagnostic_width, &unstable_opts);
 
         // check for deprecated options
-        check_deprecated_options(matches, &diag);
+        check_deprecated_options(matches, &dcx);
 
         if matches.opt_strs("passes") == ["list"] {
             println!("Available passes for running rustdoc:");
@@ -391,7 +391,7 @@ impl Options {
                 match kind.parse() {
                     Ok(kind) => emit.push(kind),
                     Err(()) => {
-                        diag.err(format!("unrecognized emission type: {kind}"));
+                        dcx.err(format!("unrecognized emission type: {kind}"));
                         return Err(1);
                     }
                 }
@@ -403,7 +403,7 @@ impl Options {
             && !matches.opt_present("show-coverage")
             && !nightly_options::is_unstable_enabled(matches)
         {
-            handler.early_error(
+            early_dcx.early_error(
                 "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)",
             );
         }
@@ -421,7 +421,7 @@ impl Options {
             let paths = match theme::load_css_paths(content) {
                 Ok(p) => p,
                 Err(e) => {
-                    diag.struct_err(e).emit();
+                    dcx.struct_err(e).emit();
                     return Err(1);
                 }
             };
@@ -430,7 +430,7 @@ impl Options {
             println!("rustdoc: [check-theme] Starting tests! (Ignoring all other arguments)");
             for theme_file in to_check.iter() {
                 print!(" - Checking \"{theme_file}\"...");
-                let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag);
+                let (success, differences) = theme::test_theme_against(theme_file, &paths, &dcx);
                 if !differences.is_empty() || !success {
                     println!(" FAILED");
                     errors += 1;
@@ -447,27 +447,27 @@ impl Options {
             return Err(0);
         }
 
-        let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(handler, matches);
+        let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
 
         let input = PathBuf::from(if describe_lints {
             "" // dummy, this won't be used
         } else if matches.free.is_empty() {
-            diag.struct_err("missing file operand").emit();
+            dcx.struct_err("missing file operand").emit();
             return Err(1);
         } else if matches.free.len() > 1 {
-            diag.struct_err("too many file operands").emit();
+            dcx.struct_err("too many file operands").emit();
             return Err(1);
         } else {
             &matches.free[0]
         });
 
         let libs =
-            matches.opt_strs("L").iter().map(|s| SearchPath::from_cli_opt(handler, s)).collect();
-        let externs = parse_externs(handler, matches, &unstable_opts);
+            matches.opt_strs("L").iter().map(|s| SearchPath::from_cli_opt(early_dcx, s)).collect();
+        let externs = parse_externs(early_dcx, matches, &unstable_opts);
         let extern_html_root_urls = match parse_extern_html_roots(matches) {
             Ok(ex) => ex,
             Err(err) => {
-                diag.struct_err(err).emit();
+                dcx.struct_err(err).emit();
                 return Err(1);
             }
         };
@@ -526,7 +526,7 @@ impl Options {
         let no_run = matches.opt_present("no-run");
 
         if !should_test && no_run {
-            diag.err("the `--test` flag must be passed to enable `--no-run`");
+            dcx.err("the `--test` flag must be passed to enable `--no-run`");
             return Err(1);
         }
 
@@ -534,7 +534,7 @@ impl Options {
         let output = matches.opt_str("output").map(|s| PathBuf::from(&s));
         let output = match (out_dir, output) {
             (Some(_), Some(_)) => {
-                diag.struct_err("cannot use both 'out-dir' and 'output' at once").emit();
+                dcx.struct_err("cannot use both 'out-dir' and 'output' at once").emit();
                 return Err(1);
             }
             (Some(out_dir), None) => out_dir,
@@ -549,7 +549,7 @@ impl Options {
 
         if let Some(ref p) = extension_css {
             if !p.is_file() {
-                diag.struct_err("option --extend-css argument must be a file").emit();
+                dcx.struct_err("option --extend-css argument must be a file").emit();
                 return Err(1);
             }
         }
@@ -567,7 +567,7 @@ impl Options {
             let paths = match theme::load_css_paths(content) {
                 Ok(p) => p,
                 Err(e) => {
-                    diag.struct_err(e).emit();
+                    dcx.struct_err(e).emit();
                     return Err(1);
                 }
             };
@@ -576,23 +576,23 @@ impl Options {
                 matches.opt_strs("theme").iter().map(|s| (PathBuf::from(&s), s.to_owned()))
             {
                 if !theme_file.is_file() {
-                    diag.struct_err(format!("invalid argument: \"{theme_s}\""))
+                    dcx.struct_err(format!("invalid argument: \"{theme_s}\""))
                         .help("arguments to --theme must be files")
                         .emit();
                     return Err(1);
                 }
                 if theme_file.extension() != Some(OsStr::new("css")) {
-                    diag.struct_err(format!("invalid argument: \"{theme_s}\""))
+                    dcx.struct_err(format!("invalid argument: \"{theme_s}\""))
                         .help("arguments to --theme must have a .css extension")
                         .emit();
                     return Err(1);
                 }
-                let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag);
+                let (success, ret) = theme::test_theme_against(&theme_file, &paths, &dcx);
                 if !success {
-                    diag.struct_err(format!("error loading theme file: \"{theme_s}\"")).emit();
+                    dcx.struct_err(format!("error loading theme file: \"{theme_s}\"")).emit();
                     return Err(1);
                 } else if !ret.is_empty() {
-                    diag.struct_warn(format!(
+                    dcx.struct_warn(format!(
                         "theme file \"{theme_s}\" is missing CSS rules from the default theme",
                     ))
                     .warn("the theme may appear incorrect when loaded")
@@ -605,7 +605,7 @@ impl Options {
             }
         }
 
-        let edition = config::parse_crate_edition(handler, matches);
+        let edition = config::parse_crate_edition(early_dcx, matches);
 
         let mut id_map = html::markdown::IdMap::new();
         let Some(external_html) = ExternalHtml::load(
@@ -615,7 +615,7 @@ impl Options {
             &matches.opt_strs("markdown-before-content"),
             &matches.opt_strs("markdown-after-content"),
             nightly_options::match_is_nightly_build(matches),
-            &diag,
+            &dcx,
             &mut id_map,
             edition,
             &None,
@@ -626,7 +626,7 @@ impl Options {
         match matches.opt_str("r").as_deref() {
             Some("rust") | None => {}
             Some(s) => {
-                diag.struct_err(format!("unknown input format: {s}")).emit();
+                dcx.struct_err(format!("unknown input format: {s}")).emit();
                 return Err(1);
             }
         }
@@ -634,19 +634,19 @@ impl Options {
         let index_page = matches.opt_str("index-page").map(|s| PathBuf::from(&s));
         if let Some(ref index_page) = index_page {
             if !index_page.is_file() {
-                diag.struct_err("option `--index-page` argument must be a file").emit();
+                dcx.struct_err("option `--index-page` argument must be a file").emit();
                 return Err(1);
             }
         }
 
-        let target = parse_target_triple(handler, matches);
+        let target = parse_target_triple(early_dcx, matches);
 
         let show_coverage = matches.opt_present("show-coverage");
 
         let crate_types = match parse_crate_types_from_list(matches.opt_strs("crate-type")) {
             Ok(types) => types,
             Err(e) => {
-                diag.struct_err(format!("unknown crate type: {e}")).emit();
+                dcx.struct_err(format!("unknown crate type: {e}")).emit();
                 return Err(1);
             }
         };
@@ -655,7 +655,7 @@ impl Options {
             Some(s) => match OutputFormat::try_from(s.as_str()) {
                 Ok(out_fmt) => {
                     if !out_fmt.is_json() && show_coverage {
-                        diag.struct_err(
+                        dcx.struct_err(
                             "html output format isn't supported for the --show-coverage option",
                         )
                         .emit();
@@ -664,7 +664,7 @@ impl Options {
                     out_fmt
                 }
                 Err(e) => {
-                    diag.struct_err(e).emit();
+                    dcx.struct_err(e).emit();
                     return Err(1);
                 }
             },
@@ -709,16 +709,16 @@ impl Options {
         let html_no_source = matches.opt_present("html-no-source");
 
         if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) {
-            diag.struct_err(
+            dcx.struct_err(
                 "--generate-link-to-definition option can only be used with HTML output format",
             )
             .emit();
             return Err(1);
         }
 
-        let scrape_examples_options = ScrapeExamplesOptions::new(matches, &diag)?;
+        let scrape_examples_options = ScrapeExamplesOptions::new(matches, &dcx)?;
         let with_examples = matches.opt_strs("with-examples");
-        let call_locations = crate::scrape_examples::load_call_locations(with_examples, &diag)?;
+        let call_locations = crate::scrape_examples::load_call_locations(with_examples, &dcx)?;
 
         let unstable_features =
             rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
@@ -803,12 +803,12 @@ impl Options {
 }
 
 /// Prints deprecation warnings for deprecated options
-fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Handler) {
+fn check_deprecated_options(matches: &getopts::Matches, dcx: &rustc_errors::DiagCtxt) {
     let deprecated_flags = [];
 
     for &flag in deprecated_flags.iter() {
         if matches.opt_present(flag) {
-            diag.struct_warn(format!("the `{flag}` flag is deprecated"))
+            dcx.struct_warn(format!("the `{flag}` flag is deprecated"))
                 .note(
                     "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
                     for more information",
@@ -821,7 +821,7 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han
 
     for &flag in removed_flags.iter() {
         if matches.opt_present(flag) {
-            let mut err = diag.struct_warn(format!("the `{flag}` flag no longer functions"));
+            let mut err = dcx.struct_warn(format!("the `{flag}` flag no longer functions"));
             err.note(
                 "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
                 for more information",
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 8215926ee33..4e904ffd768 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -120,16 +120,16 @@ impl<'tcx> DocContext<'tcx> {
     }
 }
 
-/// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
+/// Creates a new `DiagCtxt` that can be used to emit warnings and errors.
 ///
 /// If the given `error_format` is `ErrorOutputType::Json` and no `SourceMap` is given, a new one
-/// will be created for the handler.
-pub(crate) fn new_handler(
+/// will be created for the `DiagCtxt`.
+pub(crate) fn new_dcx(
     error_format: ErrorOutputType,
     source_map: Option<Lrc<source_map::SourceMap>>,
     diagnostic_width: Option<usize>,
     unstable_opts: &UnstableOptions,
-) -> rustc_errors::Handler {
+) -> rustc_errors::DiagCtxt {
     let fallback_bundle = rustc_errors::fallback_fluent_bundle(
         rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
         false,
@@ -169,8 +169,7 @@ pub(crate) fn new_handler(
         }
     };
 
-    rustc_errors::Handler::with_emitter(emitter)
-        .with_flags(unstable_opts.diagnostic_handler_flags(true))
+    rustc_errors::DiagCtxt::with_emitter(emitter).with_flags(unstable_opts.dcx_flags(true))
 }
 
 /// Parse, resolve, and typecheck the given crate.
@@ -380,13 +379,15 @@ pub(crate) fn run_global_ctxt(
             crate::lint::MISSING_CRATE_LEVEL_DOCS,
             DocContext::as_local_hir_id(tcx, krate.module.item_id).unwrap(),
             "no documentation found for this crate's top-level module",
-            |lint| lint.help(help),
+            |lint| {
+                lint.help(help);
+            },
         );
     }
 
-    fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler, sp: Span) {
+    fn report_deprecated_attr(name: &str, dcx: &rustc_errors::DiagCtxt, sp: Span) {
         let mut msg =
-            diag.struct_span_warn(sp, format!("the `#![doc({name})]` attribute is deprecated"));
+            dcx.struct_span_warn(sp, format!("the `#![doc({name})]` attribute is deprecated"));
         msg.note(
             "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
             for more information",
@@ -406,19 +407,19 @@ pub(crate) fn run_global_ctxt(
     // Process all of the crate attributes, extracting plugin metadata along
     // with the passes which we are supposed to run.
     for attr in krate.module.attrs.lists(sym::doc) {
-        let diag = ctxt.sess().diagnostic();
+        let dcx = ctxt.sess().dcx();
 
         let name = attr.name_or_empty();
         // `plugins = "..."`, `no_default_passes`, and `passes = "..."` have no effect
         if attr.is_word() && name == sym::no_default_passes {
-            report_deprecated_attr("no_default_passes", diag, attr.span());
+            report_deprecated_attr("no_default_passes", dcx, attr.span());
         } else if attr.value_str().is_some() {
             match name {
                 sym::passes => {
-                    report_deprecated_attr("passes = \"...\"", diag, attr.span());
+                    report_deprecated_attr("passes = \"...\"", dcx, attr.span());
                 }
                 sym::plugins => {
-                    report_deprecated_attr("plugins = \"...\"", diag, attr.span());
+                    report_deprecated_attr("plugins = \"...\"", dcx, attr.span());
                 }
                 _ => (),
             }
@@ -446,7 +447,7 @@ pub(crate) fn run_global_ctxt(
 
     tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc)));
 
-    if tcx.sess.diagnostic().has_errors_or_lint_errors().is_some() {
+    if tcx.sess.dcx().has_errors_or_lint_errors().is_some() {
         rustc_errors::FatalError.raise();
     }
 
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 172b7243627..c74f2ecb018 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -150,7 +150,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
 
                     collector
                 });
-                if compiler.sess.diagnostic().has_errors_or_lint_errors().is_some() {
+                if compiler.sess.dcx().has_errors_or_lint_errors().is_some() {
                     FatalError.raise();
                 }
 
@@ -558,7 +558,7 @@ pub(crate) fn make_test(
     let result = rustc_driver::catch_fatal_errors(|| {
         rustc_span::create_session_if_not_set_then(edition, |_| {
             use rustc_errors::emitter::{Emitter, EmitterWriter};
-            use rustc_errors::Handler;
+            use rustc_errors::DiagCtxt;
             use rustc_parse::parser::ForceCollect;
             use rustc_span::source_map::FilePathMapping;
 
@@ -579,8 +579,8 @@ pub(crate) fn make_test(
             let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
 
             // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
-            let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
-            let sess = ParseSess::with_span_handler(handler, sm);
+            let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
+            let sess = ParseSess::with_dcx(dcx, sm);
 
             let mut found_main = false;
             let mut found_extern_crate = crate_name.is_none();
@@ -638,10 +638,10 @@ pub(crate) fn make_test(
             }
 
             // Reset errors so that they won't be reported as compiler bugs when dropping the
-            // handler. Any errors in the tests will be reported when the test file is compiled,
+            // dcx. Any errors in the tests will be reported when the test file is compiled,
             // Note that we still need to cancel the errors above otherwise `DiagnosticBuilder`
             // will panic on drop.
-            sess.span_diagnostic.reset_err_count();
+            sess.dcx.reset_err_count();
 
             (found_main, found_extern_crate, found_macro)
         })
@@ -740,7 +740,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
     rustc_driver::catch_fatal_errors(|| {
         rustc_span::create_session_if_not_set_then(edition, |_| {
             use rustc_errors::emitter::EmitterWriter;
-            use rustc_errors::Handler;
+            use rustc_errors::DiagCtxt;
             use rustc_span::source_map::FilePathMapping;
 
             let filename = FileName::anon_source_code(source);
@@ -754,8 +754,8 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
 
             let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
 
-            let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
-            let sess = ParseSess::with_span_handler(handler, sm);
+            let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
+            let sess = ParseSess::with_dcx(dcx, sm);
             let mut parser =
                 match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) {
                     Ok(p) => p,
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index b34b69b1f15..8bc0cdf3a95 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -27,15 +27,15 @@ impl ExternalHtml {
         md_before_content: &[String],
         md_after_content: &[String],
         nightly_build: bool,
-        diag: &rustc_errors::Handler,
+        dcx: &rustc_errors::DiagCtxt,
         id_map: &mut IdMap,
         edition: Edition,
         playground: &Option<Playground>,
     ) -> Option<ExternalHtml> {
         let codes = ErrorCodes::from(nightly_build);
-        let ih = load_external_files(in_header, diag)?;
-        let bc = load_external_files(before_content, diag)?;
-        let m_bc = load_external_files(md_before_content, diag)?;
+        let ih = load_external_files(in_header, dcx)?;
+        let bc = load_external_files(before_content, dcx)?;
+        let m_bc = load_external_files(md_before_content, dcx)?;
         let bc = format!(
             "{bc}{}",
             Markdown {
@@ -51,8 +51,8 @@ impl ExternalHtml {
             }
             .into_string()
         );
-        let ac = load_external_files(after_content, diag)?;
-        let m_ac = load_external_files(md_after_content, diag)?;
+        let ac = load_external_files(after_content, dcx)?;
+        let m_ac = load_external_files(md_after_content, dcx)?;
         let ac = format!(
             "{ac}{}",
             Markdown {
@@ -79,13 +79,13 @@ pub(crate) enum LoadStringError {
 
 pub(crate) fn load_string<P: AsRef<Path>>(
     file_path: P,
-    diag: &rustc_errors::Handler,
+    dcx: &rustc_errors::DiagCtxt,
 ) -> Result<String, LoadStringError> {
     let file_path = file_path.as_ref();
     let contents = match fs::read(file_path) {
         Ok(bytes) => bytes,
         Err(e) => {
-            diag.struct_err(format!(
+            dcx.struct_err(format!(
                 "error reading `{file_path}`: {e}",
                 file_path = file_path.display()
             ))
@@ -96,16 +96,16 @@ pub(crate) fn load_string<P: AsRef<Path>>(
     match str::from_utf8(&contents) {
         Ok(s) => Ok(s.to_string()),
         Err(_) => {
-            diag.struct_err(format!("error reading `{}`: not UTF-8", file_path.display())).emit();
+            dcx.struct_err(format!("error reading `{}`: not UTF-8", file_path.display())).emit();
             Err(LoadStringError::BadUtf8)
         }
     }
 }
 
-fn load_external_files(names: &[String], diag: &rustc_errors::Handler) -> Option<String> {
+fn load_external_files(names: &[String], dcx: &rustc_errors::DiagCtxt) -> Option<String> {
     let mut out = String::new();
     for name in names {
-        let Ok(s) = load_string(name, diag) else { return None };
+        let Ok(s) = load_string(name, dcx) else { return None };
         out.push_str(&s);
         out.push('\n');
     }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index dfad1ab10db..642265f5f6b 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -835,7 +835,7 @@ impl<'tcx> ExtraInfo<'tcx> {
                 self.tcx.local_def_id_to_hir_id(def_id),
                 self.sp,
                 msg,
-                |l| l,
+                |_| {},
             );
         }
     }
@@ -843,9 +843,7 @@ impl<'tcx> ExtraInfo<'tcx> {
     fn error_invalid_codeblock_attr_with_help(
         &self,
         msg: impl Into<DiagnosticMessage>,
-        f: impl for<'a, 'b> FnOnce(
-            &'b mut DiagnosticBuilder<'a, ()>,
-        ) -> &'b mut DiagnosticBuilder<'a, ()>,
+        f: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>),
     ) {
         if let Some(def_id) = self.def_id.as_local() {
             self.tcx.struct_span_lint_hir(
@@ -1296,7 +1294,7 @@ impl LangString {
                                     lint.help(format!(
                                         "there is an attribute with a similar name: `edition{}`",
                                         &x[4..],
-                                    ))
+                                    ));
                                 },
                             );
                         }
@@ -1350,7 +1348,7 @@ impl LangString {
                                         lint.help(format!(
                                             "there is an attribute with a similar name: `{flag}`"
                                         ))
-                                        .help(help)
+                                        .help(help);
                                     },
                                 );
                             }
@@ -1383,7 +1381,7 @@ impl LangString {
         };
 
         if custom_code_classes_in_docs {
-            call(&mut TagIterator::new(string, extra).into_iter())
+            call(&mut TagIterator::new(string, extra))
         } else {
             call(&mut tokens(string))
         }
@@ -2013,6 +2011,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
     map.insert("themeStyle".into(), 1);
     map.insert("settings-menu".into(), 1);
     map.insert("help-button".into(), 1);
+    map.insert("sidebar-button".into(), 1);
     map.insert("main-content".into(), 1);
     map.insert("toggle-all-docs".into(), 1);
     map.insert("all-types".into(), 1);
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index ff7ce01e807..5ca623f01f1 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1,5 +1,3 @@
-use clean::AttributesExt;
-
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
@@ -465,16 +463,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
 
             clean::ImportItem(ref import) => {
                 let stab_tags = if let Some(import_def_id) = import.source.did {
-                    let ast_attrs = tcx.get_attrs_unchecked(import_def_id);
-                    let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs));
-
                     // Just need an item with the correct def_id and attrs
-                    let import_item = clean::Item {
-                        item_id: import_def_id.into(),
-                        attrs: import_attrs,
-                        cfg: ast_attrs.cfg(tcx, &cx.cache().hidden_cfg),
-                        ..myitem.clone()
-                    };
+                    let import_item =
+                        clean::Item { item_id: import_def_id.into(), ..myitem.clone() };
 
                     let stab_tags = Some(extra_info_tags(&import_item, item, tcx).to_string());
                     stab_tags
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 4b5d1c0d87c..6408e97df50 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -297,7 +297,10 @@ pub(super) fn write_shared(
                     .replace("\\\"", "\\\\\"")
             ));
             all_sources.sort();
-            let mut v = String::from("const srcIndex = new Map(JSON.parse('[\\\n");
+            // This needs to be `var`, not `const`.
+            // This variable needs declared in the current global scope so that if
+            // src-script.js loads first, it can pick it up.
+            let mut v = String::from("var srcIndex = new Map(JSON.parse('[\\\n");
             v.push_str(&all_sources.join(",\\\n"));
             v.push_str("\\\n]'));\ncreateSrcSidebar();\n");
             Ok(v.into_bytes())
@@ -317,13 +320,16 @@ pub(super) fn write_shared(
     // with rustdoc running in parallel.
     all_indexes.sort();
     write_invocation_specific("search-index.js", &|| {
-        let mut v = String::from("const searchIndex = new Map(JSON.parse('[\\\n");
+        // This needs to be `var`, not `const`.
+        // This variable needs declared in the current global scope so that if
+        // search.js loads first, it can pick it up.
+        let mut v = String::from("var searchIndex = new Map(JSON.parse('[\\\n");
         v.push_str(&all_indexes.join(",\\\n"));
         v.push_str(
             r#"\
 ]'));
-if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)};
-if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);
 "#,
         );
         Ok(v.into_bytes())
diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index fe0cf6dc8cc..390e812772a 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -9,7 +9,7 @@ rules.
 	margin-left: 0 !important;
 }
 
-#copy-path {
+#copy-path, #sidebar-button, .sidebar-resizer {
 	/* It requires JS to work so no need to display it in this case. */
 	display: none;
 }
@@ -132,6 +132,8 @@ nav.sub {
 	--scrape-example-help-hover-color: #000;
 	--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
 	--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+	--sidebar-resizer-hover: hsl(207, 90%, 66%);
+	--sidebar-resizer-active: hsl(207, 90%, 54%);
 }
 /* End theme: light */
 
@@ -238,6 +240,8 @@ nav.sub {
 		--scrape-example-help-hover-color: #fff;
 		--scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
 		--scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+		--sidebar-resizer-hover: hsl(207, 30%, 54%);
+		--sidebar-resizer-active: hsl(207, 90%, 54%);
 	}
 /* End theme: dark */
 }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 6e61969a8c1..6e673aa77c5 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -9,6 +9,11 @@
 :root {
 	--nav-sub-mobile-padding: 8px;
 	--search-typename-width: 6.75rem;
+	/* DEFAULT_SIDEBAR_WIDTH
+	   see main.js for information on these values
+	   and on the RUSTDOC_MOBILE_BREAKPOINT */
+	--desktop-sidebar-width: 200px;
+	--src-sidebar-width: 300px;
 }
 
 /* See FiraSans-LICENSE.txt for the Fira Sans license. */
@@ -383,13 +388,15 @@ img {
 
 .sidebar {
 	font-size: 0.875rem;
-	flex: 0 0 200px;
+	flex: 0 0 var(--desktop-sidebar-width);
+	width: var(--desktop-sidebar-width);
 	overflow-y: scroll;
 	overscroll-behavior: contain;
 	position: sticky;
 	height: 100vh;
 	top: 0;
 	left: 0;
+	z-index: 100;
 }
 
 .rustdoc.src .sidebar {
@@ -398,7 +405,95 @@ img {
 	overflow-x: hidden;
 	/* The sidebar is by default hidden  */
 	overflow-y: hidden;
-	z-index: 1;
+}
+
+.hide-sidebar .sidebar,
+.hide-sidebar .sidebar-resizer {
+	display: none;
+}
+
+.sidebar-resizer {
+	touch-action: none;
+	width: 9px;
+	cursor: col-resize;
+	z-index: 200;
+	position: fixed;
+	height: 100%;
+	/* make sure there's a 1px gap between the scrollbar and resize handle */
+	left: calc(var(--desktop-sidebar-width) + 1px);
+}
+
+.rustdoc.src .sidebar-resizer {
+	/* when closed, place resizer glow on top of the normal src sidebar border (no need to worry
+	   about sidebar) */
+	left: 49px;
+}
+
+.src-sidebar-expanded .rustdoc.src .sidebar-resizer {
+	/* for src sidebar, gap is already provided by 1px border on sidebar itself, so place resizer
+	   to right of it */
+	left: var(--src-sidebar-width);
+}
+
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+.sidebar-resizing * {
+	cursor: col-resize !important;
+}
+
+.sidebar-resizing .sidebar {
+	position: fixed;
+	z-index: 100;
+}
+.sidebar-resizing > body {
+	padding-left: var(--resizing-sidebar-width);
+}
+
+.sidebar-resizer:hover,
+.sidebar-resizer:active,
+.sidebar-resizer:focus,
+.sidebar-resizer.active {
+	width: 10px;
+	margin: 0;
+	/* when active or hovered, place resizer glow on top of the sidebar (right next to, or even
+	   on top of, the scrollbar) */
+	left: var(--desktop-sidebar-width);
+	border-left: solid 1px var(--sidebar-resizer-hover);
+}
+
+.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,
+.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,
+.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,
+.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active {
+	/* when active or hovered, place resizer glow on top of the normal src sidebar border */
+	left: calc(var(--src-sidebar-width) - 1px);
+}
+
+@media (pointer: coarse) {
+	.sidebar-resizer {
+		/* too easy to hit the resizer while trying to hit the [-] toggle */
+		display: none !important;
+	}
+}
+
+.sidebar-resizer.active {
+	/* make the resize tool bigger when actually resizing, to avoid :hover styles on other stuff
+		while resizing */
+	padding: 0 140px;
+	width: 2px;
+	margin-left: -140px;
+	border-left: none;
+}
+.sidebar-resizer.active:before {
+	border-left: solid 2px var(--sidebar-resizer-active);
+	display: block;
+	height: 100%;
+	content: "";
 }
 
 .sidebar, .mobile-topbar, .sidebar-menu-toggle,
@@ -416,7 +511,8 @@ img {
 
 .src-sidebar-expanded .src .sidebar {
 	overflow-y: auto;
-	flex-basis: 300px;
+	flex-basis: var(--src-sidebar-width);
+	width: var(--src-sidebar-width);
 }
 
 .src-sidebar-expanded .src .sidebar > *:not(#src-sidebar-toggle) {
@@ -477,6 +573,7 @@ ul.block, .block li {
 	display: block;
 	padding: 0.25rem; /* 4px */
 	margin-left: -0.25rem;
+	margin-right: 0.25rem;
 }
 
 .sidebar h2 {
@@ -775,7 +872,7 @@ h2.section-header > .anchor {
 	text-decoration: underline;
 }
 
-.crate.block a.current { font-weight: 500; }
+.crate.block li.current a { font-weight: 500; }
 
 /*  In most contexts we use `overflow-wrap: anywhere` to ensure that we can wrap
 	as much as needed on mobile (see
@@ -1478,7 +1575,20 @@ a.tooltip:hover::after {
 	margin-left: 4px;
 	display: flex;
 }
-#settings-menu > a, #help-button > a {
+#sidebar-button {
+	display: none;
+}
+.hide-sidebar #sidebar-button {
+	display: flex;
+	margin-right: 4px;
+	position: fixed;
+	left: 6px;
+	height: 34px;
+	width: 34px;
+	background-color: var(--main-background-color);
+	z-index: 1;
+}
+#settings-menu > a, #help-button > a, #sidebar-button > a {
 	display: flex;
 	align-items: center;
 	justify-content: center;
@@ -1493,10 +1603,21 @@ a.tooltip:hover::after {
 }
 
 #settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > a:hover, #help-button > a:focus {
+#help-button > a:hover, #help-button > a:focus,
+#sidebar-button > a:hover, #sidebar-button > a:focus {
 	border-color: var(--settings-button-border-focus);
 }
 
+#sidebar-button > a:before {
+	content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" \
+		fill="none" stroke="black">\
+		<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5"/>\
+		<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\
+		<path d="m7.6121 3v16 M5.375 7.625h-2 m2 3h-2 m2 3h-2" stroke-width="1.25"/></svg>');
+	width: 22px;
+	height: 22px;
+}
+
 #copy-path {
 	color: var(--copy-path-button-color);
 	background: var(--main-background-color);
@@ -1711,7 +1832,7 @@ However, it's not needed with smaller screen width because the doc/code block is
 /*
 WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
 If you update this line, then you also need to update the line with the same warning
-in src-script.js
+in src-script.js and main.js
 */
 @media (max-width: 700px) {
 	/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar,
@@ -1722,6 +1843,10 @@ in src-script.js
 		scroll-margin-top: 45px;
 	}
 
+	.hide-sidebar #sidebar-button {
+		position: static;
+	}
+
 	.rustdoc {
 		/* Sidebar should overlay main content, rather than pushing main content to the right.
 		   Turn off `display: flex` on the body element. */
@@ -1750,7 +1875,8 @@ in src-script.js
 	/* Hide the logo and item name from the sidebar. Those are displayed
 	   in the mobile-topbar instead. */
 	.sidebar .logo-container,
-	.sidebar .location {
+	.sidebar .location,
+	.sidebar-resizer {
 		display: none;
 	}
 
@@ -1818,6 +1944,10 @@ in src-script.js
 		top: 0;
 	}
 
+	.hide-sidebar .mobile-topbar {
+		display: none;
+	}
+
 	.sidebar-menu-toggle {
 		width: 45px;
 		/* Rare exception to specifying font sizes in rem. Since this is acting
@@ -1827,6 +1957,10 @@ in src-script.js
 		color: var(--main-color);
 	}
 
+	.hide-sidebar .sidebar-menu-toggle {
+		display: none;
+	}
+
 	.sidebar-elems {
 		margin-top: 1em;
 	}
@@ -1870,6 +2004,17 @@ in src-script.js
 		display: none;
 	}
 
+	/* sidebar button becomes topbar button */
+	#sidebar-button > a:before {
+		content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
+			viewBox="0 0 22 22" fill="none" stroke="black">\
+			<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5"/>\
+			<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\
+			<path d="m3 7.375h16m0-3h-4" stroke-width="1.25"/></svg>');
+		width: 22px;
+		height: 22px;
+	}
+
 	/* Display an alternating layout on tablets and phones */
 	.item-table, .item-row, .item-table > li, .item-table > li > div,
 	.search-results > a, .search-results > a > div {
@@ -2274,6 +2419,8 @@ in src-script.js
 	--scrape-example-help-hover-color: #000;
 	--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
 	--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+	--sidebar-resizer-hover: hsl(207, 90%, 66%);
+	--sidebar-resizer-active: hsl(207, 90%, 54%);
 }
 /* End theme: light */
 
@@ -2379,6 +2526,8 @@ in src-script.js
 	--scrape-example-help-hover-color: #fff;
 	--scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
 	--scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+	--sidebar-resizer-hover: hsl(207, 30%, 54%);
+	--sidebar-resizer-active: hsl(207, 90%, 54%);
 }
 /* End theme: dark */
 
@@ -2488,6 +2637,8 @@ Original by Dempfi (https://github.com/dempfi/ayu)
 	--scrape-example-help-hover-color: #fff;
 	--scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
 	--scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
+	--sidebar-resizer-hover: hsl(34, 50%, 33%);
+	--sidebar-resizer-active: hsl(34, 100%, 66%);
 }
 
 :root[data-theme="ayu"] h1,
@@ -2519,6 +2670,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
 }
 
 :root[data-theme="ayu"] .sidebar .current,
+:root[data-theme="ayu"] .sidebar .current a,
 :root[data-theme="ayu"] .sidebar a:hover,
 :root[data-theme="ayu"] #src-sidebar div.files > a:hover,
 :root[data-theme="ayu"] details.dir-entry summary:hover,
@@ -2569,7 +2721,8 @@ Original by Dempfi (https://github.com/dempfi/ayu)
 	border-bottom: 1px solid rgba(242, 151, 24, 0.3);
 }
 
-:root[data-theme="ayu"] #settings-menu > a img {
+:root[data-theme="ayu"] #settings-menu > a img,
+:root[data-theme="ayu"] #sidebar-button > a:before {
 	filter: invert(100);
 }
 /* End theme: ayu */
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index d613997cd7f..63ab56053af 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1,5 +1,5 @@
 // Local js definitions:
-/* global addClass, getSettingValue, hasClass, searchState */
+/* global addClass, getSettingValue, hasClass, searchState, updateLocalStorage */
 /* global onEach, onEachLazy, removeClass, getVar */
 
 "use strict";
@@ -495,7 +495,7 @@ function preLoadCss(cssUrl) {
                 }
                 const link = document.createElement("a");
                 link.href = path;
-                if (link.href === current_page) {
+                if (path === current_page) {
                     link.className = "current";
                 }
                 link.textContent = name;
@@ -857,12 +857,12 @@ function preLoadCss(cssUrl) {
         for (const crate of window.ALL_CRATES) {
             const link = document.createElement("a");
             link.href = window.rootPath + crate + "/index.html";
-            if (window.rootPath !== "./" && crate === window.currentCrate) {
-                link.className = "current";
-            }
             link.textContent = crate;
 
             const li = document.createElement("li");
+            if (window.rootPath !== "./" && crate === window.currentCrate) {
+                li.className = "current";
+            }
             li.appendChild(link);
             ul.appendChild(li);
         }
@@ -1473,6 +1473,264 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
     searchState.setup();
 }());
 
+// Hide, show, and resize the sidebar
+//
+// The body class and CSS variable are initially set up in storage.js,
+// but in this file, we implement:
+//
+//   * the show sidebar button, which appears if the sidebar is hidden
+//     and, by clicking on it, will bring it back
+//   * the sidebar resize handle, which appears only on large viewports
+//     with a [fine precision pointer] to allow the user to change
+//     the size of the sidebar
+//
+// [fine precision pointer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer
+(function() {
+    // 100 is the size of the logo
+    // don't let the sidebar get smaller than that, or it'll get squished
+    const SIDEBAR_MIN = 100;
+    // Don't let the sidebar get bigger than this
+    const SIDEBAR_MAX = 500;
+    // Don't let the body (including the gutter) get smaller than this
+    //
+    // WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
+    // Acceptable values for BODY_MIN are constrained by the mobile breakpoint
+    // (which is the minimum size of the whole page where the sidebar exists)
+    // and the default sidebar width:
+    //
+    //     BODY_MIN <= RUSTDOC_MOBILE_BREAKPOINT - DEFAULT_SIDEBAR_WIDTH
+    //
+    // At the time of this writing, the DEFAULT_SIDEBAR_WIDTH on src pages is
+    // 300px, and the RUSTDOC_MOBILE_BREAKPOINT is 700px, so BODY_MIN must be
+    // at most 400px. Otherwise, it would start out at the default size, then
+    // grabbing the resize handle would suddenly cause it to jank to
+    // its contraint-generated maximum.
+    const RUSTDOC_MOBILE_BREAKPOINT = 700;
+    const BODY_MIN = 400;
+    // At half-way past the minimum size, vanish the sidebar entirely
+    const SIDEBAR_VANISH_THRESHOLD = SIDEBAR_MIN / 2;
+
+    // Toolbar button to show the sidebar.
+    //
+    // On small, "mobile-sized" viewports, it's not persistent and it
+    // can only be activated by going into Settings and hiding the nav bar.
+    // On larger, "desktop-sized" viewports (though that includes many
+    // tablets), it's fixed-position, appears in the left side margin,
+    // and it can be activated by resizing the sidebar into nothing.
+    const sidebarButton = document.getElementById("sidebar-button");
+    if (sidebarButton) {
+        sidebarButton.addEventListener("click", e => {
+            removeClass(document.documentElement, "hide-sidebar");
+            updateLocalStorage("hide-sidebar", "false");
+            e.preventDefault();
+        });
+    }
+
+    // Pointer capture.
+    //
+    // Resizing is a single-pointer gesture. Any secondary pointer is ignored
+    let currentPointerId = null;
+
+    // "Desired" sidebar size.
+    //
+    // This is stashed here for window resizing. If the sidebar gets
+    // shrunk to maintain BODY_MIN, and then the user grows the window again,
+    // it gets the sidebar to restore its size.
+    let desiredSidebarSize = null;
+
+    // Sidebar resize debouncer.
+    //
+    // The sidebar itself is resized instantly, but the body HTML can be too
+    // big for that, causing reflow jank. To reduce this, we queue up a separate
+    // animation frame and throttle it.
+    let pendingSidebarResizingFrame = false;
+
+    // If this page has no sidebar at all, bail out.
+    const resizer = document.querySelector(".sidebar-resizer");
+    const sidebar = document.querySelector(".sidebar");
+    if (!resizer || !sidebar) {
+        return;
+    }
+
+    // src page and docs page use different variables, because the contents of
+    // the sidebar are so different that it's reasonable to thing the user
+    // would want them to have different sizes
+    const isSrcPage = hasClass(document.body, "src");
+
+    // Call this function to hide the sidebar when using the resize handle
+    //
+    // This function also nulls out the sidebar width CSS variable and setting,
+    // causing it to return to its default. This does not happen if you do it
+    // from settings.js, which uses a separate function. It's done here because
+    // the minimum sidebar size is rather uncomfortable, and it must pass
+    // through that size when using the shrink-to-nothing gesture.
+    function hideSidebar() {
+        if (isSrcPage) {
+            window.rustdocCloseSourceSidebar();
+            updateLocalStorage("src-sidebar-width", null);
+            // [RUSTDOCIMPL] CSS variable fast path
+            //
+            // The sidebar width variable is attached to the <html> element by
+            // storage.js, because the sidebar and resizer don't exist yet.
+            // But the resize code, in `resize()`, sets the property on the
+            // sidebar and resizer elements (which are the only elements that
+            // use the variable) to avoid recalculating CSS on the entire
+            // document on every frame.
+            //
+            // So, to clear it, we need to clear all three.
+            document.documentElement.style.removeProperty("--src-sidebar-width");
+            sidebar.style.removeProperty("--src-sidebar-width");
+            resizer.style.removeProperty("--src-sidebar-width");
+        } else {
+            addClass(document.documentElement, "hide-sidebar");
+            updateLocalStorage("hide-sidebar", "true");
+            updateLocalStorage("desktop-sidebar-width", null);
+            document.documentElement.style.removeProperty("--desktop-sidebar-width");
+            sidebar.style.removeProperty("--desktop-sidebar-width");
+            resizer.style.removeProperty("--desktop-sidebar-width");
+        }
+    }
+
+    // Call this function to show the sidebar from the resize handle.
+    // On docs pages, this can only happen if the user has grabbed the resize
+    // handle, shrunk the sidebar down to nothing, and then pulls back into
+    // the visible range without releasing it. You can, however, grab the
+    // resize handle on a source page with the sidebar closed, because it
+    // remains visible all the time on there.
+    function showSidebar() {
+        if (isSrcPage) {
+            window.rustdocShowSourceSidebar();
+        } else {
+            removeClass(document.documentElement, "hide-sidebar");
+            updateLocalStorage("hide-sidebar", "false");
+        }
+    }
+
+    /**
+     * Call this to set the correct CSS variable and setting.
+     * This function doesn't enforce size constraints. Do that before calling it!
+     *
+     * @param {number} size - CSS px width of the sidebar.
+     */
+    function changeSidebarSize(size) {
+        if (isSrcPage) {
+            updateLocalStorage("src-sidebar-width", size);
+            // [RUSTDOCIMPL] CSS variable fast path
+            //
+            // While this property is set on the HTML element at load time,
+            // because the sidebar isn't actually loaded yet,
+            // we scope this update to the sidebar to avoid hitting a slow
+            // path in WebKit.
+            sidebar.style.setProperty("--src-sidebar-width", size + "px");
+            resizer.style.setProperty("--src-sidebar-width", size + "px");
+        } else {
+            updateLocalStorage("desktop-sidebar-width", size);
+            sidebar.style.setProperty("--desktop-sidebar-width", size + "px");
+            resizer.style.setProperty("--desktop-sidebar-width", size + "px");
+        }
+    }
+
+    // Check if the sidebar is hidden. Since src pages and doc pages have
+    // different settings, this function has to check that.
+    function isSidebarHidden() {
+        return isSrcPage ?
+            !hasClass(document.documentElement, "src-sidebar-expanded") :
+            hasClass(document.documentElement, "hide-sidebar");
+    }
+
+    // Respond to the resize handle event.
+    // This function enforces size constraints, and implements the
+    // shrink-to-nothing gesture based on thresholds defined above.
+    function resize(e) {
+        if (currentPointerId === null || currentPointerId !== e.pointerId) {
+            return;
+        }
+        e.preventDefault();
+        const pos = e.clientX - sidebar.offsetLeft - 3;
+        if (pos < SIDEBAR_VANISH_THRESHOLD) {
+            hideSidebar();
+        } else if (pos >= SIDEBAR_MIN) {
+            if (isSidebarHidden()) {
+                showSidebar();
+            }
+            // don't let the sidebar get wider than SIDEBAR_MAX, or the body narrower
+            // than BODY_MIN
+            const constrainedPos = Math.min(pos, window.innerWidth - BODY_MIN, SIDEBAR_MAX);
+            changeSidebarSize(constrainedPos);
+            desiredSidebarSize = constrainedPos;
+            if (pendingSidebarResizingFrame !== false) {
+                clearTimeout(pendingSidebarResizingFrame);
+            }
+            pendingSidebarResizingFrame = setTimeout(() => {
+                if (currentPointerId === null || pendingSidebarResizingFrame === false) {
+                    return;
+                }
+                pendingSidebarResizingFrame = false;
+                document.documentElement.style.setProperty(
+                    "--resizing-sidebar-width",
+                    desiredSidebarSize + "px"
+                );
+            }, 100);
+        }
+    }
+    // Respond to the window resize event.
+    window.addEventListener("resize", () => {
+        if (window.innerWidth < RUSTDOC_MOBILE_BREAKPOINT) {
+            return;
+        }
+        stopResize();
+        if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) {
+            changeSidebarSize(window.innerWidth - BODY_MIN);
+        } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) {
+            changeSidebarSize(desiredSidebarSize);
+        }
+    });
+    function stopResize(e) {
+        if (currentPointerId === null) {
+            return;
+        }
+        if (e) {
+            e.preventDefault();
+        }
+        desiredSidebarSize = sidebar.getBoundingClientRect().width;
+        removeClass(resizer, "active");
+        window.removeEventListener("pointermove", resize, false);
+        window.removeEventListener("pointerup", stopResize, false);
+        removeClass(document.documentElement, "sidebar-resizing");
+        document.documentElement.style.removeProperty( "--resizing-sidebar-width");
+        if (resizer.releasePointerCapture) {
+            resizer.releasePointerCapture(currentPointerId);
+            currentPointerId = null;
+        }
+    }
+    function initResize(e) {
+        if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) {
+            return;
+        }
+        if (resizer.setPointerCapture) {
+            resizer.setPointerCapture(e.pointerId);
+            if (!resizer.hasPointerCapture(e.pointerId)) {
+                // unable to capture pointer; something else has it
+                // on iOS, this usually means you long-clicked a link instead
+                resizer.releasePointerCapture(e.pointerId);
+                return;
+            }
+            currentPointerId = e.pointerId;
+        }
+        e.preventDefault();
+        window.addEventListener("pointermove", resize, false);
+        window.addEventListener("pointercancel", stopResize, false);
+        window.addEventListener("pointerup", stopResize, false);
+        addClass(resizer, "active");
+        addClass(document.documentElement, "sidebar-resizing");
+        const pos = e.clientX - sidebar.offsetLeft - 3;
+        document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px");
+        desiredSidebarSize = null;
+    }
+    resizer.addEventListener("pointerdown", initResize, false);
+}());
+
+// This section handles the copy button that appears next to the path breadcrumbs
 (function() {
     let reset_button_timeout = null;
 
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index ccb54e14a5c..e824a1fd4bd 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -499,6 +499,7 @@ function initSearch(rawSearchIndex) {
                 fullPath: ["never"],
                 pathWithoutLast: [],
                 pathLast: "never",
+                normalizedPathLast: "never",
                 generics: [],
                 bindings: new Map(),
                 typeFilter: "primitive",
@@ -537,12 +538,14 @@ function initSearch(rawSearchIndex) {
         const bindingName = parserState.isInBinding;
         parserState.isInBinding = null;
         const bindings = new Map();
+        const pathLast = pathSegments[pathSegments.length - 1];
         return {
             name: name.trim(),
             id: null,
             fullPath: pathSegments,
             pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
-            pathLast: pathSegments[pathSegments.length - 1],
+            pathLast,
+            normalizedPathLast: pathLast.replace(/_/g, ""),
             generics: generics.filter(gen => {
                 // Syntactically, bindings are parsed as generics,
                 // but the query engine treats them differently.
@@ -689,6 +692,7 @@ function initSearch(rawSearchIndex) {
                 fullPath: ["[]"],
                 pathWithoutLast: [],
                 pathLast: "[]",
+                normalizedPathLast: "[]",
                 generics,
                 typeFilter: "primitive",
                 bindingName: isInBinding,
@@ -1168,13 +1172,12 @@ function initSearch(rawSearchIndex) {
      * Executes the parsed query and builds a {ResultsTable}.
      *
      * @param  {ParsedQuery} parsedQuery - The parsed user query
-     * @param  {Object} searchWords      - The list of search words to query against
      * @param  {Object} [filterCrates]   - Crate to search in if defined
      * @param  {Object} [currentCrate]   - Current crate, to rank results from this crate higher
      *
      * @return {ResultsTable}
      */
-    function execQuery(parsedQuery, searchWords, filterCrates, currentCrate) {
+    function execQuery(parsedQuery, filterCrates, currentCrate) {
         const results_others = new Map(), results_in_args = new Map(),
             results_returned = new Map();
 
@@ -1232,8 +1235,8 @@ function initSearch(rawSearchIndex) {
             const userQuery = parsedQuery.userQuery;
             const result_list = [];
             for (const result of results.values()) {
-                result.word = searchWords[result.id];
-                result.item = searchIndex[result.id] || {};
+                result.item = searchIndex[result.id];
+                result.word = searchIndex[result.id].word;
                 result_list.push(result);
             }
 
@@ -1928,7 +1931,7 @@ function initSearch(rawSearchIndex) {
          * The `results` map contains information which will be used to sort the search results:
          *
          * * `fullId` is a `string`` used as the key of the object we use for the `results` map.
-         * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
+         * * `id` is the index in the `searchIndex` array for this element.
          * * `index` is an `integer`` used to sort by the position of the word in the item's name.
          * * `dist` is the main metric used to sort the search results.
          * * `path_dist` is zero if a single-component search query is used, otherwise it's the
@@ -1986,9 +1989,8 @@ function initSearch(rawSearchIndex) {
             if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
                 return;
             }
-            let index = -1, path_dist = 0;
+            let path_dist = 0;
             const fullId = row.id;
-            const searchWord = searchWords[pos];
 
             // fpDist is a minimum possible type distance, where "type distance" is the number of
             // atoms in the function not present in the query
@@ -2021,19 +2023,10 @@ function initSearch(rawSearchIndex) {
                 return;
             }
 
-            const row_index = row.normalizedName.indexOf(elem.pathLast);
-            const word_index = searchWord.indexOf(elem.pathLast);
-
-            // lower indexes are "better" matches
-            // rank based on the "best" match
-            if (row_index === -1) {
-                index = word_index;
-            } else if (word_index === -1) {
-                index = row_index;
-            } else if (word_index < row_index) {
-                index = word_index;
-            } else {
-                index = row_index;
+            let index = row.word.indexOf(elem.pathLast);
+            const normalizedIndex = row.normalizedName.indexOf(elem.pathLast);
+            if (index === -1 || (index > normalizedIndex && normalizedIndex !== -1)) {
+                index = normalizedIndex;
             }
 
             if (elem.fullPath.length > 1) {
@@ -2044,13 +2037,13 @@ function initSearch(rawSearchIndex) {
             }
 
             if (parsedQuery.literalSearch) {
-                if (searchWord === elem.name) {
+                if (row.word === elem.pathLast) {
                     addIntoResults(results_others, fullId, pos, index, 0, path_dist);
                 }
                 return;
             }
 
-            const dist = editDistance(searchWord, elem.pathLast, maxEditDistance);
+            const dist = editDistance(row.normalizedName, elem.normalizedPathLast, maxEditDistance);
 
             if (index === -1 && dist + path_dist > maxEditDistance) {
                 return;
@@ -2135,15 +2128,15 @@ function initSearch(rawSearchIndex) {
              * @param {boolean} isAssocType
              */
             function convertNameToId(elem, isAssocType) {
-                if (typeNameIdMap.has(elem.pathLast) &&
-                    (isAssocType || !typeNameIdMap.get(elem.pathLast).assocOnly)) {
-                    elem.id = typeNameIdMap.get(elem.pathLast).id;
+                if (typeNameIdMap.has(elem.normalizedPathLast) &&
+                    (isAssocType || !typeNameIdMap.get(elem.normalizedPathLast).assocOnly)) {
+                    elem.id = typeNameIdMap.get(elem.normalizedPathLast).id;
                 } else if (!parsedQuery.literalSearch) {
                     let match = null;
                     let matchDist = maxEditDistance + 1;
                     let matchName = "";
                     for (const [name, {id, assocOnly}] of typeNameIdMap) {
-                        const dist = editDistance(name, elem.pathLast, maxEditDistance);
+                        const dist = editDistance(name, elem.normalizedPathLast, maxEditDistance);
                         if (dist <= matchDist && dist <= maxEditDistance &&
                             (isAssocType || !assocOnly)) {
                             if (dist === matchDist && matchName > name) {
@@ -2236,7 +2229,7 @@ function initSearch(rawSearchIndex) {
             if (parsedQuery.foundElems === 1 && parsedQuery.returned.length === 0) {
                 if (parsedQuery.elems.length === 1) {
                     const elem = parsedQuery.elems[0];
-                    for (let i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                    for (let i = 0, nSearchIndex = searchIndex.length; i < nSearchIndex; ++i) {
                         // It means we want to check for this element everywhere (in names, args and
                         // returned).
                         handleSingleArg(
@@ -2267,7 +2260,7 @@ function initSearch(rawSearchIndex) {
                 };
                 parsedQuery.elems.sort(sortQ);
                 parsedQuery.returned.sort(sortQ);
-                for (let i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
+                for (let i = 0, nSearchIndex = searchIndex.length; i < nSearchIndex; ++i) {
                     handleArgs(searchIndex[i], i, results_others);
                 }
             }
@@ -2651,7 +2644,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         updateSearchHistory(buildUrl(query.original, filterCrates));
 
         showResults(
-            execQuery(query, searchWords, filterCrates, window.currentCrate),
+            execQuery(query, filterCrates, window.currentCrate),
             params.go_to_first,
             filterCrates);
     }
@@ -2920,12 +2913,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
 
     function buildIndex(rawSearchIndex) {
         searchIndex = [];
-        /**
-         * List of normalized search words (ASCII lowercased, and undescores removed).
-         *
-         * @type {Array<string>}
-         */
-        const searchWords = [];
         typeNameIdMap = new Map();
         const charA = "A".charCodeAt(0);
         let currentIndex = 0;
@@ -3004,7 +2991,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
          * }}
          */
         for (const [crate, crateCorpus] of rawSearchIndex) {
-            searchWords.push(crate);
             // This object should have exactly the same set of fields as the "row"
             // object defined below. Your JavaScript runtime will thank you.
             // https://mathiasbynens.be/notes/shapes-ics
@@ -3017,6 +3003,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 parent: undefined,
                 type: null,
                 id: id,
+                word: crate,
                 normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
                 deprecated: null,
                 implDisambiguator: null,
@@ -3084,12 +3071,9 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             len = itemTypes.length;
             for (let i = 0; i < len; ++i) {
                 let word = "";
-                // This object should have exactly the same set of fields as the "crateRow"
-                // object defined above.
                 if (typeof itemNames[i] === "string") {
                     word = itemNames[i].toLowerCase();
                 }
-                searchWords.push(word);
                 const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
                 let type = null;
                 if (itemFunctionSearchTypes[i] !== 0) {
@@ -3113,6 +3097,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                         }
                     }
                 }
+                // This object should have exactly the same set of fields as the "crateRow"
+                // object defined above.
                 const row = {
                     crate: crate,
                     ty: itemTypes.charCodeAt(i) - charA,
@@ -3122,6 +3108,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type,
                     id: id,
+                    word,
                     normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
                     deprecated: deprecatedItems.has(i),
                     implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null,
@@ -3153,7 +3140,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             }
             currentIndex += itemTypes.length;
         }
-        return searchWords;
     }
 
     /**
@@ -3332,10 +3318,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         search(true);
     }
 
-    /**
-     *  @type {Array<string>}
-     */
-    const searchWords = buildIndex(rawSearchIndex);
+    buildIndex(rawSearchIndex);
     if (typeof window !== "undefined") {
         registerSearchEvents();
         // If there's a search term in the URL, execute the search now.
@@ -3349,7 +3332,6 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         exports.execQuery = execQuery;
         exports.parseQuery = parseQuery;
     }
-    return searchWords;
 }
 
 if (typeof window !== "undefined") {
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 70a2825265e..2b42fbebb80 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -29,6 +29,13 @@
                     window.rustdoc_remove_line_numbers_from_examples();
                 }
                 break;
+            case "hide-sidebar":
+                if (value === true) {
+                    addClass(document.documentElement, "hide-sidebar");
+                } else {
+                    removeClass(document.documentElement, "hide-sidebar");
+                }
+                break;
         }
     }
 
@@ -187,6 +194,11 @@
                 "default": false,
             },
             {
+                "name": "Hide persistent navigation bar",
+                "js_name": "hide-sidebar",
+                "default": false,
+            },
+            {
                 "name": "Disable keyboard shortcuts",
                 "js_name": "disable-shortcuts",
                 "default": false,
@@ -216,6 +228,13 @@
 
     function displaySettings() {
         settingsMenu.style.display = "";
+        onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"), el => {
+            const val = getSettingValue(el.id);
+            const checked = val === "true";
+            if (checked !== el.checked && val !== null) {
+                el.checked = checked;
+            }
+        });
     }
 
     function settingsBlurHandler(event) {
diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js
index 27b5cf1e2ae..fc1d2d37845 100644
--- a/src/librustdoc/html/static/js/src-script.js
+++ b/src/librustdoc/html/static/js/src-script.js
@@ -71,16 +71,31 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) {
     return hasFoundFile;
 }
 
+let toggleLabel;
+
+function getToggleLabel() {
+    toggleLabel = toggleLabel || document.querySelector("#src-sidebar-toggle button");
+    return toggleLabel;
+}
+
+window.rustdocCloseSourceSidebar = () => {
+    removeClass(document.documentElement, "src-sidebar-expanded");
+    getToggleLabel().innerText = ">";
+    updateLocalStorage("source-sidebar-show", "false");
+};
+
+window.rustdocShowSourceSidebar = () => {
+    addClass(document.documentElement, "src-sidebar-expanded");
+    getToggleLabel().innerText = "<";
+    updateLocalStorage("source-sidebar-show", "true");
+};
+
 function toggleSidebar() {
     const child = this.parentNode.children[0];
     if (child.innerText === ">") {
-        addClass(document.documentElement, "src-sidebar-expanded");
-        child.innerText = "<";
-        updateLocalStorage("source-sidebar-show", "true");
+        window.rustdocShowSourceSidebar();
     } else {
-        removeClass(document.documentElement, "src-sidebar-expanded");
-        child.innerText = ">";
-        updateLocalStorage("source-sidebar-show", "false");
+        window.rustdocCloseSourceSidebar();
     }
 }
 
@@ -131,12 +146,8 @@ function createSrcSidebar() {
     }
 }
 
-const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
-
-function highlightSrcLines(match) {
-    if (typeof match === "undefined") {
-        match = window.location.hash.match(lineNumbersRegex);
-    }
+function highlightSrcLines() {
+    const match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
     if (!match) {
         return;
     }
@@ -218,12 +229,7 @@ const handleSrcHighlight = (function() {
     };
 }());
 
-window.addEventListener("hashchange", () => {
-    const match = window.location.hash.match(lineNumbersRegex);
-    if (match) {
-        return highlightSrcLines(match);
-    }
-});
+window.addEventListener("hashchange", highlightSrcLines);
 
 onEachLazy(document.getElementsByClassName("src-line-numbers"), el => {
     el.addEventListener("click", handleSrcHighlight);
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 37250ba5a1f..ac9c6f377b8 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -183,11 +183,38 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
 
 updateTheme();
 
+// Hide, show, and resize the sidebar at page load time
+//
+// This needs to be done here because this JS is render-blocking,
+// so that the sidebar doesn't "jump" after appearing on screen.
+// The user interaction to change this is set up in main.js.
 if (getSettingValue("source-sidebar-show") === "true") {
     // At this point in page load, `document.body` is not available yet.
     // Set a class on the `<html>` element instead.
     addClass(document.documentElement, "src-sidebar-expanded");
 }
+if (getSettingValue("hide-sidebar") === "true") {
+    // At this point in page load, `document.body` is not available yet.
+    // Set a class on the `<html>` element instead.
+    addClass(document.documentElement, "hide-sidebar");
+}
+function updateSidebarWidth() {
+    const desktopSidebarWidth = getSettingValue("desktop-sidebar-width");
+    if (desktopSidebarWidth && desktopSidebarWidth !== "null") {
+        document.documentElement.style.setProperty(
+            "--desktop-sidebar-width",
+            desktopSidebarWidth + "px"
+        );
+    }
+    const srcSidebarWidth = getSettingValue("src-sidebar-width");
+    if (srcSidebarWidth && srcSidebarWidth !== "null") {
+        document.documentElement.style.setProperty(
+            "--src-sidebar-width",
+            srcSidebarWidth + "px"
+        );
+    }
+}
+updateSidebarWidth();
 
 // If we navigate away (for example to a settings page), and then use the back or
 // forward button to get back to a page, the theme may have changed in the meantime.
@@ -201,5 +228,6 @@ if (getSettingValue("source-sidebar-show") === "true") {
 window.addEventListener("pageshow", ev => {
     if (ev.persisted) {
         setTimeout(updateTheme, 0);
+        setTimeout(updateSidebarWidth, 0);
     }
 });
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 3f6147bb916..60ca5660c02 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -114,6 +114,7 @@
         {% endif %}
         {{ sidebar|safe }}
     </nav> {# #}
+    <div class="sidebar-resizer"></div>
     <main> {# #}
         {% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
             <nav class="sub"> {# #}
@@ -128,6 +129,11 @@
                 {% endif %}
                 <form class="search-form"> {# #}
                     <span></span> {# This empty span is a hacky fix for Safari - See #93184 #}
+                    {% if page.css_class != "src" %}
+                    <div id="sidebar-button" tabindex="-1"> {# #}
+                        <a href="{{page.root_path|safe}}{{layout.krate|safe}}/all.html" title="show sidebar"></a> {# #}
+                    </div> {# #}
+                    {% endif %}
                     <input {#+ #}
                         class="search-input" {#+ #}
                         name="search" {#+ #}
@@ -136,8 +142,8 @@
                         spellcheck="false" {#+ #}
                         placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {#+ #}
                         type="search"> {# #}
-                    <div id="help-button" title="help" tabindex="-1"> {# #}
-                        <a href="{{page.root_path|safe}}help.html">?</a> {# #}
+                    <div id="help-button" tabindex="-1"> {# #}
+                        <a href="{{page.root_path|safe}}help.html" title="help">?</a> {# #}
                     </div> {# #}
                     <div id="settings-menu" tabindex="-1"> {# #}
                         <a href="{{page.root_path|safe}}settings.html" title="settings"> {# #}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 1c02e0ba76e..52803c2b005 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -82,7 +82,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_interface::interface;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
-use rustc_session::{getopts, EarlyErrorHandler};
+use rustc_session::{getopts, EarlyDiagCtxt};
 
 use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
 
@@ -157,7 +157,7 @@ pub fn main() {
         }
     }
 
-    let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+    let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
     let using_internal_features = rustc_driver::install_ice_hook(
         "https://github.com/rust-lang/rust/issues/new\
@@ -175,11 +175,11 @@ pub fn main() {
     // `debug_logging = true` is because all rustc logging goes to its version of tracing (the one
     // in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml).
 
-    init_logging(&handler);
-    rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG"));
+    init_logging(&early_dcx);
+    rustc_driver::init_logger(&early_dcx, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG"));
 
-    let exit_code = rustc_driver::catch_with_exit_code(|| match get_args(&handler) {
-        Some(args) => main_args(&mut handler, &args, using_internal_features),
+    let exit_code = rustc_driver::catch_with_exit_code(|| match get_args(&early_dcx) {
+        Some(args) => main_args(&mut early_dcx, &args, using_internal_features),
         _ =>
         {
             #[allow(deprecated)]
@@ -189,15 +189,15 @@ pub fn main() {
     process::exit(exit_code);
 }
 
-fn init_logging(handler: &EarlyErrorHandler) {
+fn init_logging(early_dcx: &EarlyDiagCtxt) {
     let color_logs = match std::env::var("RUSTDOC_LOG_COLOR").as_deref() {
         Ok("always") => true,
         Ok("never") => false,
         Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(),
-        Ok(value) => handler.early_error(format!(
+        Ok(value) => early_dcx.early_error(format!(
             "invalid log color value '{value}': expected one of always, never, or auto",
         )),
-        Err(VarError::NotUnicode(value)) => handler.early_error(format!(
+        Err(VarError::NotUnicode(value)) => early_dcx.early_error(format!(
             "invalid log color value '{}': expected one of always, never, or auto",
             value.to_string_lossy()
         )),
@@ -220,13 +220,13 @@ fn init_logging(handler: &EarlyErrorHandler) {
     tracing::subscriber::set_global_default(subscriber).unwrap();
 }
 
-fn get_args(handler: &EarlyErrorHandler) -> Option<Vec<String>> {
+fn get_args(early_dcx: &EarlyDiagCtxt) -> Option<Vec<String>> {
     env::args_os()
         .enumerate()
         .map(|(i, arg)| {
             arg.into_string()
                 .map_err(|arg| {
-                    handler.early_warn(format!("Argument {i} is not valid Unicode: {arg:?}"));
+                    early_dcx.early_warn(format!("Argument {i} is not valid Unicode: {arg:?}"));
                 })
                 .ok()
         })
@@ -673,11 +673,11 @@ fn usage(argv0: &str) {
 /// A result type used by several functions under `main()`.
 type MainResult = Result<(), ErrorGuaranteed>;
 
-fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult {
+fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult {
     match res {
-        Ok(()) => diag.has_errors().map_or(Ok(()), Err),
+        Ok(()) => dcx.has_errors().map_or(Ok(()), Err),
         Err(err) => {
-            let reported = diag.struct_err(err).emit();
+            let reported = dcx.struct_err(err).emit();
             Err(reported)
         }
     }
@@ -704,7 +704,7 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
 }
 
 fn main_args(
-    handler: &mut EarlyErrorHandler,
+    early_dcx: &mut EarlyDiagCtxt,
     at_args: &[String],
     using_internal_features: Arc<AtomicBool>,
 ) -> MainResult {
@@ -718,7 +718,7 @@ fn main_args(
     // the compiler with @empty_file as argv[0] and no more arguments.
     let at_args = at_args.get(1..).unwrap_or_default();
 
-    let args = rustc_driver::args::arg_expand_all(handler, at_args);
+    let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
 
     let mut options = getopts::Options::new();
     for option in opts() {
@@ -727,13 +727,13 @@ fn main_args(
     let matches = match options.parse(&args) {
         Ok(m) => m,
         Err(err) => {
-            handler.early_error(err.to_string());
+            early_dcx.early_error(err.to_string());
         }
     };
 
     // Note that we discard any distinction between different non-zero exit
     // codes from `from_matches` here.
-    let (options, render_options) = match config::Options::from_matches(handler, &matches, args) {
+    let (options, render_options) = match config::Options::from_matches(early_dcx, &matches, args) {
         Ok(opts) => opts,
         Err(code) => {
             return if code == 0 {
@@ -745,12 +745,8 @@ fn main_args(
         }
     };
 
-    let diag = core::new_handler(
-        options.error_format,
-        None,
-        options.diagnostic_width,
-        &options.unstable_opts,
-    );
+    let diag =
+        core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
 
     match (options.should_test, options.markdown_input()) {
         (true, true) => return wrap_return(&diag, markdown::test(options)),
@@ -774,7 +770,7 @@ fn main_args(
     }
 
     // need to move these items separately because we lose them by the time the closure is called,
-    // but we can't create the Handler ahead of time because it's not Send
+    // but we can't create the dcx ahead of time because it's not Send
     let show_coverage = options.show_coverage;
     let run_check = options.run_check;
 
@@ -803,7 +799,7 @@ fn main_args(
 
         compiler.enter(|queries| {
             let mut gcx = abort_on_err(queries.global_ctxt(), sess);
-            if sess.diagnostic().has_errors_or_lint_errors().is_some() {
+            if sess.dcx().has_errors_or_lint_errors().is_some() {
                 sess.fatal("Compilation failed, aborting rustdoc");
             }
 
diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs
index d45040e348a..267f1cb0b72 100644
--- a/src/librustdoc/lint.rs
+++ b/src/librustdoc/lint.rs
@@ -33,7 +33,7 @@ where
     let lints = || {
         lint::builtin::HardwiredLints::get_lints()
             .into_iter()
-            .chain(rustc_lint::SoftLints::get_lints().into_iter())
+            .chain(rustc_lint::SoftLints::get_lints())
     };
 
     let lint_opts = lints()
@@ -46,7 +46,7 @@ where
                 filter_call(lint)
             }
         })
-        .chain(lint_opts.into_iter())
+        .chain(lint_opts)
         .collect::<Vec<_>>();
 
     let lint_caps = lints()
diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs
index 6266d3ff51d..73f71cc062b 100644
--- a/src/librustdoc/passes/check_custom_code_classes.rs
+++ b/src/librustdoc/passes/check_custom_code_classes.rs
@@ -48,7 +48,7 @@ struct TestsWithCustomClasses {
 
 impl crate::doctest::Tester for TestsWithCustomClasses {
     fn add_test(&mut self, _: String, config: LangString, _: usize) {
-        self.custom_classes_found.extend(config.added_classes.into_iter());
+        self.custom_classes_found.extend(config.added_classes);
     }
 }
 
@@ -66,9 +66,8 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
     if !tests.custom_classes_found.is_empty() {
         let span = item.attr_span(cx.tcx);
         let sess = &cx.tcx.sess.parse_sess;
-        let mut err = sess
-            .span_diagnostic
-            .struct_span_warn(span, "custom classes in code blocks will change behaviour");
+        let mut err =
+            sess.dcx.struct_span_warn(span, "custom classes in code blocks will change behaviour");
         add_feature_diagnostics_for_issue(
             &mut err,
             sess,
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 8d5b17f43cd..a931e8804d9 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -131,7 +131,7 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
                 hir_id,
                 sp,
                 "missing code example in this documentation",
-                |lint| lint,
+                |_| {},
             );
         }
     } else if tests.found_tests > 0
@@ -142,7 +142,7 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
             hir_id,
             item.attr_span(cx.tcx),
             "documentation test in private item",
-            |lint| lint,
+            |_| {},
         );
     }
 }
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 6b536e97c43..ee185ab9892 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1755,8 +1755,6 @@ fn report_diagnostic(
         }
 
         decorate(lint, span, link_range);
-
-        lint
     });
 }
 
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index b6f73d3fdcd..df2e8584b84 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -22,7 +22,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
     let tcx = cx.tcx;
     // We need to check if there are errors before running this pass because it would crash when
     // we try to get auto and blanket implementations.
-    if tcx.sess.diagnostic().has_errors_or_lint_errors().is_some() {
+    if tcx.sess.dcx().has_errors_or_lint_errors().is_some() {
         return krate;
     }
 
diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs
index 0c5cfffe1be..bffa17da3b4 100644
--- a/src/librustdoc/passes/lint/bare_urls.rs
+++ b/src/librustdoc/passes/lint/bare_urls.rs
@@ -31,7 +31,7 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) {
                             "use an automatic link instead",
                             format!("<{url}>"),
                             Applicability::MachineApplicable,
-                        )
+                        );
                 });
             };
 
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index e9b9e36a530..ce42b9c204b 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{
     emitter::Emitter,
     translation::{to_fluent_args, Translate},
-    Applicability, Diagnostic, Handler, LazyFallbackBundle,
+    Applicability, DiagCtxt, Diagnostic, LazyFallbackBundle,
 };
 use rustc_parse::parse_stream_from_source_str;
 use rustc_resolve::rustdoc::source_span_for_markdown_range;
@@ -42,9 +42,9 @@ fn check_rust_syntax(
     let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
 
     let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-    let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
+    let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
     let source = dox[code_block.code].to_owned();
-    let sess = ParseSess::with_span_handler(handler, sm);
+    let sess = ParseSess::with_dcx(dcx, sm);
 
     let edition = code_block.lang_string.edition.unwrap_or_else(|| cx.tcx.sess.edition());
     let expn_data =
@@ -131,8 +131,6 @@ fn check_rust_syntax(
         for message in buffer.messages.iter() {
             lint.note(message.clone());
         }
-
-        lint
     });
 }
 
diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index 4a80c22ed08..90874c01102 100644
--- a/src/librustdoc/passes/lint/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -89,7 +89,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
                     if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
                         || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
                     {
-                        return lint;
+                        return;
                     }
                     // multipart form is chosen here because ``Vec<i32>`` would be confusing.
                     lint.multipart_suggestion(
@@ -101,8 +101,6 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
                         Applicability::MaybeIncorrect,
                     );
                 }
-
-                lint
             });
         };
 
diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs
index 472781e7d22..4491d20b478 100644
--- a/src/librustdoc/passes/lint/redundant_explicit_links.rs
+++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs
@@ -181,8 +181,6 @@ fn check_inline_or_reference_unknown_redundancy(
                 .span_label(display_span, "because label contains path that resolves to same destination")
                 .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links")
                 .span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways);
-
-            lint
         });
     }
 
@@ -234,8 +232,6 @@ fn check_reference_redundancy(
                 .span_note(def_span, "referenced explicit link target defined here")
                 .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links")
                 .span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways);
-
-            lint
         });
     }
 
diff --git a/src/librustdoc/passes/lint/unescaped_backticks.rs b/src/librustdoc/passes/lint/unescaped_backticks.rs
index 8b7fdd6ab1f..0893cd0b40b 100644
--- a/src/librustdoc/passes/lint/unescaped_backticks.rs
+++ b/src/librustdoc/passes/lint/unescaped_backticks.rs
@@ -111,8 +111,6 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
                     }
 
                     suggest_insertion(cx, item, &dox, lint, backtick_index, '\\', "if you meant to use a literal backtick, escape it");
-
-                    lint
                 });
             }
             Event::Code(_) => {
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 14680fdb064..a343d7afcee 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -40,7 +40,7 @@ pub(crate) struct ScrapeExamplesOptions {
 impl ScrapeExamplesOptions {
     pub(crate) fn new(
         matches: &getopts::Matches,
-        diag: &rustc_errors::Handler,
+        dcx: &rustc_errors::DiagCtxt,
     ) -> Result<Option<Self>, i32> {
         let output_path = matches.opt_str("scrape-examples-output-path");
         let target_crates = matches.opt_strs("scrape-examples-target-crate");
@@ -52,11 +52,11 @@ impl ScrapeExamplesOptions {
                 scrape_tests,
             })),
             (Some(_), false, _) | (None, true, _) => {
-                diag.err("must use --scrape-examples-output-path and --scrape-examples-target-crate together");
+                dcx.err("must use --scrape-examples-output-path and --scrape-examples-target-crate together");
                 Err(1)
             }
             (None, false, true) => {
-                diag.err("must use --scrape-examples-output-path and --scrape-examples-target-crate with --scrape-tests");
+                dcx.err("must use --scrape-examples-output-path and --scrape-examples-target-crate with --scrape-tests");
                 Err(1)
             }
             (None, false, false) => Ok(None),
@@ -311,7 +311,7 @@ pub(crate) fn run(
 
         // The visitor might have found a type error, which we need to
         // promote to a fatal error
-        if tcx.sess.diagnostic().has_errors_or_lint_errors().is_some() {
+        if tcx.sess.dcx().has_errors_or_lint_errors().is_some() {
             return Err(String::from("Compilation failed, aborting rustdoc"));
         }
 
@@ -337,10 +337,11 @@ pub(crate) fn run(
     Ok(())
 }
 
-// Note: the Handler must be passed in explicitly because sess isn't available while parsing options
+// Note: the DiagCtxt must be passed in explicitly because sess isn't available while parsing
+// options.
 pub(crate) fn load_call_locations(
     with_examples: Vec<String>,
-    diag: &rustc_errors::Handler,
+    dcx: &rustc_errors::DiagCtxt,
 ) -> Result<AllCallLocations, i32> {
     let inner = || {
         let mut all_calls: AllCallLocations = FxHashMap::default();
@@ -358,7 +359,7 @@ pub(crate) fn load_call_locations(
     };
 
     inner().map_err(|e: String| {
-        diag.err(format!("failed to load examples: {e}"));
+        dcx.err(format!("failed to load examples: {e}"));
         1
     })
 }
diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs
index 8c1acbd7347..98010b056c9 100644
--- a/src/librustdoc/theme.rs
+++ b/src/librustdoc/theme.rs
@@ -5,7 +5,7 @@ use std::iter::Peekable;
 use std::path::Path;
 use std::str::Chars;
 
-use rustc_errors::Handler;
+use rustc_errors::DiagCtxt;
 
 #[cfg(test)]
 mod tests;
@@ -236,7 +236,7 @@ pub(crate) fn get_differences(
 pub(crate) fn test_theme_against<P: AsRef<Path>>(
     f: &P,
     origin: &FxHashMap<String, CssPath>,
-    diag: &Handler,
+    dcx: &DiagCtxt,
 ) -> (bool, Vec<String>) {
     let against = match fs::read_to_string(f)
         .map_err(|e| e.to_string())
@@ -244,7 +244,7 @@ pub(crate) fn test_theme_against<P: AsRef<Path>>(
     {
         Ok(c) => c,
         Err(e) => {
-            diag.struct_err(e).emit();
+            dcx.struct_err(e).emit();
             return (false, vec![]);
         }
     };
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index bbc7e5b48d1..907ea6d309c 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -163,15 +163,16 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     .iter()
                     .filter_map(|attr| {
                         Cfg::parse(attr.meta_item()?)
-                            .map_err(|e| self.cx.sess().diagnostic().span_err(e.span, e.msg))
+                            .map_err(|e| self.cx.sess().dcx().span_err(e.span, e.msg))
                             .ok()
                     })
                     .collect::<Vec<_>>()
             })
-            .chain(
-                [Cfg::Cfg(sym::test, None), Cfg::Cfg(sym::doc, None), Cfg::Cfg(sym::doctest, None)]
-                    .into_iter(),
-            )
+            .chain([
+                Cfg::Cfg(sym::test, None),
+                Cfg::Cfg(sym::doc, None),
+                Cfg::Cfg(sym::doctest, None),
+            ])
             .collect();
 
         self.cx.cache.exact_paths = self.exact_paths;
diff --git a/src/stage0.json b/src/stage0.json
index 3fb6a0d4ed0..5625e7219f8 100644
--- a/src/stage0.json
+++ b/src/stage0.json
@@ -74,6 +74,54 @@
     "dist/2023-11-13/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "790a859eaf49da5fdef1ea1ebf8b7f1f001bdaa62798a745d791bc915d886094",
     "dist/2023-11-13/cargo-beta-x86_64-unknown-netbsd.tar.gz": "f14395b57c0eca7c8ac9012b303c3781e16a2287698f68705fcbbc5c7c5916c9",
     "dist/2023-11-13/cargo-beta-x86_64-unknown-netbsd.tar.xz": "2c759c8bfc9cc556eb3464ac7e5589b573c46bd3938ac288ba52549121d3333d",
+    "dist/2023-11-13/clippy-beta-aarch64-apple-darwin.tar.gz": "258b55f0db78c93f14952fc3e2d9fc49f1121de5268ec49d03d4f197bd7c9773",
+    "dist/2023-11-13/clippy-beta-aarch64-apple-darwin.tar.xz": "554bd9488a9887c4acfb7c24775365108aef049feb15c93926128f805183f5e1",
+    "dist/2023-11-13/clippy-beta-aarch64-pc-windows-msvc.tar.gz": "d120adaec65b5f44f40fba38fa10a0cda33769756ac7fc2878d64154acc7e06c",
+    "dist/2023-11-13/clippy-beta-aarch64-pc-windows-msvc.tar.xz": "1dd23152a87fb8fa60569c304a3bbfd93f97c52d533fe53c8d9a5a4a72912938",
+    "dist/2023-11-13/clippy-beta-aarch64-unknown-linux-gnu.tar.gz": "41198264d015ccf624e0fca876bee16f8c3cbb07596f5f4d4f271b9843b10e30",
+    "dist/2023-11-13/clippy-beta-aarch64-unknown-linux-gnu.tar.xz": "1c889aad06c73dd2212403ff2aa5f256eba490cb8f1c81d7c75199a267052648",
+    "dist/2023-11-13/clippy-beta-aarch64-unknown-linux-musl.tar.gz": "db784d117c156d0e8854518a5df6e9adbf0b2d24f3504358b1a371b8e7954eef",
+    "dist/2023-11-13/clippy-beta-aarch64-unknown-linux-musl.tar.xz": "a946d40d32be1c5477f1740648e18af5792ceed38fe255e8cf09160acb27d02f",
+    "dist/2023-11-13/clippy-beta-arm-unknown-linux-gnueabi.tar.gz": "1b760fadecb9f4c4c1780b36d9bd651fa6c8a8fb6b48dc0dfbfb2ecd9359ca92",
+    "dist/2023-11-13/clippy-beta-arm-unknown-linux-gnueabi.tar.xz": "9e21f778eee57b067f411faa15425317158abd22f7c60dd4fc24214536ff7415",
+    "dist/2023-11-13/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz": "fbe3d7776b9353782d2690ff1c98a0ce5d8d23de2381ecd3d4eff4ce165b66d5",
+    "dist/2023-11-13/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz": "bfabf9e2419065173bf709166b0c81db66e1b2514003b28d08ec4410e04525a3",
+    "dist/2023-11-13/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz": "5371c798687aece2b98e22cf5bca02e568913011393a86a1aa3b0ff79eeabb57",
+    "dist/2023-11-13/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz": "82723066f2ec52fb5854a397f71df79db54976f4c03113034627c1a5c92ceffb",
+    "dist/2023-11-13/clippy-beta-i686-pc-windows-gnu.tar.gz": "0214eee5dc37f3a780ca62f1e7a66c5e19d583542dbfca047e383dfacca604b3",
+    "dist/2023-11-13/clippy-beta-i686-pc-windows-gnu.tar.xz": "cee7d7039b1ee909fa4a188d7fd0bae14d718bad98209d8df2dc087cc28d7a9c",
+    "dist/2023-11-13/clippy-beta-i686-pc-windows-msvc.tar.gz": "b6f1042f0d4652e941d975af8aeb4a61e335c585c1cd6dad7f85ba5b03c6face",
+    "dist/2023-11-13/clippy-beta-i686-pc-windows-msvc.tar.xz": "a31c1951abdff06c3709472acede08cd000b940a7a65f9afd664ca276fc724c1",
+    "dist/2023-11-13/clippy-beta-i686-unknown-linux-gnu.tar.gz": "1e0f2f2ea38988cd0012ceedacfa9828b4b6c6dfd3dca9eee5a98483fd90f439",
+    "dist/2023-11-13/clippy-beta-i686-unknown-linux-gnu.tar.xz": "f19b56068a40af2aee7c828a4688289a8142f98a81b80d66d12ddc6725209628",
+    "dist/2023-11-13/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz": "e0f02e3ba303bef68a7cd1ea5effaa126eedbdc892b01dac85f4b3e6d4b2f522",
+    "dist/2023-11-13/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz": "17a01e484f238bff790ad6cfededa8d9c7df0f6f36af336a1e8a7414a43271f5",
+    "dist/2023-11-13/clippy-beta-powerpc-unknown-linux-gnu.tar.gz": "ea4bc3aed8739bcecaa97f01972bbbba45e838c811ca0c408824b1409f9ac7c6",
+    "dist/2023-11-13/clippy-beta-powerpc-unknown-linux-gnu.tar.xz": "9ddb71b1f35f6953aedc6a1f93dde4a9b014255b5bd7d4448e4dd9b1a65c7f5a",
+    "dist/2023-11-13/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz": "ccb95c3ce3dd4ee2776201ce1bd79122e97916c555126393f1839d301824e746",
+    "dist/2023-11-13/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz": "9aad65fcefe5789670bc85cd177bd72eac469eb0a33db54685132a7ce5115a83",
+    "dist/2023-11-13/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz": "eb50e6349423c963fdce3193f361572a47e4d7fe5b90f8a297471c0a5df31feb",
+    "dist/2023-11-13/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz": "aa72d573b344953b9224ceb6eff484b6f012febfc009bc7773d398a3ffa69ea0",
+    "dist/2023-11-13/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz": "8546ac9a9e5081384d7ef889254e41998f60c04bbdca319128039e8a4e7cbc68",
+    "dist/2023-11-13/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz": "15fa574f7f7de2d8fcd124f44b1880a78bdd6a2ba77fe6b392299dd746ea9c63",
+    "dist/2023-11-13/clippy-beta-s390x-unknown-linux-gnu.tar.gz": "ee27937b85c142ebf46bb59ad9e113414cc9d631179a2f8df2b32980aa57b515",
+    "dist/2023-11-13/clippy-beta-s390x-unknown-linux-gnu.tar.xz": "e6e99e486418cb23a0cb80a2b145413bfeb62746b60fd9a0ff3959424b7ee07e",
+    "dist/2023-11-13/clippy-beta-x86_64-apple-darwin.tar.gz": "8a37545cef25abdd3c722a21385b843cb9d8d06fed8f302880beecbcb9373f98",
+    "dist/2023-11-13/clippy-beta-x86_64-apple-darwin.tar.xz": "f572586fc28bb5b2b77464d89b41c374de4002b453c297a8fc1468cee10db972",
+    "dist/2023-11-13/clippy-beta-x86_64-pc-windows-gnu.tar.gz": "5add2dab913488a482be6849887d9fa8c3cd197d3c423595a7133b511efcefca",
+    "dist/2023-11-13/clippy-beta-x86_64-pc-windows-gnu.tar.xz": "1d0d97a6b8703166616b6ae56d2b344e85dfa1db36976585d21cda65362cfff8",
+    "dist/2023-11-13/clippy-beta-x86_64-pc-windows-msvc.tar.gz": "4069dc9296cdc54af7e14d4ba5fc12ee5e6445be74f39c39526e4ed77378112f",
+    "dist/2023-11-13/clippy-beta-x86_64-pc-windows-msvc.tar.xz": "fdf4dfaef5f8fb24075bf8b24bdbd71804a49acda40921de84c61bc2250ade6c",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-freebsd.tar.gz": "4554eee1b1bd7e5575eaf5e7a815686da88fb395a80084c7a0fad3f338ac83b1",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-freebsd.tar.xz": "b0f1718df2a1e88ff4aa239bf4b9dc7a971fbc64cfa1ef8f399760fe09117097",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-illumos.tar.gz": "035400e01edb1a91ddcac72fe3ca1ea94465c4fe9047546d057f6867d8b068d1",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-illumos.tar.xz": "3ad1b06c3bc1e327ea7a3b30908b48bf6b59b7222101e5a30fb054b1be579087",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-linux-gnu.tar.gz": "88d611c40a30922515d09425ecd1a8b87f46131ab61f3c4bf841a41f281134b3",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-linux-gnu.tar.xz": "9908227c8a684aa5c24f3272997fc9688044580f792375ec9fc8f775e394953f",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-linux-musl.tar.gz": "10a7b2427ee8a4e83d271023e6bd00573266f317789cff3b75e68d7bddc41cbc",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-linux-musl.tar.xz": "cb1f3c2be55e11920340344d2f2f796098b7f13c9ddd2fb8a38ac1b29c5d6925",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-netbsd.tar.gz": "2b29461b085f0af1bb50d10ef8bf6448550c9b5867a364da3d921daea8e0a13d",
+    "dist/2023-11-13/clippy-beta-x86_64-unknown-netbsd.tar.xz": "e9d2ff4c8c63a9cfb7d8b12df4942f5b90c33c4fa3f755bbdc3c5df1b2b877fe",
     "dist/2023-11-13/rust-std-beta-aarch64-apple-darwin.tar.gz": "818e3554a6bd82c3533e68f8597c09563af1dc08d6b23bbd2323b8ebcce251f6",
     "dist/2023-11-13/rust-std-beta-aarch64-apple-darwin.tar.xz": "6f9333c281859e40b1c1c20e9f07ac1633ca2c99e86268b521945b8ed6cf6a50",
     "dist/2023-11-13/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "fa4bb7a5d4dcecb2dfd8596e3ad442a5fe05468c939de3b36e44a43e10ea9d30",
diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs
index b007f9a22c3..bd97b4eaa3e 100644
--- a/src/tools/bump-stage0/src/main.rs
+++ b/src/tools/bump-stage0/src/main.rs
@@ -4,7 +4,7 @@ use indexmap::IndexMap;
 use std::collections::HashMap;
 
 const PATH: &str = "src/stage0.json";
-const COMPILER_COMPONENTS: &[&str] = &["rustc", "rust-std", "cargo"];
+const COMPILER_COMPONENTS: &[&str] = &["rustc", "rust-std", "cargo", "clippy-preview"];
 const RUSTFMT_COMPONENTS: &[&str] = &["rustfmt-preview", "rustc"];
 
 struct Tool {
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 1aa9df1a5be205cce621f0bc0ea6062a5e22a98
+Subproject 1a2666ddd14cf0a255d4ddb61c63531c259a7b3
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index f67233dec62..73c25550742 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -206,6 +206,7 @@ jobs:
       max-parallel: 6
       matrix:
         integration:
+        - 'matthiaskrgr/clippy_ci_panic_test'
         - 'rust-lang/cargo'
         - 'rust-lang/chalk'
         - 'rust-lang/rustfmt'
@@ -220,7 +221,6 @@ jobs:
         - 'rust-itertools/itertools'
         - 'rust-lang-nursery/failure'
         - 'rust-lang/log'
-        - 'matthiaskrgr/clippy_ci_panic_test'
 
     runs-on: ubuntu-latest
 
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 2e9b755caa0..70aff7f60e0 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -4946,6 +4946,7 @@ Released 2018-09-13
 [`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 [`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
 [`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
+[`blocks_in_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_conditions
 [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
@@ -5145,9 +5146,11 @@ Released 2018-09-13
 [`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
+[`ineffective_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_open_options
 [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 [`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 [`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
+[`infinite_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_loop
 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 [`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
@@ -5462,6 +5465,7 @@ Released 2018-09-13
 [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
 [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
+[`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity
 [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 [`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization
 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
@@ -5582,6 +5586,7 @@ Released 2018-09-13
 [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
 [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
+[`uninhabited_references`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninhabited_references
 [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
 [`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
 [`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs
index a19be1bca6c..906a9727810 100644
--- a/src/tools/clippy/clippy_dev/src/lint.rs
+++ b/src/tools/clippy/clippy_dev/src/lint.rs
@@ -1,6 +1,6 @@
 use crate::{cargo_clippy_path, exit_if_err};
-use std::fs;
 use std::process::{self, Command};
+use std::{env, fs};
 
 pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
     let is_file = match fs::metadata(path) {
@@ -13,7 +13,7 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
 
     if is_file {
         exit_if_err(
-            Command::new("cargo")
+            Command::new(env::var("CARGO").unwrap_or("cargo".into()))
                 .args(["run", "--bin", "clippy-driver", "--"])
                 .args(["-L", "./target/debug"])
                 .args(["-Z", "no-codegen"])
@@ -23,7 +23,11 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
                 .status(),
         );
     } else {
-        exit_if_err(Command::new("cargo").arg("build").status());
+        exit_if_err(
+            Command::new(env::var("CARGO").unwrap_or("cargo".into()))
+                .arg("build")
+                .status(),
+        );
 
         let status = Command::new(cargo_clippy_path())
             .arg("clippy")
diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs
index 535c25e69f1..ea925f6709f 100644
--- a/src/tools/clippy/clippy_dev/src/serve.rs
+++ b/src/tools/clippy/clippy_dev/src/serve.rs
@@ -2,8 +2,8 @@ use std::ffi::OsStr;
 use std::num::ParseIntError;
 use std::path::Path;
 use std::process::Command;
-use std::thread;
 use std::time::{Duration, SystemTime};
+use std::{env, thread};
 
 /// # Panics
 ///
@@ -16,7 +16,7 @@ pub fn run(port: u16, lint: Option<&String>) -> ! {
 
     loop {
         if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") {
-            Command::new("cargo")
+            Command::new(env::var("CARGO").unwrap_or("cargo".into()))
                 .arg("collect-metadata")
                 .spawn()
                 .unwrap()
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index a9375214be4..ad8b7ded46b 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -16,7 +16,7 @@ clippy_utils = { path = "../clippy_utils" }
 declare_clippy_lint = { path = "../declare_clippy_lint" }
 itertools = "0.11"
 quine-mc_cluskey = "0.2"
-regex-syntax = "0.7"
+regex-syntax = "0.8"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = { version = "1.0", optional = true }
 tempfile = { version = "3.3.0", optional = true }
diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs
index e052d36f115..e3daf75c3eb 100644
--- a/src/tools/clippy/clippy_lints/src/as_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs
@@ -48,11 +48,10 @@ declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
 
 impl<'tcx> LateLintPass<'tcx> for AsConversions {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-        if in_external_macro(cx.sess(), expr.span) || is_from_proc_macro(cx, expr) {
-            return;
-        }
-
-        if let ExprKind::Cast(_, _) = expr.kind {
+        if let ExprKind::Cast(_, _) = expr.kind
+            && !in_external_macro(cx.sess(), expr.span)
+            && !is_from_proc_macro(cx, expr)
+        {
             span_lint_and_help(
                 cx,
                 AS_CONVERSIONS,
diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs
new file mode 100644
index 00000000000..1417e230aee
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs
@@ -0,0 +1,137 @@
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_block_with_applicability;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::{get_parent_expr, higher};
+use core::ops::ControlFlow;
+use rustc_errors::Applicability;
+use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::declare_lint_pass;
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `if` conditions that use blocks containing an
+    /// expression, statements or conditions that use closures with blocks.
+    ///
+    /// ### Why is this bad?
+    /// Style, using blocks in the condition makes it hard to read.
+    ///
+    /// ### Examples
+    /// ```no_run
+    /// # fn somefunc() -> bool { true };
+    /// if { true } { /* ... */ }
+    ///
+    /// if { let x = somefunc(); x } { /* ... */ }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// # fn somefunc() -> bool { true };
+    /// if true { /* ... */ }
+    ///
+    /// let res = { let x = somefunc(); x };
+    /// if res { /* ... */ }
+    /// ```
+    #[clippy::version = "1.45.0"]
+    pub BLOCKS_IN_CONDITIONS,
+    style,
+    "useless or complex blocks that can be eliminated in conditions"
+}
+
+declare_lint_pass!(BlocksInConditions => [BLOCKS_IN_CONDITIONS]);
+
+const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
+
+impl<'tcx> LateLintPass<'tcx> for BlocksInConditions {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+
+        let Some((cond, keyword, desc)) = higher::If::hir(expr)
+            .map(|hif| (hif.cond, "if", "an `if` condition"))
+            .or(if let ExprKind::Match(match_ex, _, MatchSource::Normal) = expr.kind {
+                Some((match_ex, "match", "a `match` scrutinee"))
+            } else {
+                None
+            })
+        else {
+            return;
+        };
+        let complex_block_message = &format!(
+            "in {desc}, avoid complex blocks or closures with blocks; \
+            instead, move the block or closure higher and bind it with a `let`",
+        );
+
+        if let ExprKind::Block(block, _) = &cond.kind {
+            if block.rules == BlockCheckMode::DefaultBlock {
+                if block.stmts.is_empty() {
+                    if let Some(ex) = &block.expr {
+                        // don't dig into the expression here, just suggest that they remove
+                        // the block
+                        if expr.span.from_expansion() || ex.span.from_expansion() {
+                            return;
+                        }
+                        let mut applicability = Applicability::MachineApplicable;
+                        span_lint_and_sugg(
+                            cx,
+                            BLOCKS_IN_CONDITIONS,
+                            cond.span,
+                            BRACED_EXPR_MESSAGE,
+                            "try",
+                            snippet_block_with_applicability(cx, ex.span, "..", Some(expr.span), &mut applicability)
+                                .to_string(),
+                            applicability,
+                        );
+                    }
+                } else {
+                    let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
+                    if span.from_expansion() || expr.span.from_expansion() {
+                        return;
+                    }
+                    // move block higher
+                    let mut applicability = Applicability::MachineApplicable;
+                    span_lint_and_sugg(
+                        cx,
+                        BLOCKS_IN_CONDITIONS,
+                        expr.span.with_hi(cond.span.hi()),
+                        complex_block_message,
+                        "try",
+                        format!(
+                            "let res = {}; {keyword} res",
+                            snippet_block_with_applicability(cx, block.span, "..", Some(expr.span), &mut applicability),
+                        ),
+                        applicability,
+                    );
+                }
+            }
+        } else {
+            let _: Option<!> = for_each_expr(cond, |e| {
+                if let ExprKind::Closure(closure) = e.kind {
+                    // do not lint if the closure is called using an iterator (see #1141)
+                    if let Some(parent) = get_parent_expr(cx, e)
+                        && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind
+                        && let caller = cx.typeck_results().expr_ty(self_arg)
+                        && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
+                        && implements_trait(cx, caller, iter_id, &[])
+                    {
+                        return ControlFlow::Continue(Descend::No);
+                    }
+
+                    let body = cx.tcx.hir().body(closure.body);
+                    let ex = &body.value;
+                    if let ExprKind::Block(block, _) = ex.kind {
+                        if !body.value.span.from_expansion() && !block.stmts.is_empty() {
+                            span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message);
+                            return ControlFlow::Continue(Descend::No);
+                        }
+                    }
+                }
+                ControlFlow::Continue(Descend::Yes)
+            });
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
deleted file mode 100644
index 692309629b7..00000000000
--- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::source::snippet_block_with_applicability;
-use clippy_utils::ty::implements_trait;
-use clippy_utils::visitors::{for_each_expr, Descend};
-use clippy_utils::{get_parent_expr, higher};
-use core::ops::ControlFlow;
-use rustc_errors::Applicability;
-use rustc_hir::{BlockCheckMode, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::declare_lint_pass;
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `if` conditions that use blocks containing an
-    /// expression, statements or conditions that use closures with blocks.
-    ///
-    /// ### Why is this bad?
-    /// Style, using blocks in the condition makes it hard to read.
-    ///
-    /// ### Examples
-    /// ```no_run
-    /// # fn somefunc() -> bool { true };
-    /// if { true } { /* ... */ }
-    ///
-    /// if { let x = somefunc(); x } { /* ... */ }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// # fn somefunc() -> bool { true };
-    /// if true { /* ... */ }
-    ///
-    /// let res = { let x = somefunc(); x };
-    /// if res { /* ... */ }
-    /// ```
-    #[clippy::version = "1.45.0"]
-    pub BLOCKS_IN_IF_CONDITIONS,
-    style,
-    "useless or complex blocks that can be eliminated in conditions"
-}
-
-declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]);
-
-const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
-const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
-                                    instead, move the block or closure higher and bind it with a `let`";
-
-impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-        if let Some(higher::If { cond, .. }) = higher::If::hir(expr) {
-            if let ExprKind::Block(block, _) = &cond.kind {
-                if block.rules == BlockCheckMode::DefaultBlock {
-                    if block.stmts.is_empty() {
-                        if let Some(ex) = &block.expr {
-                            // don't dig into the expression here, just suggest that they remove
-                            // the block
-                            if expr.span.from_expansion() || ex.span.from_expansion() {
-                                return;
-                            }
-                            let mut applicability = Applicability::MachineApplicable;
-                            span_lint_and_sugg(
-                                cx,
-                                BLOCKS_IN_IF_CONDITIONS,
-                                cond.span,
-                                BRACED_EXPR_MESSAGE,
-                                "try",
-                                format!(
-                                    "{}",
-                                    snippet_block_with_applicability(
-                                        cx,
-                                        ex.span,
-                                        "..",
-                                        Some(expr.span),
-                                        &mut applicability
-                                    )
-                                ),
-                                applicability,
-                            );
-                        }
-                    } else {
-                        let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
-                        if span.from_expansion() || expr.span.from_expansion() {
-                            return;
-                        }
-                        // move block higher
-                        let mut applicability = Applicability::MachineApplicable;
-                        span_lint_and_sugg(
-                            cx,
-                            BLOCKS_IN_IF_CONDITIONS,
-                            expr.span.with_hi(cond.span.hi()),
-                            COMPLEX_BLOCK_MESSAGE,
-                            "try",
-                            format!(
-                                "let res = {}; if res",
-                                snippet_block_with_applicability(
-                                    cx,
-                                    block.span,
-                                    "..",
-                                    Some(expr.span),
-                                    &mut applicability
-                                ),
-                            ),
-                            applicability,
-                        );
-                    }
-                }
-            } else {
-                let _: Option<!> = for_each_expr(cond, |e| {
-                    if let ExprKind::Closure(closure) = e.kind {
-                        // do not lint if the closure is called using an iterator (see #1141)
-                        if let Some(parent) = get_parent_expr(cx, e)
-                            && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind
-                            && let caller = cx.typeck_results().expr_ty(self_arg)
-                            && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
-                            && implements_trait(cx, caller, iter_id, &[])
-                        {
-                            return ControlFlow::Continue(Descend::No);
-                        }
-
-                        let body = cx.tcx.hir().body(closure.body);
-                        let ex = &body.value;
-                        if let ExprKind::Block(block, _) = ex.kind {
-                            if !body.value.span.from_expansion() && !block.stmts.is_empty() {
-                                span_lint(cx, BLOCKS_IN_IF_CONDITIONS, ex.span, COMPLEX_BLOCK_MESSAGE);
-                                return ControlFlow::Continue(Descend::No);
-                            }
-                        }
-                    }
-                    ControlFlow::Continue(Descend::Yes)
-                });
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
index d3d4f3c41c8..0ca4a0e067d 100644
--- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
@@ -57,7 +57,6 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
             && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
             && let ref_ty = cx.typeck_results().expr_ty(deref_target)
             && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind()
-            && !is_from_proc_macro(cx, e)
         {
             if let Some(parent_expr) = get_parent_expr(cx, e) {
                 if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..))
@@ -75,6 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
                     return;
                 }
             }
+            if is_from_proc_macro(cx, e) {
+                return;
+            }
 
             span_lint_and_then(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
index 55294f5f386..8bfb7383f14 100644
--- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
@@ -9,11 +9,10 @@ use rustc_middle::ty::{self, Ty, TypeAndMut};
 use super::AS_PTR_CAST_MUT;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) {
-    if let ty::RawPtr(
-        ptrty @ TypeAndMut {
-            mutbl: Mutability::Mut, ..
-        },
-    ) = cast_to.kind()
+    if let ty::RawPtr(TypeAndMut {
+        mutbl: Mutability::Mut,
+        ty: ptrty,
+    }) = cast_to.kind()
         && let ty::RawPtr(TypeAndMut {
             mutbl: Mutability::Not, ..
         }) = cx.typeck_results().node_type(cast_expr.hir_id).kind()
@@ -34,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             cx,
             AS_PTR_CAST_MUT,
             expr.span,
-            &format!("casting the result of `as_ptr` to *{ptrty}"),
+            &format!("casting the result of `as_ptr` to *mut {ptrty}"),
             "replace with",
             format!("{recv}.as_mut_ptr()"),
             applicability,
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
index 0c555c1acc5..35e36e9ef3f 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -3,12 +3,29 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
+use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind};
+use rustc_hir_pretty::qpath_to_string;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, TypeAndMut};
+use rustc_span::sym;
 
 use super::PTR_AS_PTR;
 
+enum OmitFollowedCastReason<'a> {
+    None,
+    Null(&'a QPath<'a>),
+    NullMut(&'a QPath<'a>),
+}
+
+impl OmitFollowedCastReason<'_> {
+    fn corresponding_item(&self) -> Option<&QPath<'_>> {
+        match self {
+            OmitFollowedCastReason::None => None,
+            OmitFollowedCastReason::Null(x) | OmitFollowedCastReason::NullMut(x) => Some(*x),
+        }
+    }
+}
+
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
     if !msrv.meets(msrvs::POINTER_CAST) {
         return;
@@ -25,7 +42,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
         && to_pointee_ty.is_sized(cx.tcx, cx.param_env)
     {
         let mut app = Applicability::MachineApplicable;
-        let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
         let turbofish = match &cast_to_hir_ty.kind {
             TyKind::Infer => String::new(),
             TyKind::Ptr(mut_ty) => {
@@ -41,13 +57,44 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
             _ => return,
         };
 
+        // following `cast` does not compile because it fails to infer what type is expected
+        // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so
+        // we omit following `cast`:
+        let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind
+            && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind
+        {
+            let method_defid = path.res.def_id();
+            if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) {
+                OmitFollowedCastReason::Null(qpath)
+            } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) {
+                OmitFollowedCastReason::NullMut(qpath)
+            } else {
+                OmitFollowedCastReason::None
+            }
+        } else {
+            OmitFollowedCastReason::None
+        };
+
+        let (help, final_suggestion) = if let Some(method) = omit_cast.corresponding_item() {
+            // don't force absolute path
+            let method = qpath_to_string(method);
+            ("try call directly", format!("{method}{turbofish}()"))
+        } else {
+            let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
+
+            (
+                "try `pointer::cast`, a safer alternative",
+                format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
+            )
+        };
+
         span_lint_and_sugg(
             cx,
             PTR_AS_PTR,
             expr.span,
             "`as` casting between raw pointers without changing its mutability",
-            "try `pointer::cast`, a safer alternative",
-            format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
+            help,
+            final_suggestion,
             app,
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 3627096ed91..1220eb89013 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -63,7 +63,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO,
     crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO,
     crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO,
-    crate::blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS_INFO,
+    crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO,
     crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO,
     crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO,
     crate::booleans::NONMINIMAL_BOOL_INFO,
@@ -215,6 +215,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
     crate::indexing_slicing::INDEXING_SLICING_INFO,
     crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
+    crate::ineffective_open_options::INEFFECTIVE_OPEN_OPTIONS_INFO,
     crate::infinite_iter::INFINITE_ITER_INFO,
     crate::infinite_iter::MAYBE_INFINITE_ITER_INFO,
     crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO,
@@ -265,6 +266,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,
     crate::loops::EXPLICIT_ITER_LOOP_INFO,
     crate::loops::FOR_KV_MAP_INFO,
+    crate::loops::INFINITE_LOOP_INFO,
     crate::loops::ITER_NEXT_LOOP_INFO,
     crate::loops::MANUAL_FIND_INFO,
     crate::loops::MANUAL_FLATTEN_INFO,
@@ -598,6 +600,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::reference::DEREF_ADDROF_INFO,
     crate::regex::INVALID_REGEX_INFO,
     crate::regex::TRIVIAL_REGEX_INFO,
+    crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO,
     crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO,
     crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
     crate::returns::LET_AND_RETURN_INFO,
@@ -678,6 +681,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::unicode::INVISIBLE_CHARACTERS_INFO,
     crate::unicode::NON_ASCII_LITERAL_INFO,
     crate::unicode::UNICODE_NOT_NFC_INFO,
+    crate::uninhabited_references::UNINHABITED_REFERENCES_INFO,
     crate::uninit_vec::UNINIT_VEC_INFO,
     crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO,
     crate::unit_types::LET_UNIT_VALUE_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs
index c0b11eb0dd1..a58219c2946 100644
--- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs
@@ -9,11 +9,21 @@ use url::Url;
 use crate::doc::DOC_MARKDOWN;
 
 pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
-    for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
+    for orig_word in text.split(|c: char| c.is_whitespace() || c == '\'') {
         // Trim punctuation as in `some comment (see foo::bar).`
         //                                                   ^^
         // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
-        let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
+        let trim_pattern = |c: char| !c.is_alphanumeric() && c != ':';
+        let mut word = orig_word.trim_end_matches(trim_pattern);
+
+        // If word is immediately followed by `()`, claw it back.
+        if let Some(tmp_word) = orig_word.get(..word.len() + 2)
+            && tmp_word.ends_with("()")
+        {
+            word = tmp_word;
+        }
+
+        word = word.trim_start_matches(trim_pattern);
 
         // Remove leading or trailing single `:` which may be part of a sentence.
         if word.starts_with(':') && !word.starts_with("::") {
@@ -84,7 +94,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
         return;
     }
 
-    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
+    if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") {
         let mut applicability = Applicability::MachineApplicable;
 
         span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
index e019523e609..c639813a3f9 100644
--- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
@@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint;
 use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::Handler;
+use rustc_errors::DiagCtxt;
 use rustc_lint::LateContext;
 use rustc_parse::maybe_new_parser_from_source_str;
 use rustc_parse::parser::ForceCollect;
@@ -45,10 +45,10 @@ pub fn check(
                 let fallback_bundle =
                     rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
                 let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
-                let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
-                #[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler
+                let dcx = DiagCtxt::with_emitter(Box::new(emitter)).disable_warnings();
+                #[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_dcx
                 let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-                let sess = ParseSess::with_span_handler(handler, sm);
+                let sess = ParseSess::with_dcx(dcx, sm);
 
                 let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
                     Ok(p) => p,
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index f1366c434f4..e8c1e5db35e 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
     /// replaced with `(e)print!()` / `(e)println!()`
     ///
     /// ### Why is this bad?
-    /// Using `(e)println! is clearer and more concise
+    /// Using `(e)println!` is clearer and more concise
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 4811691c80d..0f5a9ea5d84 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -194,7 +194,12 @@ fn is_same_generics<'tcx>(
         .enumerate()
         .skip(1) // skip `Self` implicit arg
         .all(|(arg_index, arg)| {
-            if [implied_by_generics.host_effect_index, implied_generics.host_effect_index].contains(&Some(arg_index)) {
+            if [
+                implied_by_generics.host_effect_index,
+                implied_generics.host_effect_index,
+            ]
+            .contains(&Some(arg_index))
+            {
                 // skip host effect params in determining whether generics are same
                 return true;
             }
diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs
new file mode 100644
index 00000000000..955f90d4262
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs
@@ -0,0 +1,95 @@
+use crate::methods::method_call;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::peel_blocks;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::declare_lint_pass;
+use rustc_span::{sym, BytePos, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if both `.write(true)` and `.append(true)` methods are called
+    /// on a same `OpenOptions`.
+    ///
+    /// ### Why is this bad?
+    /// `.append(true)` already enables `write(true)`, making this one
+    /// superflous.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # use std::fs::OpenOptions;
+    /// let _ = OpenOptions::new()
+    ///            .write(true)
+    ///            .append(true)
+    ///            .create(true)
+    ///            .open("file.json");
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # use std::fs::OpenOptions;
+    /// let _ = OpenOptions::new()
+    ///            .append(true)
+    ///            .create(true)
+    ///            .open("file.json");
+    /// ```
+    #[clippy::version = "1.76.0"]
+    pub INEFFECTIVE_OPEN_OPTIONS,
+    suspicious,
+    "usage of both `write(true)` and `append(true)` on same `OpenOptions`"
+}
+
+declare_lint_pass!(IneffectiveOpenOptions => [INEFFECTIVE_OPEN_OPTIONS]);
+
+fn index_if_arg_is_boolean(args: &[Expr<'_>], call_span: Span) -> Option<Span> {
+    if let [arg] = args
+        && let ExprKind::Lit(lit) = peel_blocks(arg).kind
+        && lit.node == LitKind::Bool(true)
+    {
+        // The `.` is not included in the span so we cheat a little bit to include it as well.
+        Some(call_span.with_lo(call_span.lo() - BytePos(1)))
+    } else {
+        None
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let Some(("open", mut receiver, [_arg], _, _)) = method_call(expr) else {
+            return;
+        };
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+        match receiver_ty.peel_refs().kind() {
+            ty::Adt(adt, _) if cx.tcx.is_diagnostic_item(sym::FsOpenOptions, adt.did()) => {},
+            _ => return,
+        }
+
+        let mut append = None;
+        let mut write = None;
+
+        while let Some((name, recv, args, _, span)) = method_call(receiver) {
+            if name == "append" {
+                append = index_if_arg_is_boolean(args, span);
+            } else if name == "write" {
+                write = index_if_arg_is_boolean(args, span);
+            }
+            receiver = recv;
+        }
+
+        if let Some(write_span) = write
+            && append.is_some()
+        {
+            span_lint_and_sugg(
+                cx,
+                INEFFECTIVE_OPEN_OPTIONS,
+                write_span,
+                "unnecessary use of `.write(true)` because there is `.append(true)`",
+                "remove `.write(true)`",
+                String::new(),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 83af551fcd3..8c032b17023 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -8,8 +8,8 @@ use rustc_hir::def::Res;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::{
     AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind,
-    ImplicitSelfKind, Item, ItemKind, LangItem, Mutability, Node, PatKind, PathSegment, PrimTy, QPath, TraitItemRef,
-    TyKind, TypeBindingKind,
+    ImplicitSelfKind, Item, ItemKind, Mutability, Node, PatKind, PathSegment, PrimTy, QPath, TraitItemRef,
+    TyKind, TypeBindingKind, OpaqueTyOrigin,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@@ -289,8 +289,10 @@ fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&
             kind: ItemKind::OpaqueTy(opaque),
             ..
         } = item
-        && opaque.bounds.len() == 1
-        && let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0]
+        && let OpaqueTyOrigin::AsyncFn(_) = opaque.origin
+        && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds
+        && let Some(segment) = trait_ref.trait_ref.path.segments.last()
+        && let Some(generic_args) = segment.args
         && generic_args.bindings.len() == 1
         && let TypeBindingKind::Equality {
             term:
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 1c59b2df853..7758d6a58e6 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -50,6 +50,8 @@ extern crate clippy_utils;
 #[macro_use]
 extern crate declare_clippy_lint;
 
+use std::collections::BTreeMap;
+
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::{Lint, LintId};
 
@@ -74,7 +76,7 @@ mod assertions_on_result_states;
 mod async_yields_async;
 mod attrs;
 mod await_holding_invalid;
-mod blocks_in_if_conditions;
+mod blocks_in_conditions;
 mod bool_assert_comparison;
 mod bool_to_int_with_if;
 mod booleans;
@@ -153,6 +155,7 @@ mod implied_bounds_in_impls;
 mod inconsistent_struct_constructor;
 mod index_refutable_slice;
 mod indexing_slicing;
+mod ineffective_open_options;
 mod infinite_iter;
 mod inherent_impl;
 mod inherent_to_string;
@@ -289,6 +292,7 @@ mod ref_option_ref;
 mod ref_patterns;
 mod reference;
 mod regex;
+mod repeat_vec_with_capacity;
 mod reserve_after_initialization;
 mod return_self_not_must_use;
 mod returns;
@@ -325,6 +329,7 @@ mod tuple_array_conversions;
 mod types;
 mod undocumented_unsafe_blocks;
 mod unicode;
+mod uninhabited_references;
 mod uninit_vec;
 mod unit_return_expecting_ord;
 mod unit_types;
@@ -653,7 +658,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default());
     store.register_late_pass(|_| Box::new(len_zero::LenZero));
     store.register_late_pass(|_| Box::new(attrs::Attributes));
-    store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
+    store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions));
     store.register_late_pass(|_| Box::new(unicode::Unicode));
     store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
     store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
@@ -722,6 +727,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         Box::new(vec::UselessVec {
             too_large_for_stack,
             msrv: msrv(),
+            span_to_lint_map: BTreeMap::new(),
         })
     });
     store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
@@ -1069,6 +1075,9 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
     store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
     store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes));
+    store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity));
+    store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences));
+    store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
new file mode 100644
index 00000000000..9b88dd76e5c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
@@ -0,0 +1,125 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{fn_def_id, is_lint_allowed};
+use hir::intravisit::{walk_expr, Visitor};
+use hir::{Expr, ExprKind, FnRetTy, FnSig, Node};
+use rustc_ast::Label;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::INFINITE_LOOP;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'_>,
+    loop_block: &'tcx hir::Block<'_>,
+    label: Option<Label>,
+) {
+    if is_lint_allowed(cx, INFINITE_LOOP, expr.hir_id) {
+        return;
+    }
+
+    // Skip check if this loop is not in a function/method/closure. (In some weird case)
+    let Some(parent_fn_ret) = get_parent_fn_ret_ty(cx, expr) else {
+        return;
+    };
+    // Or, its parent function is already returning `Never`
+    if matches!(
+        parent_fn_ret,
+        FnRetTy::Return(hir::Ty {
+            kind: hir::TyKind::Never,
+            ..
+        })
+    ) {
+        return;
+    }
+
+    let mut loop_visitor = LoopVisitor {
+        cx,
+        label,
+        is_finite: false,
+        loop_depth: 0,
+    };
+    loop_visitor.visit_block(loop_block);
+
+    let is_finite_loop = loop_visitor.is_finite;
+
+    if !is_finite_loop {
+        span_lint_and_then(cx, INFINITE_LOOP, expr.span, "infinite loop detected", |diag| {
+            if let FnRetTy::DefaultReturn(ret_span) = parent_fn_ret {
+                diag.span_suggestion(
+                    ret_span,
+                    "if this is intentional, consider specifing `!` as function return",
+                    " -> !",
+                    Applicability::MaybeIncorrect,
+                );
+            } else {
+                diag.help("if this is not intended, try adding a `break` or `return` condition in the loop");
+            }
+        });
+    }
+}
+
+fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<FnRetTy<'tcx>> {
+    for (_, parent_node) in cx.tcx.hir().parent_iter(expr.hir_id) {
+        match parent_node {
+            Node::Item(hir::Item {
+                kind: hir::ItemKind::Fn(FnSig { decl, .. }, _, _),
+                ..
+            })
+            | Node::TraitItem(hir::TraitItem {
+                kind: hir::TraitItemKind::Fn(FnSig { decl, .. }, _),
+                ..
+            })
+            | Node::ImplItem(hir::ImplItem {
+                kind: hir::ImplItemKind::Fn(FnSig { decl, .. }, _),
+                ..
+            })
+            | Node::Expr(Expr {
+                kind: ExprKind::Closure(hir::Closure { fn_decl: decl, .. }),
+                ..
+            }) => return Some(decl.output),
+            _ => (),
+        }
+    }
+    None
+}
+
+struct LoopVisitor<'hir, 'tcx> {
+    cx: &'hir LateContext<'tcx>,
+    label: Option<Label>,
+    loop_depth: usize,
+    is_finite: bool,
+}
+
+impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> {
+    fn visit_expr(&mut self, ex: &'hir Expr<'_>) {
+        match &ex.kind {
+            ExprKind::Break(hir::Destination { label, .. }, ..) => {
+                // Assuming breaks the loop when `loop_depth` is 0,
+                // as it could only means this `break` breaks current loop or any of its upper loop.
+                // Or, the depth is not zero but the label is matched.
+                if self.loop_depth == 0 || (label.is_some() && *label == self.label) {
+                    self.is_finite = true;
+                }
+            },
+            ExprKind::Ret(..) => self.is_finite = true,
+            ExprKind::Loop(..) => {
+                self.loop_depth += 1;
+                walk_expr(self, ex);
+                self.loop_depth = self.loop_depth.saturating_sub(1);
+            },
+            _ => {
+                // Calls to a function that never return
+                if let Some(did) = fn_def_id(self.cx, ex) {
+                    let fn_ret_ty = self.cx.tcx.fn_sig(did).skip_binder().output().skip_binder();
+                    if fn_ret_ty.is_never() {
+                        self.is_finite = true;
+                        return;
+                    }
+                }
+                walk_expr(self, ex);
+            },
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 892336878c7..3c9bde86bb6 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -3,6 +3,7 @@ mod explicit_counter_loop;
 mod explicit_into_iter_loop;
 mod explicit_iter_loop;
 mod for_kv_map;
+mod infinite_loop;
 mod iter_next_loop;
 mod manual_find;
 mod manual_flatten;
@@ -635,6 +636,48 @@ declare_clippy_lint! {
     "checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for infinite loops in a function where the return type is not `!`
+    /// and lint accordingly.
+    ///
+    /// ### Why is this bad?
+    /// A loop should be gently exited somewhere, or at least mark its parent function as
+    /// never return (`!`).
+    ///
+    /// ### Example
+    /// ```no_run,ignore
+    /// fn run_forever() {
+    ///     loop {
+    ///         // do something
+    ///     }
+    /// }
+    /// ```
+    /// If infinite loops are as intended:
+    /// ```no_run,ignore
+    /// fn run_forever() -> ! {
+    ///     loop {
+    ///         // do something
+    ///     }
+    /// }
+    /// ```
+    /// Otherwise add a `break` or `return` condition:
+    /// ```no_run,ignore
+    /// fn run_forever() {
+    ///     loop {
+    ///         // do something
+    ///         if condition {
+    ///             break;
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.75.0"]
+    pub INFINITE_LOOP,
+    restriction,
+    "possibly unintended infinite loop"
+}
+
 pub struct Loops {
     msrv: Msrv,
     enforce_iter_loop_reborrow: bool,
@@ -669,6 +712,7 @@ impl_lint_pass!(Loops => [
     MANUAL_FIND,
     MANUAL_WHILE_LET_SOME,
     UNUSED_ENUMERATE_INDEX,
+    INFINITE_LOOP,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -707,10 +751,11 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
         // check for `loop { if let {} else break }` that could be `while let`
         // (also matches an explicit "match" instead of "if let")
         // (even if the "match" or "if let" is used for declaration)
-        if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind {
+        if let ExprKind::Loop(block, label, LoopSource::Loop, _) = expr.kind {
             // also check for empty `loop {}` statements, skipping those in #[panic_handler]
             empty_loop::check(cx, expr, block);
             while_let_loop::check(cx, expr, block);
+            infinite_loop::check(cx, expr, block, label);
         }
 
         while_let_on_iterator::check(cx, expr);
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 28abecd0429..4acf46f73c9 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -13,8 +13,7 @@ use rustc_lint::LateContext;
 use rustc_middle::middle::region;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::{sym, Symbol};
-use std::iter::{self};
-use std::mem;
+use std::{iter, mem};
 
 /// Checks for looping over a range and then indexing a sequence with it.
 /// The iteratee must be a range literal.
diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
index 31cfb41640d..72cf1d7a354 100644
--- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -105,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
             // case somebody does that for some reason
             && (is_infinity(const_1) && is_neg_infinity(const_2)
                 || is_neg_infinity(const_1) && is_infinity(const_2))
-            && !is_from_proc_macro(cx, expr)
             && let Some(local_snippet) = snippet_opt(cx, first.span)
         {
             let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
@@ -113,6 +112,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
                 (BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => Variant::ManualIsFinite,
                 _ => return,
             };
+            if is_from_proc_macro(cx, expr) {
+                return;
+            }
 
             span_lint_and_then(cx, variant.lint(), expr.span, variant.msg(), |diag| {
                 match variant {
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
index 737c70496c2..781db4b97f0 100644
--- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
@@ -108,18 +108,16 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_
 
     let arg_kind = &args[0].kind;
     if let ExprKind::Path(qpath) = &func.kind {
-        if let QPath::TypeRelative(_, _) = qpath {
-            // String::from(...) or String::try_from(...)
-            if let QPath::TypeRelative(ty, path_seg) = qpath
-                && [sym::from, sym::try_from].contains(&path_seg.ident.name)
-                && let TyKind::Path(qpath) = &ty.kind
-                && let QPath::Resolved(_, path) = qpath
-                && let [path_seg] = path.segments
-                && path_seg.ident.name == sym::String
-                && is_expr_kind_empty_str(arg_kind)
-            {
-                warn_then_suggest(cx, span);
-            }
+        // String::from(...) or String::try_from(...)
+        if let QPath::TypeRelative(ty, path_seg) = qpath
+            && [sym::from, sym::try_from].contains(&path_seg.ident.name)
+            && let TyKind::Path(qpath) = &ty.kind
+            && let QPath::Resolved(_, path) = qpath
+            && let [path_seg] = path.segments
+            && path_seg.ident.name == sym::String
+            && is_expr_kind_empty_str(arg_kind)
+        {
+            warn_then_suggest(cx, span);
         } else if let QPath::Resolved(_, path) = qpath {
             // From::from(...) or TryFrom::try_from(...)
             if let [path_seg1, path_seg2] = path.segments
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 25c681bb9d9..b4f60ffadd7 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -3906,7 +3906,7 @@ impl_lint_pass!(Methods => [
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
-fn method_call<'tcx>(
+pub fn method_call<'tcx>(
     recv: &'tcx hir::Expr<'tcx>,
 ) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> {
     if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 4a651396f14..4429f032605 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -19,10 +19,6 @@ pub(super) fn check<'tcx>(
     arg: &'tcx hir::Expr<'_>,
     simplify_using: &str,
 ) {
-    if is_from_proc_macro(cx, expr) {
-        return;
-    }
-
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
     let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
@@ -32,7 +28,7 @@ pub(super) fn check<'tcx>(
             let body = cx.tcx.hir().body(body);
             let body_expr = &body.value;
 
-            if usage::BindingUsageFinder::are_params_used(cx, body) {
+            if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
                 return;
             }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index 395b22ebc5d..c4775b6bd04 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -15,8 +15,7 @@ use rustc_lint::LateContext;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 use rustc_middle::ty::{
-    self, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate,
-    TraitPredicate, Ty,
+    self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty,
 };
 use rustc_span::{sym, Symbol};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -359,6 +358,7 @@ fn get_input_traits_and_projections<'tcx>(
     (trait_predicates, projection_predicates)
 }
 
+#[expect(clippy::too_many_lines)]
 fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
     for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
         match node {
@@ -387,22 +387,21 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                 if let Some((callee_def_id, call_generic_args, recv, call_args)) =
                     get_callee_generic_args_and_args(cx, parent_expr)
                 {
-                    // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually
-                    // call `tcx.try_instantiate_and_normalize_erasing_regions` further down
-                    // (i.e., we are explicitly not in the identity context).
-                    let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder();
+                    let bound_fn_sig = cx.tcx.fn_sig(callee_def_id);
+                    let fn_sig = bound_fn_sig.skip_binder();
                     if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
-                        && let Some(param_ty) = fn_sig.inputs().get(arg_index)
-                        && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
+                        && let param_ty = fn_sig.input(arg_index).skip_binder()
+                        && let ty::Param(ParamTy { index: param_index , ..}) = *param_ty.kind()
                         // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021
-                        && (*param_index as usize) < call_generic_args.len()
+                        && (param_index as usize) < call_generic_args.len()
                     {
                         if fn_sig
+                            .skip_binder()
                             .inputs()
                             .iter()
                             .enumerate()
                             .filter(|(i, _)| *i != arg_index)
-                            .any(|(_, ty)| ty.contains(*param_ty))
+                            .any(|(_, ty)| ty.contains(param_ty))
                         {
                             return false;
                         }
@@ -414,7 +413,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                                 .iter()
                                 .filter(|predicate| {
                                     if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
-                                        && trait_predicate.trait_ref.self_ty() == *param_ty
+                                        && trait_predicate.trait_ref.self_ty() == param_ty
                                     {
                                         true
                                     } else {
@@ -425,7 +424,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                         let new_subst = cx
                             .tcx
                             .mk_args_from_iter(call_generic_args.iter().enumerate().map(|(i, t)| {
-                                if i == (*param_index as usize) {
+                                if i == param_index as usize {
                                     GenericArg::from(ty)
                                 } else {
                                     t
@@ -433,7 +432,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                             }));
 
                         if trait_predicates.any(|predicate| {
-                            let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst);
+                            let predicate = bound_fn_sig.rebind(predicate).instantiate(cx.tcx, new_subst);
                             let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
                             !cx.tcx
                                 .infer_ctxt()
@@ -443,12 +442,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                             return false;
                         }
 
-                        let output_ty = fn_sig.output();
-                        if output_ty.contains(*param_ty) {
+                        let output_ty = cx.tcx.instantiate_bound_regions_with_erased(fn_sig.output());
+                        if output_ty.contains(param_ty) {
                             if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions(
                                 new_subst,
                                 cx.param_env,
-                                EarlyBinder::bind(output_ty),
+                                bound_fn_sig.rebind(output_ty),
                             ) {
                                 expr = parent_expr;
                                 ty = new_ty;
diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
index 8f2a5390781..0f18e943451 100644
--- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -52,7 +52,7 @@ declare_clippy_lint! {
     /// Use instead:
     /// ```no_run
     /// fn sum(v: &[u8]) -> u8 {
-    ///     assert!(v.len() > 4);
+    ///     assert!(v.len() > 3);
     ///     // no bounds checks
     ///     v[0] + v[1] + v[2] + v[3]
     /// }
@@ -87,11 +87,14 @@ enum LengthComparison {
     LengthLessThanOrEqualInt,
     /// `5 <= v.len()`
     IntLessThanOrEqualLength,
+    /// `5 == v.len()`
+    /// `v.len() == 5`
+    LengthEqualInt,
 }
 
 /// Extracts parts out of a length comparison expression.
 ///
-/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, `v.len()`))`
+/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, v.len()))`
 fn len_comparison<'hir>(
     bin_op: BinOp,
     left: &'hir Expr<'hir>,
@@ -114,6 +117,8 @@ fn len_comparison<'hir>(
         (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, *right as usize, left)),
         (Rel::Le, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanOrEqualLength, *left as usize, right)),
         (Rel::Le, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanOrEqualInt, *right as usize, left)),
+        (Rel::Eq, int_lit_pat!(left), _) => Some((LengthComparison::LengthEqualInt, *left as usize, right)),
+        (Rel::Eq, _, int_lit_pat!(right)) => Some((LengthComparison::LengthEqualInt, *right as usize, left)),
         _ => None,
     }
 }
@@ -316,11 +321,11 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
                 continue;
             };
 
-            match entry {
+            match *entry {
                 IndexEntry::AssertWithIndex {
                     highest_index,
                     asserted_len,
-                    indexes,
+                    ref indexes,
                     comparison,
                     assert_span,
                     slice,
@@ -343,6 +348,12 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
                             "assert!({}.len() > {highest_index})",
                             snippet(cx, slice.span, "..")
                         )),
+                        // `highest_index` here is rather a length, so we need to add 1 to it
+                        LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => Some(format!(
+                            "assert!({}.len() == {})",
+                            snippet(cx, slice.span, ".."),
+                            highest_index + 1
+                        )),
                         _ => None,
                     };
 
@@ -354,7 +365,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
                             indexes,
                             |diag| {
                                 diag.span_suggestion(
-                                    *assert_span,
+                                    assert_span,
                                     "provide the highest index that is indexed with",
                                     sugg,
                                     Applicability::MachineApplicable,
@@ -364,7 +375,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
                     }
                 },
                 IndexEntry::IndexWithoutAssert {
-                    indexes,
+                    ref indexes,
                     highest_index,
                     slice,
                 } if indexes.len() > 1 => {
diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
index 34119c6911c..3ff40081c47 100644
--- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -213,7 +213,9 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
         if parent_id == cur_id {
             break;
         }
-        let Some(parent_node) = vis.cx.tcx.opt_hir_node(parent_id) else { break };
+        let Some(parent_node) = vis.cx.tcx.opt_hir_node(parent_id) else {
+            break;
+        };
 
         let stop_early = match parent_node {
             Node::Expr(expr) => check_expr(vis, expr),
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
index 85166b0dd95..a32bca3d038 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::is_copy;
+use clippy_utils::ty::{implements_trait, is_copy};
 use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -169,6 +169,7 @@ fn needless_borrow_count<'tcx>(
 ) -> usize {
     let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
     let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
+    let drop_trait_def_id = cx.tcx.lang_items().drop_trait();
 
     let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
     let predicates = cx.tcx.param_env(fn_id).caller_bounds();
@@ -223,7 +224,14 @@ fn needless_borrow_count<'tcx>(
     // elements are modified each time `check_referent` is called.
     let mut args_with_referent_ty = callee_args.to_vec();
 
-    let mut check_reference_and_referent = |reference, referent| {
+    let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| {
+        if let ExprKind::Field(base, _) = &referent.kind {
+            let base_ty = cx.typeck_results().expr_ty(base);
+            if drop_trait_def_id.map_or(false, |id| implements_trait(cx, base_ty, id, &[])) {
+                return false;
+            }
+        }
+
         let referent_ty = cx.typeck_results().expr_ty(referent);
 
         if !is_copy(cx, referent_ty)
diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_if.rs
index 41d05d72284..51bee4b51f6 100644
--- a/src/tools/clippy/clippy_lints/src/needless_if.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_if.rs
@@ -44,7 +44,6 @@ impl LateLintPass<'_> for NeedlessIf {
             && block.stmts.is_empty()
             && block.expr.is_none()
             && !in_external_macro(cx.sess(), expr.span)
-            && !is_from_proc_macro(cx, expr)
             && let Some(then_snippet) = snippet_opt(cx, then.span)
             // Ignore
             // - empty macro expansions
@@ -53,6 +52,7 @@ impl LateLintPass<'_> for NeedlessIf {
             // - #[cfg]'d out code
             && then_snippet.chars().all(|ch| matches!(ch, '{' | '}') || ch.is_ascii_whitespace())
             && let Some(cond_snippet) = snippet_opt(cx, cond.span)
+            && !is_from_proc_macro(cx, expr)
         {
             span_lint_and_sugg(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 6e65dd628a4..5978da83199 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -87,6 +87,17 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
 
 fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
     if let StmtKind::Semi(expr) = stmt.kind {
+        // move `expr.span.from_expansion()` ahead
+        if expr.span.from_expansion() {
+            return false;
+        }
+        let expr = peel_blocks(expr);
+
+        if is_operator_overridden(cx, expr) {
+            // Return `true`, to prevent `check_unnecessary_operation` from
+            // linting on this statement as well.
+            return true;
+        }
         if has_no_effect(cx, expr) {
             span_lint_hir_and_then(
                 cx,
@@ -153,11 +164,26 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
     false
 }
 
-fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    if expr.span.from_expansion() {
-        return false;
+fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    // It's very hard or impossable to check whether overridden operator have side-effect this lint.
+    // So, this function assume user-defined operator is overridden with an side-effect.
+    // The definition of user-defined structure here is ADT-type,
+    // Althrough this will weaken the ability of this lint, less error lint-fix happen.
+    match expr.kind {
+        ExprKind::Binary(..) | ExprKind::Unary(..) => {
+            // No need to check type of `lhs` and `rhs`
+            // because if the operator is overridden, at least one operand is ADT type
+
+            // reference: rust/compiler/rustc_middle/src/ty/typeck_results.rs: `is_method_call`.
+            // use this function to check whether operator is overridden in `ExprKind::{Binary, Unary}`.
+            cx.typeck_results().is_method_call(expr)
+        },
+        _ => false,
     }
-    match peel_blocks(expr).kind {
+}
+
+fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match expr.kind {
         ExprKind::Lit(..) | ExprKind::Closure { .. } => true,
         ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
         ExprKind::Index(a, b, _) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 1a2b20bf438..c081dec9b6b 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -132,7 +132,11 @@ impl ArithmeticSideEffects {
     }
 
     // Common entry-point to avoid code duplication.
-    fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
+    fn issue_lint<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if is_from_proc_macro(cx, expr) {
+            return;
+        }
+
         let msg = "arithmetic operation that can potentially result in unexpected side-effects";
         span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg);
         self.expr_span = Some(expr.span);
@@ -160,10 +164,10 @@ impl ArithmeticSideEffects {
     fn manage_bin_ops<'tcx>(
         &mut self,
         cx: &LateContext<'tcx>,
-        expr: &hir::Expr<'tcx>,
+        expr: &'tcx hir::Expr<'_>,
         op: &Spanned<hir::BinOpKind>,
-        lhs: &hir::Expr<'tcx>,
-        rhs: &hir::Expr<'tcx>,
+        lhs: &'tcx hir::Expr<'_>,
+        rhs: &'tcx hir::Expr<'_>,
     ) {
         if constant_simple(cx, cx.typeck_results(), expr).is_some() {
             return;
@@ -236,10 +240,10 @@ impl ArithmeticSideEffects {
     /// provided input.
     fn manage_method_call<'tcx>(
         &mut self,
-        args: &[hir::Expr<'tcx>],
+        args: &'tcx [hir::Expr<'_>],
         cx: &LateContext<'tcx>,
-        ps: &hir::PathSegment<'tcx>,
-        receiver: &hir::Expr<'tcx>,
+        ps: &'tcx hir::PathSegment<'_>,
+        receiver: &'tcx hir::Expr<'_>,
     ) {
         let Some(arg) = args.first() else {
             return;
@@ -264,8 +268,8 @@ impl ArithmeticSideEffects {
     fn manage_unary_ops<'tcx>(
         &mut self,
         cx: &LateContext<'tcx>,
-        expr: &hir::Expr<'tcx>,
-        un_expr: &hir::Expr<'tcx>,
+        expr: &'tcx hir::Expr<'_>,
+        un_expr: &'tcx hir::Expr<'_>,
         un_op: hir::UnOp,
     ) {
         let hir::UnOp::Neg = un_op else {
@@ -287,14 +291,13 @@ impl ArithmeticSideEffects {
 
     fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool {
         is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id)
-            || is_from_proc_macro(cx, expr)
             || self.expr_span.is_some()
             || self.const_span.map_or(false, |sp| sp.contains(expr.span))
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
         if self.should_skip_expr(cx, expr) {
             return;
         }
diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
index 9f5897d2ca7..85979903b58 100644
--- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
@@ -4,8 +4,9 @@
 pub static RENAMED_LINTS: &[(&str, &str)] = &[
     ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"),
     ("clippy::blacklisted_name", "clippy::disallowed_names"),
-    ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
-    ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
+    ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"),
+    ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"),
+    ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"),
     ("clippy::box_vec", "clippy::box_collection"),
     ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
     ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
new file mode 100644
index 00000000000..5a4933a3fce
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
@@ -0,0 +1,114 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher::VecArgs;
+use clippy_utils::macros::root_macro_call;
+use clippy_utils::source::snippet;
+use clippy_utils::{expr_or_init, fn_def_id, match_def_path, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for patterns such as `vec![Vec::with_capacity(x); n]` or `iter::repeat(Vec::with_capacity(x))`.
+    ///
+    /// ### Why is this bad?
+    /// These constructs work by cloning the element, but cloning a `Vec<_>` does not
+    /// respect the old vector's capacity and effectively discards it.
+    ///
+    /// This makes `iter::repeat(Vec::with_capacity(x))` especially suspicious because the user most certainly
+    /// expected that the yielded `Vec<_>` will have the requested capacity, otherwise one can simply write
+    /// `iter::repeat(Vec::new())` instead and it will have the same effect.
+    ///
+    /// Similarly for `vec![x; n]`, the element `x` is cloned to fill the vec.
+    /// Unlike `iter::repeat` however, the vec repeat macro does not have to clone the value `n` times
+    /// but just `n - 1` times, because it can reuse the passed value for the last slot.
+    /// That means that the last `Vec<_>` gets the requested capacity but all other ones do not.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::iter;
+    ///
+    /// let _: Vec<Vec<u8>> = vec![Vec::with_capacity(42); 123];
+    /// let _: Vec<Vec<u8>> = iter::repeat(Vec::with_capacity(42)).take(123).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::iter;
+    ///
+    /// let _: Vec<Vec<u8>> = iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect();
+    /// //                                      ^^^ this closure executes 123 times
+    /// //                                          and the vecs will have the expected capacity
+    /// ```
+    #[clippy::version = "1.74.0"]
+    pub REPEAT_VEC_WITH_CAPACITY,
+    suspicious,
+    "repeating a `Vec::with_capacity` expression which does not retain capacity"
+}
+
+declare_lint_pass!(RepeatVecWithCapacity => [REPEAT_VEC_WITH_CAPACITY]);
+
+fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, sugg_msg: &'static str, sugg: String) {
+    span_lint_and_then(
+        cx,
+        REPEAT_VEC_WITH_CAPACITY,
+        span,
+        &format!("repeating `Vec::with_capacity` using `{kind}`, which does not retain capacity"),
+        |diag| {
+            diag.note(note);
+            diag.span_suggestion_verbose(span, sugg_msg, sugg, Applicability::MaybeIncorrect);
+        },
+    );
+}
+
+/// Checks `vec![Vec::with_capacity(x); n]`
+fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if let Some(mac_call) = root_macro_call(expr.span)
+        && cx.tcx.is_diagnostic_item(sym::vec_macro, mac_call.def_id)
+        && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr)
+        && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY))
+        && !len_expr.span.from_expansion()
+        && let Some(Constant::Int(2..)) = constant(cx, cx.typeck_results(), expr_or_init(cx, len_expr))
+    {
+        emit_lint(
+            cx,
+            expr.span.source_callsite(),
+            "vec![x; n]",
+            "only the last `Vec` will have the capacity",
+            "if you intended to initialize multiple `Vec`s with an initial capacity, try",
+            format!(
+                "(0..{}).map(|_| {}).collect::<Vec<_>>()",
+                snippet(cx, len_expr.span, ""),
+                snippet(cx, repeat_expr.span, "..")
+            ),
+        );
+    }
+}
+
+/// Checks `iter::repeat(Vec::with_capacity(x))`
+fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if !expr.span.from_expansion()
+        && fn_def_id(cx, expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::iter_repeat, did))
+        && let ExprKind::Call(_, [repeat_expr]) = expr.kind
+        && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY))
+        && !repeat_expr.span.from_expansion()
+    {
+        emit_lint(
+            cx,
+            expr.span,
+            "iter::repeat",
+            "none of the yielded `Vec`s will have the requested capacity",
+            "if you intended to create an iterator that yields `Vec`s with an initial capacity, try",
+            format!("std::iter::repeat_with(|| {})", snippet(cx, repeat_expr.span, "..")),
+        );
+    }
+}
+
+impl LateLintPass<'_> for RepeatVecWithCapacity {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        check_vec_macro(cx, expr);
+        check_repeat_fn(cx, expr);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 1316343e0c2..7a351dab2d4 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
             if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. })
                 && let item = cx.tcx.hir().item(id)
                 && let ItemKind::Impl(Impl {
-                  items,
-                  of_trait,
-                  self_ty,
-                  ..
-                                      }) = &item.kind
+                    items,
+                    of_trait,
+                    self_ty,
+                    ..
+                }) = &item.kind
                 && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
             {
                 if !map.contains_key(res) {
@@ -75,24 +75,24 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
 
                 match of_trait {
                     Some(trait_ref) => {
-                        let mut methods_in_trait: BTreeSet<Symbol> = if let Some(Node::TraitRef(TraitRef {
-                            path, ..
-                        })) = cx.tcx.opt_hir_node(trait_ref.hir_ref_id)
-                            && let Res::Def(DefKind::Trait, did) = path.res
-                        {
-                            // FIXME: if
-                            // `rustc_middle::ty::assoc::AssocItems::items` is public,
-                            // we can iterate its keys instead of `in_definition_order`,
-                            // which's more efficient
-                            cx.tcx
-                                .associated_items(did)
-                                .in_definition_order()
-                                .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn))
-                                .map(|assoc_item| assoc_item.name)
-                                .collect()
-                        } else {
-                            BTreeSet::new()
-                        };
+                        let mut methods_in_trait: BTreeSet<Symbol> =
+                            if let Some(Node::TraitRef(TraitRef { path, .. })) =
+                                cx.tcx.opt_hir_node(trait_ref.hir_ref_id)
+                                && let Res::Def(DefKind::Trait, did) = path.res
+                            {
+                                // FIXME: if
+                                // `rustc_middle::ty::assoc::AssocItems::items` is public,
+                                // we can iterate its keys instead of `in_definition_order`,
+                                // which's more efficient
+                                cx.tcx
+                                    .associated_items(did)
+                                    .in_definition_order()
+                                    .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn))
+                                    .map(|assoc_item| assoc_item.name)
+                                    .collect()
+                            } else {
+                                BTreeSet::new()
+                            };
 
                         let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
                             if let Some((impl_span, hir_id)) = existing_name.impl_methods.get(&method_name) {
diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
index 396d2717a13..8e181c3ccc7 100644
--- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
@@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn {
     ) {
         if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id)
             || in_external_macro(cx.sess(), span)
-            || is_from_proc_macro(cx, &(&kind, body, cx.tcx.local_def_id_to_hir_id(def_id), span))
             || is_in_test_function(cx.tcx, body.value.hir_id)
+            || is_from_proc_macro(cx, &(&kind, body, cx.tcx.local_def_id_to_hir_id(def_id), span))
         {
             return;
         }
diff --git a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
new file mode 100644
index 00000000000..d41576cadad
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
@@ -0,0 +1,84 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, TyKind, UnOp};
+use rustc_hir_analysis::hir_ty_to_ty;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::declare_lint_pass;
+use rustc_span::def_id::LocalDefId;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It detects references to uninhabited types, such as `!` and
+    /// warns when those are either dereferenced or returned from a function.
+    ///
+    /// ### Why is this bad?
+    /// Dereferencing a reference to an uninhabited type would create
+    /// an instance of such a type, which cannot exist. This constitutes
+    /// undefined behaviour. Such a reference could have been created
+    /// by `unsafe` code.
+    ///
+    /// ### Example
+    /// The following function can return a reference to an uninhabited type
+    /// (`Infallible`) because it uses `unsafe` code to create it. However,
+    /// the user of such a function could dereference the return value and
+    /// trigger an undefined behavior from safe code.
+    ///
+    /// ```no_run
+    /// fn create_ref() -> &'static std::convert::Infallible {
+    ///     unsafe { std::mem::transmute(&()) }
+    /// }
+    /// ```
+    #[clippy::version = "1.76.0"]
+    pub UNINHABITED_REFERENCES,
+    suspicious,
+    "reference to uninhabited type"
+}
+
+declare_lint_pass!(UninhabitedReferences => [UNINHABITED_REFERENCES]);
+
+impl LateLintPass<'_> for UninhabitedReferences {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
+        if in_external_macro(cx.tcx.sess, expr.span) {
+            return;
+        }
+
+        if let ExprKind::Unary(UnOp::Deref, _) = expr.kind {
+            let ty = cx.typeck_results().expr_ty_adjusted(expr);
+            if ty.is_privately_uninhabited(cx.tcx, cx.param_env) {
+                span_lint(
+                    cx,
+                    UNINHABITED_REFERENCES,
+                    expr.span,
+                    "dereferencing a reference to an uninhabited type is undefined behavior",
+                );
+            }
+        }
+    }
+
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'_>,
+        kind: FnKind<'_>,
+        fndecl: &'_ FnDecl<'_>,
+        _: &'_ Body<'_>,
+        span: Span,
+        _: LocalDefId,
+    ) {
+        if in_external_macro(cx.tcx.sess, span) || matches!(kind, FnKind::Closure) {
+            return;
+        }
+        if let FnRetTy::Return(hir_ty) = fndecl.output
+            && let TyKind::Ref(_, mut_ty) = hir_ty.kind
+            && hir_ty_to_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env)
+        {
+            span_lint(
+                cx,
+                UNINHABITED_REFERENCES,
+                hir_ty.span,
+                "dereferencing a reference to an uninhabited type would be undefined behavior",
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
index 9979e02297e..2b0d2d61d20 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs
@@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
             };
             match constructor_symbol {
                 sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (),
-                sym::Err if path.ident.name == sym!(map_err) => (),
+                sym::Err if path.ident.name == sym::map_err => (),
                 _ => return,
             }
 
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 373b076f92c..fae1b90ace2 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -28,12 +28,12 @@ use rustc_span::{sym, Loc, Span, Symbol};
 use serde::ser::SerializeStruct;
 use serde::{Serialize, Serializer};
 use std::collections::{BTreeSet, BinaryHeap};
-use std::fmt;
 use std::fmt::Write as _;
 use std::fs::{self, File};
 use std::io::prelude::*;
 use std::path::{Path, PathBuf};
 use std::process::Command;
+use std::{env, fmt};
 
 /// This is the json output file of the lint collector.
 const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
@@ -415,7 +415,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root
 
     let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}");
 
-    let mut cmd = Command::new("cargo");
+    let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into()));
 
     cmd.current_dir(clippy_project_root)
         .env("CARGO_INCREMENTAL", "0")
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
index 31a81a52497..6e449dc9806 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
@@ -218,7 +218,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
         ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) {
             Res::Local(hir_id) => {
                 let parent_id = cx.tcx.hir().parent_id(hir_id);
-                if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir_node(parent_id) {
+                if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.hir_node(parent_id) {
                     path_to_matched_type(cx, init)
                 } else {
                     None
@@ -246,7 +246,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
 fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> {
     let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() {
         let &alloc = alloc.provenance().ptrs().values().next()?;
-        if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
+        if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) {
             (alloc.inner(), ty)
         } else {
             return None;
@@ -264,7 +264,7 @@ fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation
             .ptrs()
             .values()
             .map(|&alloc| {
-                if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
+                if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) {
                     let alloc = alloc.inner();
                     str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
                         .ok()
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index ba958c5b392..5e13c73f035 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -1,25 +1,27 @@
+use std::collections::BTreeMap;
 use std::ops::ControlFlow;
 
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_copy;
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{get_parent_expr, higher, is_trait_method};
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Node, PatKind};
+use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{sym, DesugaringKind, Span};
 
 #[expect(clippy::module_name_repetitions)]
 #[derive(Clone)]
 pub struct UselessVec {
     pub too_large_for_stack: u64,
     pub msrv: Msrv,
+    pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
 }
 
 declare_clippy_lint! {
@@ -69,64 +71,88 @@ pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 
 impl<'tcx> LateLintPass<'tcx> for UselessVec {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
-        if adjusts_to_slice(cx, expr)
-            && let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows())
-        {
-            let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind {
-                // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.)
-                (SuggestedType::SliceRef(mutability), expr.span)
-            } else {
-                // `expr` is the `vec![_]` expansion, so suggest `[_]`
-                // and also use the span of the actual `vec![_]` expression
-                (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site)
-            };
-
-            self.check_vec_macro(cx, &vec_args, span, suggest_slice);
-        }
+        if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) {
+            // search for `let foo = vec![_]` expressions where all uses of `foo`
+            // adjust to slices or call a method that exist on slices (e.g. len)
+            if let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id)
+                // for now ignore locals with type annotations.
+                // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
+                && local.ty.is_none()
+                && let PatKind::Binding(_, id, ..) = local.pat.kind
+                && is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr.peel_borrows())))
+            {
+                let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
+                    // allow indexing into a vec and some set of allowed method calls that exist on slices, too
+                    if let Some(parent) = get_parent_expr(cx, expr)
+                        && (adjusts_to_slice(cx, expr)
+                            || matches!(parent.kind, ExprKind::Index(..))
+                            || is_allowed_vec_method(cx, parent))
+                    {
+                        ControlFlow::Continue(())
+                    } else {
+                        ControlFlow::Break(())
+                    }
+                })
+                .is_continue();
 
-        // search for `let foo = vec![_]` expressions where all uses of `foo`
-        // adjust to slices or call a method that exist on slices (e.g. len)
-        if let Some(vec_args) = higher::VecArgs::hir(cx, expr)
-            && let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id)
-            // for now ignore locals with type annotations.
-            // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
-            && local.ty.is_none()
-            && let PatKind::Binding(_, id, ..) = local.pat.kind
-            && is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr)))
-        {
-            let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
-                // allow indexing into a vec and some set of allowed method calls that exist on slices, too
-                if let Some(parent) = get_parent_expr(cx, expr)
-                    && (adjusts_to_slice(cx, expr)
-                        || matches!(parent.kind, ExprKind::Index(..))
-                        || is_allowed_vec_method(cx, parent))
-                {
-                    ControlFlow::Continue(())
+                let span = expr.span.ctxt().outer_expn_data().call_site;
+                if only_slice_uses {
+                    self.check_vec_macro(cx, &vec_args, span, expr.hir_id, SuggestedType::Array);
                 } else {
-                    ControlFlow::Break(())
+                    self.span_to_lint_map.insert(span, None);
+                }
+            }
+            // if the local pattern has a specified type, do not lint.
+            else if let Some(_) = higher::VecArgs::hir(cx, expr)
+                && let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id)
+                && local.ty.is_some()
+            {
+                let span = expr.span.ctxt().outer_expn_data().call_site;
+                self.span_to_lint_map.insert(span, None);
+            }
+            // search for `for _ in vec![...]`
+            else if let Some(parent) = get_parent_expr(cx, expr)
+                && parent.span.is_desugaring(DesugaringKind::ForLoop)
+                && self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
+            {
+                // report the error around the `vec!` not inside `<std macros>:`
+                let span = expr.span.ctxt().outer_expn_data().call_site;
+                self.check_vec_macro(cx, &vec_args, span, expr.hir_id, SuggestedType::Array);
+            }
+            // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
+            else {
+                let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind {
+                    // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.)
+                    (SuggestedType::SliceRef(mutability), expr.span)
+                } else {
+                    // `expr` is the `vec![_]` expansion, so suggest `[_]`
+                    // and also use the span of the actual `vec![_]` expression
+                    (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site)
+                };
+
+                if adjusts_to_slice(cx, expr) {
+                    self.check_vec_macro(cx, &vec_args, span, expr.hir_id, suggest_slice);
+                } else {
+                    self.span_to_lint_map.insert(span, None);
                 }
-            })
-            .is_continue();
-
-            if only_slice_uses {
-                self.check_vec_macro(
-                    cx,
-                    &vec_args,
-                    expr.span.ctxt().outer_expn_data().call_site,
-                    SuggestedType::Array,
-                );
             }
         }
+    }
 
-        // search for `for _ in vec![…]`
-        if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr)
-            && let Some(vec_args) = higher::VecArgs::hir(cx, arg)
-            && self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
-        {
-            // report the error around the `vec!` not inside `<std macros>:`
-            let span = arg.span.ctxt().outer_expn_data().call_site;
-            self.check_vec_macro(cx, &vec_args, span, SuggestedType::Array);
+    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+        for (span, lint_opt) in &self.span_to_lint_map {
+            if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt {
+                let help_msg = format!(
+                    "you can use {} directly",
+                    match suggest_slice {
+                        SuggestedType::SliceRef(_) => "a slice",
+                        SuggestedType::Array => "an array",
+                    }
+                );
+                span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| {
+                    diag.span_suggestion(*span, help_msg, snippet, *applicability);
+                });
+            }
         }
     }
 
@@ -134,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
 }
 
 #[derive(Copy, Clone)]
-enum SuggestedType {
+pub(crate) enum SuggestedType {
     /// Suggest using a slice `&[..]` / `&mut [..]`
     SliceRef(Mutability),
     /// Suggest using an array: `[..]`
@@ -147,6 +173,7 @@ impl UselessVec {
         cx: &LateContext<'tcx>,
         vec_args: &higher::VecArgs<'tcx>,
         span: Span,
+        hir_id: HirId,
         suggest_slice: SuggestedType,
     ) {
         if span.from_expansion() {
@@ -204,21 +231,9 @@ impl UselessVec {
             },
         };
 
-        span_lint_and_sugg(
-            cx,
-            USELESS_VEC,
-            span,
-            "useless use of `vec!`",
-            &format!(
-                "you can use {} directly",
-                match suggest_slice {
-                    SuggestedType::SliceRef(_) => "a slice",
-                    SuggestedType::Array => "an array",
-                }
-            ),
-            snippet,
-            applicability,
-        );
+        self.span_to_lint_map
+            .entry(span)
+            .or_insert(Some((hir_id, suggest_slice, snippet, applicability)));
     }
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 47237de4fef..e36f2fa87a7 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -206,7 +206,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         ) => {
             eq_closure_binder(lb, rb)
                 && lc == rc
-                && la.map_or(false, |la| la.is_async()) == ra.map_or(false, |ra| ra.is_async())
+                && la.map_or(false, CoroutineKind::is_async) == ra.map_or(false, CoroutineKind::is_async)
                 && lm == rm
                 && eq_fn_decl(lf, rf)
                 && eq_expr(le, re)
@@ -564,13 +564,16 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
 }
 
 fn eq_opt_coroutine_kind(l: Option<CoroutineKind>, r: Option<CoroutineKind>) -> bool {
-    match (l, r) {
+    matches!(
+        (l, r),
         (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. }))
-        | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. }))
-        | (Some(CoroutineKind::AsyncGen { .. }), Some(CoroutineKind::AsyncGen { .. })) => true,
-        (None, None) => true,
-        _ => false,
-    }
+            | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. }))
+            | (
+                Some(CoroutineKind::AsyncGen { .. }),
+                Some(CoroutineKind::AsyncGen { .. })
+            )
+            | (None, None)
+    )
 }
 
 pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 3f68b6d75b5..470d31fa3e1 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -12,14 +12,14 @@
 //! code was written, and check if the span contains that text. Note this will only work correctly
 //! if the span is not from a `macro_rules` based macro.
 
-use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy};
+use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy};
 use rustc_ast::token::CommentKind;
 use rustc_ast::AttrStyle;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId, Impl, ImplItem,
-    ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem, TraitItemKind, Ty,
-    TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
+    Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl,
+    ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem,
+    TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
 };
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::TyCtxt;
@@ -33,8 +33,6 @@ use rustc_target::spec::abi::Abi;
 pub enum Pat {
     /// A single string.
     Str(&'static str),
-    /// A single string.
-    OwnedStr(String),
     /// Any of the given strings.
     MultiStr(&'static [&'static str]),
     /// Any of the given strings.
@@ -59,14 +57,12 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
         let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
         (match start_pat {
             Pat::Str(text) => start_str.starts_with(text),
-            Pat::OwnedStr(text) => start_str.starts_with(&text),
             Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
             Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
             Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
             Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
         } && match end_pat {
             Pat::Str(text) => end_str.ends_with(text),
-            Pat::OwnedStr(text) => end_str.starts_with(&text),
             Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
             Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
             Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
@@ -125,6 +121,8 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
 fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
     match e.kind {
         ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
+        // Parenthesis are trimmed from the text before the search patterns are matched.
+        // See: `span_matches_pat`
         ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
         ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
         ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
@@ -286,23 +284,17 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
 fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
     match attr.kind {
         AttrKind::Normal(..) => {
-            let mut pat = if matches!(attr.style, AttrStyle::Outer) {
-                (Pat::Str("#["), Pat::Str("]"))
-            } else {
-                (Pat::Str("#!["), Pat::Str("]"))
-            };
-
-            if let Some(ident) = attr.ident()
-                && let Pat::Str(old_pat) = pat.0
-            {
+            if let Some(ident) = attr.ident() {
                 // TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
                 // refactoring
                 // NOTE: This will likely have false positives, like `allow = 1`
-                pat.0 = Pat::OwnedMultiStr(vec![ident.to_string(), old_pat.to_owned()]);
-                pat.1 = Pat::Str("");
+                (
+                    Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]),
+                    Pat::Str(""),
+                )
+            } else {
+                (Pat::Str("#"), Pat::Str("]"))
             }
-
-            pat
         },
         AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
             if matches!(attr.style, AttrStyle::Outer) {
@@ -324,32 +316,42 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
 fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
     match ty.kind {
         TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
-        TyKind::Ptr(MutTy { mutbl, ty }) => (
-            if mutbl.is_mut() {
-                Pat::Str("*const")
-            } else {
-                Pat::Str("*mut")
-            },
-            ty_search_pat(ty).1,
-        ),
+        TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1),
         TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1),
         TyKind::BareFn(bare_fn) => (
-            Pat::OwnedStr(format!("{}{} fn", bare_fn.unsafety.prefix_str(), bare_fn.abi.name())),
-            ty_search_pat(ty).1,
+            if bare_fn.unsafety == Unsafety::Unsafe {
+                Pat::Str("unsafe")
+            } else if bare_fn.abi != Abi::Rust {
+                Pat::Str("extern")
+            } else {
+                Pat::MultiStr(&["fn", "extern"])
+            },
+            match bare_fn.decl.output {
+                FnRetTy::DefaultReturn(_) => {
+                    if let [.., ty] = bare_fn.decl.inputs {
+                        ty_search_pat(ty).1
+                    } else {
+                        Pat::Str("(")
+                    }
+                },
+                FnRetTy::Return(ty) => ty_search_pat(ty).1,
+            },
         ),
-        TyKind::Never => (Pat::Str("!"), Pat::Str("")),
-        TyKind::Tup(..) => (Pat::Str("("), Pat::Str(")")),
+        TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
+        // Parenthesis are trimmed from the text before the search patterns are matched.
+        // See: `span_matches_pat`
+        TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
+        TyKind::Tup([ty]) => ty_search_pat(ty),
+        TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1),
         TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
         TyKind::Path(qpath) => qpath_search_pat(&qpath),
-        // NOTE: This is missing `TraitObject`. It will always return true then.
+        TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
+        TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) => (Pat::Str("dyn"), Pat::Str("")),
+        // NOTE: `TraitObject` is incomplete. It will always return true then.
         _ => (Pat::Str(""), Pat::Str("")),
     }
 }
 
-fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
-    (Pat::OwnedStr(ident.name.as_str().to_owned()), Pat::Str(""))
-}
-
 pub trait WithSearchPat<'cx> {
     type Context: LintContext;
     fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
@@ -408,7 +410,7 @@ impl<'cx> WithSearchPat<'cx> for Ident {
     type Context = LateContext<'cx>;
 
     fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
-        ident_search_pat(*self)
+        (Pat::Sym(self.name), Pat::Sym(self.name))
     }
 
     fn span(&self) -> Span {
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index fa56e5b0ba2..7562961538e 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -49,7 +49,6 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
     #[expect(clippy::disallowed_methods)]
     cx.struct_span_lint(lint, sp, msg.to_string(), |diag| {
         docs_link(diag, lint);
-        diag
     });
 }
 
@@ -90,7 +89,6 @@ pub fn span_lint_and_help<T: LintContext>(
             diag.help(help.to_string());
         }
         docs_link(diag, lint);
-        diag
     });
 }
 
@@ -134,7 +132,6 @@ pub fn span_lint_and_note<T: LintContext>(
             diag.note(note);
         }
         docs_link(diag, lint);
-        diag
     });
 }
 
@@ -152,7 +149,6 @@ where
     cx.struct_span_lint(lint, sp, msg.to_string(), |diag| {
         f(diag);
         docs_link(diag, lint);
-        diag
     });
 }
 
@@ -160,7 +156,6 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s
     #[expect(clippy::disallowed_methods)]
     cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| {
         docs_link(diag, lint);
-        diag
     });
 }
 
@@ -176,7 +171,6 @@ pub fn span_lint_hir_and_then(
     cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| {
         f(diag);
         docs_link(diag, lint);
-        diag
     });
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 868c92f2f54..61d0663aa83 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -214,8 +214,17 @@ pub fn implements_trait<'tcx>(
     trait_id: DefId,
     args: &[GenericArg<'tcx>],
 ) -> bool {
-    let callee_id = cx.enclosing_body.map(|body| cx.tcx.hir().body_owner(body).owner.to_def_id());
-    implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x)))
+    let callee_id = cx
+        .enclosing_body
+        .map(|body| cx.tcx.hir().body_owner(body).owner.to_def_id());
+    implements_trait_with_env_from_iter(
+        cx.tcx,
+        cx.param_env,
+        ty,
+        trait_id,
+        callee_id,
+        args.iter().map(|&x| Some(x)),
+    )
 }
 
 /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -227,7 +236,14 @@ pub fn implements_trait_with_env<'tcx>(
     callee_id: DefId,
     args: &[GenericArg<'tcx>],
 ) -> bool {
-    implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, Some(callee_id), args.iter().map(|&x| Some(x)))
+    implements_trait_with_env_from_iter(
+        tcx,
+        param_env,
+        ty,
+        trait_id,
+        Some(callee_id),
+        args.iter().map(|&x| Some(x)),
+    )
 }
 
 /// Same as `implements_trait_from_env` but takes the arguments as an iterator.
@@ -248,19 +264,28 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
     }
 
     let infcx = tcx.infer_ctxt().build();
-    let args = args.into_iter().map(|arg| {
-        arg.into().unwrap_or_else(|| {
-            let orig = TypeVariableOrigin {
-                kind: TypeVariableOriginKind::MiscVariable,
-                span: DUMMY_SP,
-            };
-            infcx.next_ty_var(orig).into()
+    let args = args
+        .into_iter()
+        .map(|arg| {
+            arg.into().unwrap_or_else(|| {
+                let orig = TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::MiscVariable,
+                    span: DUMMY_SP,
+                };
+                infcx.next_ty_var(orig).into()
+            })
         })
-    }).collect::<Vec<_>>();
+        .collect::<Vec<_>>();
 
     // If an effect arg was not specified, we need to specify it.
-    let effect_arg = if tcx.generics_of(trait_id).host_effect_index.is_some_and(|x| args.get(x - 1).is_none()) {
-        Some(GenericArg::from(callee_id.map(|def_id| tcx.expected_host_effect_param_for_body(def_id)).unwrap_or(tcx.consts.true_)))
+    let effect_arg = if tcx
+        .generics_of(trait_id)
+        .host_effect_index
+        .is_some_and(|x| args.get(x - 1).is_none())
+    {
+        Some(GenericArg::from(callee_id.map_or(tcx.consts.true_, |def_id| {
+            tcx.expected_host_effect_param_for_body(def_id)
+        })))
     } else {
         None
     };
@@ -268,9 +293,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
     let trait_ref = TraitRef::new(
         tcx,
         trait_id,
-        Some(GenericArg::from(ty))
-            .into_iter()
-            .chain(args).chain(effect_arg),
+        Some(GenericArg::from(ty)).into_iter().chain(args).chain(effect_arg),
     );
 
     debug_assert_matches!(
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index 841b605f5fb..88966b41f69 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -367,7 +367,7 @@ impl Crate {
             //
             // The wrapper is set to the `lintcheck` so we can force enable linting and ignore certain crates
             // (see `crate::driver`)
-            let status = Command::new("cargo")
+            let status = Command::new(env::var("CARGO").unwrap_or("cargo".into()))
                 .arg("check")
                 .arg("--quiet")
                 .current_dir(&self.path)
@@ -441,7 +441,7 @@ impl Crate {
 
 /// Builds clippy inside the repo to make sure we have a clippy executable we can use.
 fn build_clippy() {
-    let status = Command::new("cargo")
+    let status = Command::new(env::var("CARGO").unwrap_or("cargo".into()))
         .arg("build")
         .status()
         .expect("Failed to build clippy!");
@@ -816,7 +816,7 @@ fn lintcheck_test() {
         "--crates-toml",
         "lintcheck/test_sources.toml",
     ];
-    let status = std::process::Command::new("cargo")
+    let status = std::process::Command::new(env::var("CARGO").unwrap_or("cargo".into()))
         .args(args)
         .current_dir("..") // repo root
         .status();
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 684cf4574b9..d575da6dece 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-12-01"
+channel = "nightly-2023-12-16"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 49f0cad08e0..b944a299256 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -18,7 +18,7 @@ extern crate rustc_span;
 use rustc_interface::interface;
 use rustc_session::config::ErrorOutputType;
 use rustc_session::parse::ParseSess;
-use rustc_session::EarlyErrorHandler;
+use rustc_session::EarlyDiagCtxt;
 use rustc_span::symbol::Symbol;
 
 use std::env;
@@ -174,9 +174,9 @@ const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/ne
 #[allow(clippy::too_many_lines)]
 #[allow(clippy::ignored_unit_patterns)]
 pub fn main() {
-    let handler = EarlyErrorHandler::new(ErrorOutputType::default());
+    let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
-    rustc_driver::init_rustc_env_logger(&handler);
+    rustc_driver::init_rustc_env_logger(&early_dcx);
 
     let using_internal_features = rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| {
         // FIXME: this macro calls unwrap internally but is called in a panicking context!  It's not
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index bbf7d22c850..dffa854177b 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -105,7 +105,7 @@ impl ClippyCmd {
     }
 
     fn into_std_cmd(self) -> Command {
-        let mut cmd = Command::new("cargo");
+        let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into()));
         let clippy_args: String = self
             .clippy_args
             .iter()
diff --git a/src/tools/clippy/tests/headers.rs b/src/tools/clippy/tests/headers.rs
index 7eec9a9cdd2..d1f986ef526 100644
--- a/src/tools/clippy/tests/headers.rs
+++ b/src/tools/clippy/tests/headers.rs
@@ -12,7 +12,12 @@ fn old_test_headers() {
 
     for entry in WalkDir::new("tests") {
         let entry = entry.unwrap();
-        if !entry.file_type().is_file() {
+        let is_hidden_file = entry
+            .file_name()
+            .to_str()
+            .expect("non-UTF-8 file name")
+            .starts_with('.');
+        if is_hidden_file || !entry.file_type().is_file() {
             continue;
         }
 
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
index d776feb7f2e..bdceb752608 100644
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
@@ -8,4 +8,4 @@ error: `mod.rs` files are required, found `src/bad.rs`
   = note: `-D clippy::self-named-module-files` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]`
 
-error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to previous error
+error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to 1 previous error
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
index 22558bc4ce8..06eaa071e82 100644
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
@@ -8,4 +8,4 @@ error: `mod.rs` files are not allowed, found `src/bad/mod.rs`
   = note: `-D clippy::mod-module-files` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::mod_module_files)]`
 
-error: could not compile `fail-no-mod` (bin "fail-no-mod") due to previous error
+error: could not compile `fail-no-mod` (bin "fail-no-mod") due to 1 previous error
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
index 4beedc10830..39f7176ade2 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
@@ -3,4 +3,4 @@ error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
   = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]`
 
-error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to previous error
+error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to 1 previous error
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
index 65a19bb0718..a3539051b4d 100644
--- a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
@@ -3,4 +3,4 @@ error: wildcard dependency for `regex`
   = note: `-D clippy::wildcard-dependencies` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::wildcard_dependencies)]`
 
-error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to previous error
+error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to 1 previous error
diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
index d737a832dd1..4375f324aca 100644
--- a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
+++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
@@ -9,7 +9,7 @@
 #![allow(clippy::never_loop)]
 #![allow(clippy::needless_if)]
 #![warn(clippy::excessive_nesting)]
-#![allow(clippy::collapsible_if)]
+#![allow(clippy::collapsible_if, clippy::blocks_in_conditions)]
 
 #[macro_use]
 extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed
index 8cbadc67d71..8c77039b3c0 100644
--- a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed
@@ -1,5 +1,5 @@
 #![deny(clippy::bind_instead_of_map)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::blocks_in_conditions)]
 
 pub fn main() {
     let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs
index 91d9d11e3c1..44257f3a469 100644
--- a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs
@@ -1,5 +1,5 @@
 #![deny(clippy::bind_instead_of_map)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::blocks_in_conditions)]
 
 pub fn main() {
     let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) });
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed
index f89c465047e..2ab441bbd0c 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed
+++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed
@@ -1,4 +1,4 @@
-#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::blocks_in_conditions)]
 #![allow(unused, clippy::let_and_return, clippy::needless_if)]
 #![warn(clippy::nonminimal_bool)]
 
@@ -21,6 +21,7 @@ fn macro_if() {
 
 fn condition_has_block() -> i32 {
     let res = {
+        //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
         let x = 3;
         x == 3
     }; if res {
@@ -32,6 +33,7 @@ fn condition_has_block() -> i32 {
 
 fn condition_has_block_with_single_expression() -> i32 {
     if true { 6 } else { 10 }
+    //~^ ERROR: omit braces around single expression condition
 }
 
 fn condition_is_normal() -> i32 {
@@ -61,4 +63,26 @@ fn block_in_assert() {
     );
 }
 
+// issue #11814
+fn block_in_match_expr(num: i32) -> i32 {
+    let res = {
+        //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
+        let opt = Some(2);
+        opt
+    }; match res {
+        Some(0) => 1,
+        Some(n) => num * 2,
+        None => 0,
+    };
+
+    match unsafe {
+        let hearty_hearty_hearty = vec![240, 159, 146, 150];
+        String::from_utf8_unchecked(hearty_hearty_hearty).as_str()
+    } {
+        "💖" => 1,
+        "what" => 2,
+        _ => 3,
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs
index 34febc5fa2c..dd5ae4fb486 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs
+++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::blocks_in_conditions)]
 #![allow(unused, clippy::let_and_return, clippy::needless_if)]
 #![warn(clippy::nonminimal_bool)]
 
@@ -21,6 +21,7 @@ fn macro_if() {
 
 fn condition_has_block() -> i32 {
     if {
+        //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
         let x = 3;
         x == 3
     } {
@@ -32,6 +33,7 @@ fn condition_has_block() -> i32 {
 
 fn condition_has_block_with_single_expression() -> i32 {
     if { true } { 6 } else { 10 }
+    //~^ ERROR: omit braces around single expression condition
 }
 
 fn condition_is_normal() -> i32 {
@@ -61,4 +63,26 @@ fn block_in_assert() {
     );
 }
 
+// issue #11814
+fn block_in_match_expr(num: i32) -> i32 {
+    match {
+        //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
+        let opt = Some(2);
+        opt
+    } {
+        Some(0) => 1,
+        Some(n) => num * 2,
+        None => 0,
+    };
+
+    match unsafe {
+        let hearty_hearty_hearty = vec![240, 159, 146, 150];
+        String::from_utf8_unchecked(hearty_hearty_hearty).as_str()
+    } {
+        "💖" => 1,
+        "what" => 2,
+        _ => 3,
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr
index d80ef9c0fd8..b00fe2f632c 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr
+++ b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr
@@ -1,30 +1,32 @@
 error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
-  --> $DIR/blocks_in_if_conditions.rs:23:5
+  --> $DIR/blocks_in_conditions.rs:23:5
    |
 LL | /     if {
+LL | |
 LL | |         let x = 3;
 LL | |         x == 3
 LL | |     } {
    | |_____^
    |
-   = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::blocks_in_if_conditions)]`
+   = note: `-D clippy::blocks-in-conditions` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]`
 help: try
    |
 LL ~     let res = {
+LL +
 LL +         let x = 3;
 LL +         x == 3
 LL ~     }; if res {
    |
 
 error: omit braces around single expression condition
-  --> $DIR/blocks_in_if_conditions.rs:34:8
+  --> $DIR/blocks_in_conditions.rs:35:8
    |
 LL |     if { true } { 6 } else { 10 }
    |        ^^^^^^^^ help: try: `true`
 
 error: this boolean expression can be simplified
-  --> $DIR/blocks_in_if_conditions.rs:39:8
+  --> $DIR/blocks_in_conditions.rs:41:8
    |
 LL |     if true && x == 3 { 6 } else { 10 }
    |        ^^^^^^^^^^^^^^ help: try: `x == 3`
@@ -32,5 +34,24 @@ LL |     if true && x == 3 { 6 } else { 10 }
    = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]`
 
-error: aborting due to 3 previous errors
+error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
+  --> $DIR/blocks_in_conditions.rs:68:5
+   |
+LL | /     match {
+LL | |
+LL | |         let opt = Some(2);
+LL | |         opt
+LL | |     } {
+   | |_____^
+   |
+help: try
+   |
+LL ~     let res = {
+LL +
+LL +         let opt = Some(2);
+LL +         opt
+LL ~     }; match res {
+   |
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs
index 539f2df15bd..db31e4ae1a9 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs
+++ b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::blocks_in_conditions)]
 #![allow(
     unused,
     clippy::let_and_return,
@@ -22,7 +22,7 @@ fn pred_test() {
         && predicate(
             |x| {
                 //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks
-                //~| NOTE: `-D clippy::blocks-in-if-conditions` implied by `-D warnings`
+                //~| NOTE: `-D clippy::blocks-in-conditions` implied by `-D warnings`
                 let target = 3;
                 x == target
             },
@@ -60,6 +60,23 @@ fn function_with_empty_closure() {
     if closure(|| {}) {}
 }
 
+// issue #11814
+fn match_with_pred() {
+    let v = 3;
+    match Some(predicate(
+        |x| {
+            //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks
+            let target = 3;
+            x == target
+        },
+        v,
+    )) {
+        Some(true) => 1,
+        Some(false) => 2,
+        None => 3,
+    };
+}
+
 #[rustfmt::skip]
 fn main() {
     let mut range = 0..10;
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr
index ab68997d477..08b98f1b4fc 100644
--- a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr
+++ b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr
@@ -1,5 +1,5 @@
 error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
-  --> $DIR/blocks_in_if_conditions_closure.rs:23:17
+  --> $DIR/blocks_in_conditions_closure.rs:23:17
    |
 LL |               |x| {
    |  _________________^
@@ -10,11 +10,11 @@ LL | |                 x == target
 LL | |             },
    | |_____________^
    |
-   = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::blocks_in_if_conditions)]`
+   = note: `-D clippy::blocks-in-conditions` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]`
 
 error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
-  --> $DIR/blocks_in_if_conditions_closure.rs:34:13
+  --> $DIR/blocks_in_conditions_closure.rs:34:13
    |
 LL |           |x| {
    |  _____________^
@@ -24,5 +24,16 @@ LL | |             x == target
 LL | |         },
    | |_________^
 
-error: aborting due to 2 previous errors
+error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
+  --> $DIR/blocks_in_conditions_closure.rs:67:13
+   |
+LL |           |x| {
+   |  _____________^
+LL | |
+LL | |             let target = 3;
+LL | |             x == target
+LL | |         },
+   | |_________^
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
index aee89719728..708ac666675 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -227,3 +227,6 @@ where [(); N.checked_next_power_of_two().unwrap()]: {
 
 /// this checks if the lowerCamelCase issue is fixed
 fn issue_11568() {}
+
+/// There is no try (`do()` or `do_not()`).
+fn parenthesized_word() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index b6346b881ad..040d6352c52 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -227,3 +227,6 @@ where [(); N.checked_next_power_of_two().unwrap()]: {
 
 /// this checks if the lowerCamelCase issue is fixed
 fn issue_11568() {}
+
+/// There is no try (do() or do_not()).
+fn parenthesized_word() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index 4c9ff41d918..033604e030a 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -319,5 +319,27 @@ help: try
 LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
    |                                  ~~~~~~~~~~~~~~~~~
 
-error: aborting due to 29 previous errors
+error: item in documentation is missing backticks
+  --> $DIR/doc-fixable.rs:231:22
+   |
+LL | /// There is no try (do() or do_not()).
+   |                      ^^^^
+   |
+help: try
+   |
+LL | /// There is no try (`do()` or do_not()).
+   |                      ~~~~~~
+
+error: item in documentation is missing backticks
+  --> $DIR/doc-fixable.rs:231:30
+   |
+LL | /// There is no try (do() or do_not()).
+   |                              ^^^^^^^^
+   |
+help: try
+   |
+LL | /// There is no try (do() or `do_not()`).
+   |                              ~~~~~~~~~~
+
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs
index 0c8eac5ccff..f7f41c915e3 100644
--- a/src/tools/clippy/tests/ui/doc_unsafe.rs
+++ b/src/tools/clippy/tests/ui/doc_unsafe.rs
@@ -1,6 +1,6 @@
 //@aux-build:proc_macros.rs
 
-#![allow(clippy::let_unit_value)]
+#![allow(clippy::let_unit_value, clippy::needless_pass_by_ref_mut)]
 
 extern crate proc_macros;
 use proc_macros::external;
diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.fixed b/src/tools/clippy/tests/ui/ineffective_open_options.fixed
new file mode 100644
index 00000000000..3af8f3c5aaa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ineffective_open_options.fixed
@@ -0,0 +1,41 @@
+#![warn(clippy::ineffective_open_options)]
+
+use std::fs::OpenOptions;
+
+fn main() {
+    let file = OpenOptions::new()
+        .create(true)
+         //~ ERROR: unnecessary use of `.write(true)`
+        .append(true)
+        .open("dump.json")
+        .unwrap();
+
+    let file = OpenOptions::new()
+        .create(true)
+        .append(true)
+         //~ ERROR: unnecessary use of `.write(true)`
+        .open("dump.json")
+        .unwrap();
+
+    // All the next calls are ok.
+    let file = OpenOptions::new()
+        .create(true)
+        .write(false)
+        .append(true)
+        .open("dump.json")
+        .unwrap();
+    let file = OpenOptions::new()
+        .create(true)
+        .write(true)
+        .append(false)
+        .open("dump.json")
+        .unwrap();
+    let file = OpenOptions::new()
+        .create(true)
+        .write(false)
+        .append(false)
+        .open("dump.json")
+        .unwrap();
+    let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap();
+    let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.rs b/src/tools/clippy/tests/ui/ineffective_open_options.rs
new file mode 100644
index 00000000000..4eaf6293c40
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ineffective_open_options.rs
@@ -0,0 +1,41 @@
+#![warn(clippy::ineffective_open_options)]
+
+use std::fs::OpenOptions;
+
+fn main() {
+    let file = OpenOptions::new()
+        .create(true)
+        .write(true) //~ ERROR: unnecessary use of `.write(true)`
+        .append(true)
+        .open("dump.json")
+        .unwrap();
+
+    let file = OpenOptions::new()
+        .create(true)
+        .append(true)
+        .write(true) //~ ERROR: unnecessary use of `.write(true)`
+        .open("dump.json")
+        .unwrap();
+
+    // All the next calls are ok.
+    let file = OpenOptions::new()
+        .create(true)
+        .write(false)
+        .append(true)
+        .open("dump.json")
+        .unwrap();
+    let file = OpenOptions::new()
+        .create(true)
+        .write(true)
+        .append(false)
+        .open("dump.json")
+        .unwrap();
+    let file = OpenOptions::new()
+        .create(true)
+        .write(false)
+        .append(false)
+        .open("dump.json")
+        .unwrap();
+    let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap();
+    let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.stderr b/src/tools/clippy/tests/ui/ineffective_open_options.stderr
new file mode 100644
index 00000000000..7dc5322232c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ineffective_open_options.stderr
@@ -0,0 +1,17 @@
+error: unnecessary use of `.write(true)` because there is `.append(true)`
+  --> $DIR/ineffective_open_options.rs:8:9
+   |
+LL |         .write(true)
+   |         ^^^^^^^^^^^^ help: remove `.write(true)`
+   |
+   = note: `-D clippy::ineffective-open-options` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::ineffective_open_options)]`
+
+error: unnecessary use of `.write(true)` because there is `.append(true)`
+  --> $DIR/ineffective_open_options.rs:16:9
+   |
+LL |         .write(true)
+   |         ^^^^^^^^^^^^ help: remove `.write(true)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed
index 60304177b42..ecf1b14e5b6 100644
--- a/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed
+++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed
@@ -1,6 +1,6 @@
 #![feature(exhaustive_patterns, never_type)]
 #![allow(dead_code, unreachable_code, unused_variables)]
-#![allow(clippy::let_and_return)]
+#![allow(clippy::let_and_return, clippy::uninhabited_references)]
 
 enum SingleVariantEnum {
     Variant(i32),
diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.rs b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs
index b77aac4a16c..7cc7cb9d687 100644
--- a/src/tools/clippy/tests/ui/infallible_destructuring_match.rs
+++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs
@@ -1,6 +1,6 @@
 #![feature(exhaustive_patterns, never_type)]
 #![allow(dead_code, unreachable_code, unused_variables)]
-#![allow(clippy::let_and_return)]
+#![allow(clippy::let_and_return, clippy::uninhabited_references)]
 
 enum SingleVariantEnum {
     Variant(i32),
diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs
new file mode 100644
index 00000000000..646f1eca56d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infinite_loops.rs
@@ -0,0 +1,366 @@
+//@no-rustfix
+#![allow(clippy::never_loop)]
+#![warn(clippy::infinite_loop)]
+
+fn do_something() {}
+
+fn no_break() {
+    loop {
+        //~^ ERROR: infinite loop detected
+        do_something();
+    }
+}
+
+fn all_inf() {
+    loop {
+        //~^ ERROR: infinite loop detected
+        loop {
+            //~^ ERROR: infinite loop detected
+            loop {
+                //~^ ERROR: infinite loop detected
+                do_something();
+            }
+        }
+        do_something();
+    }
+}
+
+fn no_break_return_some_ty() -> Option<u8> {
+    loop {
+        do_something();
+        return None;
+    }
+    loop {
+        //~^ ERROR: infinite loop detected
+        do_something();
+    }
+}
+
+fn no_break_never_ret() -> ! {
+    loop {
+        do_something();
+    }
+}
+
+fn no_break_never_ret_noise() {
+    loop {
+        fn inner_fn() -> ! {
+            std::process::exit(0);
+        }
+        do_something();
+    }
+}
+
+fn has_direct_break_1() {
+    loop {
+        do_something();
+        break;
+    }
+}
+
+fn has_direct_break_2() {
+    'outer: loop {
+        do_something();
+        break 'outer;
+    }
+}
+
+fn has_indirect_break_1(cond: bool) {
+    'outer: loop {
+        loop {
+            if cond {
+                break 'outer;
+            }
+        }
+    }
+}
+
+fn has_indirect_break_2(stop_num: i32) {
+    'outer: loop {
+        for x in 0..5 {
+            if x == stop_num {
+                break 'outer;
+            }
+        }
+    }
+}
+
+fn break_inner_but_not_outer_1(cond: bool) {
+    loop {
+        //~^ ERROR: infinite loop detected
+        loop {
+            if cond {
+                break;
+            }
+        }
+    }
+}
+
+fn break_inner_but_not_outer_2(cond: bool) {
+    loop {
+        //~^ ERROR: infinite loop detected
+        'inner: loop {
+            loop {
+                if cond {
+                    break 'inner;
+                }
+            }
+        }
+    }
+}
+
+fn break_outer_but_not_inner() {
+    loop {
+        loop {
+            //~^ ERROR: infinite loop detected
+            do_something();
+        }
+        break;
+    }
+}
+
+fn can_break_both_inner_and_outer(cond: bool) {
+    'outer: loop {
+        loop {
+            if cond {
+                break 'outer;
+            } else {
+                break;
+            }
+        }
+    }
+}
+
+fn break_wrong_loop(cond: bool) {
+    // 'inner has statement to break 'outer loop, but it was breaked early by a labeled child loop
+    'outer: loop {
+        loop {
+            //~^ ERROR: infinite loop detected
+            'inner: loop {
+                loop {
+                    loop {
+                        break 'inner;
+                    }
+                    break 'outer;
+                }
+            }
+        }
+    }
+}
+
+fn has_direct_return(cond: bool) {
+    loop {
+        if cond {
+            return;
+        }
+    }
+}
+
+fn ret_in_inner(cond: bool) {
+    loop {
+        loop {
+            if cond {
+                return;
+            }
+        }
+    }
+}
+
+enum Foo {
+    A,
+    B,
+    C,
+}
+
+fn match_like() {
+    let opt: Option<u8> = Some(1);
+    loop {
+        //~^ ERROR: infinite loop detected
+        match opt {
+            Some(v) => {
+                println!("{v}");
+            },
+            None => {
+                do_something();
+            },
+        }
+    }
+
+    loop {
+        match opt {
+            Some(v) => {
+                println!("{v}");
+            },
+            None => {
+                do_something();
+                break;
+            },
+        }
+    }
+
+    let result: Result<u8, u16> = Ok(1);
+    loop {
+        let _val = match result {
+            Ok(1) => 1 + 1,
+            Ok(v) => v / 2,
+            Err(_) => return,
+        };
+    }
+
+    loop {
+        let Ok(_val) = result else { return };
+    }
+
+    loop {
+        let Ok(_val) = result.map(|v| 10) else { break };
+    }
+
+    loop {
+        //~^ ERROR: infinite loop detected
+        let _x = matches!(result, Ok(v) if v != 0).then_some(0);
+    }
+
+    loop {
+        //~^ ERROR: infinite loop detected
+        // This `return` does not return the function, so it doesn't count
+        let _x = matches!(result, Ok(v) if v != 0).then(|| {
+            if true {
+                return;
+            }
+            do_something();
+        });
+    }
+
+    let mut val = 0;
+    let mut fooc = Foo::C;
+
+    loop {
+        val = match fooc {
+            Foo::A => 0,
+            Foo::B => {
+                fooc = Foo::C;
+                1
+            },
+            Foo::C => break,
+        };
+    }
+
+    loop {
+        val = match fooc {
+            Foo::A => 0,
+            Foo::B => 1,
+            Foo::C => {
+                break;
+            },
+        };
+    }
+}
+
+macro_rules! set_or_ret {
+    ($opt:expr, $a:expr) => {{
+        match $opt {
+            Some(val) => $a = val,
+            None => return,
+        }
+    }};
+}
+
+fn ret_in_macro(opt: Option<u8>) {
+    let opt: Option<u8> = Some(1);
+    let mut a: u8 = 0;
+    loop {
+        set_or_ret!(opt, a);
+    }
+
+    let res: Result<bool, u8> = Ok(true);
+    loop {
+        match res {
+            Ok(true) => set_or_ret!(opt, a),
+            _ => do_something(),
+        }
+    }
+}
+
+fn panic_like_macros_1() {
+    loop {
+        do_something();
+        panic!();
+    }
+}
+
+fn panic_like_macros_2() {
+    let mut x = 0;
+
+    loop {
+        do_something();
+        if true {
+            todo!();
+        }
+    }
+    loop {
+        do_something();
+        x += 1;
+        assert_eq!(x, 0);
+    }
+    loop {
+        do_something();
+        assert!(x % 2 == 0);
+    }
+    loop {
+        do_something();
+        match Some(1) {
+            Some(n) => println!("{n}"),
+            None => unreachable!("It won't happen"),
+        }
+    }
+}
+
+fn exit_directly(cond: bool) {
+    loop {
+        if cond {
+            std::process::exit(0);
+        }
+    }
+}
+
+trait MyTrait {
+    fn problematic_trait_method() {
+        loop {
+            //~^ ERROR: infinite loop detected
+            do_something();
+        }
+    }
+    fn could_be_problematic();
+}
+
+impl MyTrait for String {
+    fn could_be_problematic() {
+        loop {
+            //~^ ERROR: infinite loop detected
+            do_something();
+        }
+    }
+}
+
+fn inf_loop_in_closure() {
+    let _loop_forever = || {
+        loop {
+            //~^ ERROR: infinite loop detected
+            do_something();
+        }
+    };
+
+    let _somehow_ok = || -> ! {
+        loop {
+            do_something();
+        }
+    };
+}
+
+fn inf_loop_in_res() -> Result<(), i32> {
+    Ok(loop {
+        do_something()
+    })
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr
new file mode 100644
index 00000000000..f58b3cebbc3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infinite_loops.stderr
@@ -0,0 +1,259 @@
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:8:5
+   |
+LL | /     loop {
+LL | |
+LL | |         do_something();
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::infinite-loop` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::infinite_loop)]`
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn no_break() -> ! {
+   |               ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:15:5
+   |
+LL | /     loop {
+LL | |
+LL | |         loop {
+LL | |
+...  |
+LL | |         do_something();
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn all_inf() -> ! {
+   |              ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:17:9
+   |
+LL | /         loop {
+LL | |
+LL | |             loop {
+LL | |
+LL | |                 do_something();
+LL | |             }
+LL | |         }
+   | |_________^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn all_inf() -> ! {
+   |              ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:19:13
+   |
+LL | /             loop {
+LL | |
+LL | |                 do_something();
+LL | |             }
+   | |_____________^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn all_inf() -> ! {
+   |              ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:33:5
+   |
+LL | /     loop {
+LL | |
+LL | |         do_something();
+LL | |     }
+   | |_____^
+   |
+   = help: if this is not intended, try adding a `break` or `return` condition in the loop
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:46:5
+   |
+LL | /     loop {
+LL | |         fn inner_fn() -> ! {
+LL | |             std::process::exit(0);
+LL | |         }
+LL | |         do_something();
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn no_break_never_ret_noise() -> ! {
+   |                               ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:89:5
+   |
+LL | /     loop {
+LL | |
+LL | |         loop {
+LL | |             if cond {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn break_inner_but_not_outer_1(cond: bool) -> ! {
+   |                                            ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:100:5
+   |
+LL | /     loop {
+LL | |
+LL | |         'inner: loop {
+LL | |             loop {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn break_inner_but_not_outer_2(cond: bool) -> ! {
+   |                                            ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:114:9
+   |
+LL | /         loop {
+LL | |
+LL | |             do_something();
+LL | |         }
+   | |_________^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn break_outer_but_not_inner() -> ! {
+   |                                ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:137:9
+   |
+LL | /         loop {
+LL | |
+LL | |             'inner: loop {
+LL | |                 loop {
+...  |
+LL | |             }
+LL | |         }
+   | |_________^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn break_wrong_loop(cond: bool) -> ! {
+   |                                 ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:177:5
+   |
+LL | /     loop {
+LL | |
+LL | |         match opt {
+LL | |             Some(v) => {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn match_like() -> ! {
+   |                 ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:218:5
+   |
+LL | /     loop {
+LL | |
+LL | |         let _x = matches!(result, Ok(v) if v != 0).then_some(0);
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn match_like() -> ! {
+   |                 ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:223:5
+   |
+LL | /     loop {
+LL | |
+LL | |         // This `return` does not return the function, so it doesn't count
+LL | |         let _x = matches!(result, Ok(v) if v != 0).then(|| {
+...  |
+LL | |         });
+LL | |     }
+   | |_____^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL | fn match_like() -> ! {
+   |                 ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:328:9
+   |
+LL | /         loop {
+LL | |
+LL | |             do_something();
+LL | |         }
+   | |_________^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL |     fn problematic_trait_method() -> ! {
+   |                                   ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:338:9
+   |
+LL | /         loop {
+LL | |
+LL | |             do_something();
+LL | |         }
+   | |_________^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL |     fn could_be_problematic() -> ! {
+   |                               ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:347:9
+   |
+LL | /         loop {
+LL | |
+LL | |             do_something();
+LL | |         }
+   | |_________^
+   |
+help: if this is intentional, consider specifing `!` as function return
+   |
+LL |     let _loop_forever = || -> ! {
+   |                            ++++
+
+error: infinite loop detected
+  --> $DIR/infinite_loops.rs:361:8
+   |
+LL |       Ok(loop {
+   |  ________^
+LL | |         do_something()
+LL | |     })
+   | |_____^
+   |
+   = help: if this is not intended, try adding a `break` or `return` condition in the loop
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_filter.fixed b/src/tools/clippy/tests/ui/manual_filter.fixed
index c1bc4aae92e..a0fb0e32d60 100644
--- a/src/tools/clippy/tests/ui/manual_filter.fixed
+++ b/src/tools/clippy/tests/ui/manual_filter.fixed
@@ -40,7 +40,7 @@ fn main() {
         };
     }
 
-    #[allow(clippy::blocks_in_if_conditions)]
+    #[allow(clippy::blocks_in_conditions)]
     Some(11).filter(|&x| {
                 println!("foo");
                 x > 10 && x < 100
diff --git a/src/tools/clippy/tests/ui/manual_filter.rs b/src/tools/clippy/tests/ui/manual_filter.rs
index ee44909f37e..0ac6cbefc4e 100644
--- a/src/tools/clippy/tests/ui/manual_filter.rs
+++ b/src/tools/clippy/tests/ui/manual_filter.rs
@@ -135,7 +135,7 @@ fn main() {
         };
     }
 
-    #[allow(clippy::blocks_in_if_conditions)]
+    #[allow(clippy::blocks_in_conditions)]
     match Some(11) {
         // Lint, statement is preserved by `.filter`
         Some(x) => {
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed
index a96827259f5..ac44a6f3fdb 100644
--- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed
@@ -118,4 +118,19 @@ fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
     let _ = v1[0] + v2[1];
 }
 
+fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
+    assert!(v1.len() == 3);
+    assert!(v2.len() == 4);
+    assert!(v3.len() == 3);
+    assert!(4 == v4.len());
+
+    let _ = v1[0] + v1[1] + v1[2];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[0] + v2[1] + v2[2];
+
+    let _ = v3[0] + v3[1] + v3[2];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v4[0] + v4[1] + v4[2];
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs
index 0b4b883acf8..f05d5fea57d 100644
--- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs
@@ -118,4 +118,19 @@ fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
     let _ = v1[0] + v2[1];
 }
 
+fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
+    assert!(v1.len() == 2);
+    assert!(v2.len() == 4);
+    assert!(2 == v3.len());
+    assert!(4 == v4.len());
+
+    let _ = v1[0] + v1[1] + v1[2];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[0] + v2[1] + v2[2];
+
+    let _ = v3[0] + v3[1] + v3[2];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v4[0] + v4[1] + v4[2];
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr
index a3e66d7958e..61dce6ccc6c 100644
--- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr
@@ -249,5 +249,57 @@ LL |     let _ = v1[0] + v1[12];
    |                     ^^^^^^
    = note: asserting the length before indexing will elide bounds checks
 
-error: aborting due to 9 previous errors
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:127:13
+   |
+LL |     assert!(v1.len() == 2);
+   |     ---------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)`
+...
+LL |     let _ = v1[0] + v1[1] + v1[2];
+   |             ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:127:13
+   |
+LL |     let _ = v1[0] + v1[1] + v1[2];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:127:21
+   |
+LL |     let _ = v1[0] + v1[1] + v1[2];
+   |                     ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:127:29
+   |
+LL |     let _ = v1[0] + v1[1] + v1[2];
+   |                             ^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:131:13
+   |
+LL |     assert!(2 == v3.len());
+   |     ---------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)`
+...
+LL |     let _ = v3[0] + v3[1] + v3[2];
+   |             ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:131:13
+   |
+LL |     let _ = v3[0] + v3[1] + v3[2];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:131:21
+   |
+LL |     let _ = v3[0] + v3[1] + v3[2];
+   |                     ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:131:29
+   |
+LL |     let _ = v3[0] + v3[1] + v3[2];
+   |                             ^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed
index 2a335516f51..bd7a9a0b984 100644
--- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed
@@ -284,4 +284,19 @@ fn main() {
         {
         }
     }
+    // address of field when operand impl Drop
+    {
+        struct CustomDrop(String);
+
+        impl Drop for CustomDrop {
+            fn drop(&mut self) {}
+        }
+
+        fn check_str<P: AsRef<str>>(_to: P) {}
+
+        fn test() {
+            let owner = CustomDrop(String::default());
+            check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop
+        }
+    }
 }
diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs
index f0567f486ac..5cfd4ce30cc 100644
--- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs
@@ -284,4 +284,19 @@ fn main() {
         {
         }
     }
+    // address of field when operand impl Drop
+    {
+        struct CustomDrop(String);
+
+        impl Drop for CustomDrop {
+            fn drop(&mut self) {}
+        }
+
+        fn check_str<P: AsRef<str>>(_to: P) {}
+
+        fn test() {
+            let owner = CustomDrop(String::default());
+            check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop
+        }
+    }
 }
diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_if.fixed
index be35dcddbe6..1086ae2c984 100644
--- a/src/tools/clippy/tests/ui/needless_if.fixed
+++ b/src/tools/clippy/tests/ui/needless_if.fixed
@@ -1,7 +1,7 @@
 //@aux-build:proc_macros.rs
 #![feature(let_chains)]
 #![allow(
-    clippy::blocks_in_if_conditions,
+    clippy::blocks_in_conditions,
     clippy::if_same_then_else,
     clippy::ifs_same_cond,
     clippy::let_unit_value,
diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_if.rs
index e2ad17e69a8..131cceaf712 100644
--- a/src/tools/clippy/tests/ui/needless_if.rs
+++ b/src/tools/clippy/tests/ui/needless_if.rs
@@ -1,7 +1,7 @@
 //@aux-build:proc_macros.rs
 #![feature(let_chains)]
 #![allow(
-    clippy::blocks_in_if_conditions,
+    clippy::blocks_in_conditions,
     clippy::if_same_then_else,
     clippy::ifs_same_cond,
     clippy::let_unit_value,
diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed
index 891b2b01437..6db87049044 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.fixed
+++ b/src/tools/clippy/tests/ui/needless_late_init.fixed
@@ -3,7 +3,7 @@
 #![allow(unused)]
 #![allow(
     clippy::assign_op_pattern,
-    clippy::blocks_in_if_conditions,
+    clippy::blocks_in_conditions,
     clippy::let_and_return,
     clippy::let_unit_value,
     clippy::nonminimal_bool,
diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs
index 55399511639..c1e86212a08 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.rs
+++ b/src/tools/clippy/tests/ui/needless_late_init.rs
@@ -3,7 +3,7 @@
 #![allow(unused)]
 #![allow(
     clippy::assign_op_pattern,
-    clippy::blocks_in_if_conditions,
+    clippy::blocks_in_conditions,
     clippy::let_and_return,
     clippy::let_unit_value,
     clippy::nonminimal_bool,
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
index 25a02bdd2f2..a92197fb0af 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
@@ -307,6 +307,19 @@ fn filter_copy<T: Copy>(predicate: &mut impl FnMut(T) -> bool) -> impl FnMut(&T)
     move |&item| predicate(item)
 }
 
+// `is_from_proc_macro` stress tests
+fn _empty_tup(x: &mut (())) {}
+fn _single_tup(x: &mut ((i32,))) {}
+fn _multi_tup(x: &mut ((i32, u32))) {}
+fn _fn(x: &mut (fn())) {}
+#[rustfmt::skip]
+fn _extern_rust_fn(x: &mut extern "Rust" fn()) {}
+fn _extern_c_fn(x: &mut extern "C" fn()) {}
+fn _unsafe_fn(x: &mut unsafe fn()) {}
+fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {}
+fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {}
+fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {}
+
 fn main() {
     let mut u = 0;
     let mut v = vec![0];
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
index 92b753276ac..5d1e9515de1 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
@@ -139,5 +139,65 @@ LL | pub async fn closure4(n: &mut usize) {
    |
    = warning: changing this function will impact semver compatibility
 
-error: aborting due to 21 previous errors
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:311:18
+   |
+LL | fn _empty_tup(x: &mut (())) {}
+   |                  ^^^^^^^^^ help: consider changing to: `&()`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:312:19
+   |
+LL | fn _single_tup(x: &mut ((i32,))) {}
+   |                   ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:313:18
+   |
+LL | fn _multi_tup(x: &mut ((i32, u32))) {}
+   |                  ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:314:11
+   |
+LL | fn _fn(x: &mut (fn())) {}
+   |           ^^^^^^^^^^^ help: consider changing to: `&fn()`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:316:23
+   |
+LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {}
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:317:20
+   |
+LL | fn _extern_c_fn(x: &mut extern "C" fn()) {}
+   |                    ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:318:18
+   |
+LL | fn _unsafe_fn(x: &mut unsafe fn()) {}
+   |                  ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:319:25
+   |
+LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {}
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:320:20
+   |
+LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {}
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)`
+
+error: this argument is a mutable reference, but not used mutably
+  --> $DIR/needless_pass_by_ref_mut.rs:321:20
+   |
+LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {}
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)`
+
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs
index c52f4389192..777b1e52c2d 100644
--- a/src/tools/clippy/tests/ui/no_effect.rs
+++ b/src/tools/clippy/tests/ui/no_effect.rs
@@ -9,6 +9,30 @@
     clippy::useless_vec
 )]
 
+use std::fmt::Display;
+use std::ops::{Neg, Shl};
+
+struct Cout;
+
+impl<T> Shl<T> for Cout
+where
+    T: Display,
+{
+    type Output = Self;
+    fn shl(self, rhs: T) -> Self::Output {
+        println!("{}", rhs);
+        self
+    }
+}
+
+impl Neg for Cout {
+    type Output = Self;
+    fn neg(self) -> Self::Output {
+        println!("hello world");
+        self
+    }
+}
+
 struct Unit;
 struct Tuple(i32);
 struct Struct {
@@ -174,4 +198,11 @@ fn main() {
     GreetStruct1("world");
     GreetStruct2()("world");
     GreetStruct3 {}("world");
+
+    fn n() -> i32 {
+        42
+    }
+
+    Cout << 142;
+    -Cout;
 }
diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr
index feba35697f5..f5ba234b4cb 100644
--- a/src/tools/clippy/tests/ui/no_effect.stderr
+++ b/src/tools/clippy/tests/ui/no_effect.stderr
@@ -1,5 +1,5 @@
 error: statement with no effect
-  --> $DIR/no_effect.rs:98:5
+  --> $DIR/no_effect.rs:122:5
    |
 LL |     0;
    |     ^^
@@ -8,151 +8,151 @@ LL |     0;
    = help: to override `-D warnings` add `#[allow(clippy::no_effect)]`
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:101:5
+  --> $DIR/no_effect.rs:125:5
    |
 LL |     s2;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:103:5
+  --> $DIR/no_effect.rs:127:5
    |
 LL |     Unit;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:105:5
+  --> $DIR/no_effect.rs:129:5
    |
 LL |     Tuple(0);
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:107:5
+  --> $DIR/no_effect.rs:131:5
    |
 LL |     Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:109:5
+  --> $DIR/no_effect.rs:133:5
    |
 LL |     Struct { ..s };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:111:5
+  --> $DIR/no_effect.rs:135:5
    |
 LL |     Union { a: 0 };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:113:5
+  --> $DIR/no_effect.rs:137:5
    |
 LL |     Enum::Tuple(0);
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:115:5
+  --> $DIR/no_effect.rs:139:5
    |
 LL |     Enum::Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:117:5
+  --> $DIR/no_effect.rs:141:5
    |
 LL |     5 + 6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:119:5
+  --> $DIR/no_effect.rs:143:5
    |
 LL |     *&42;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:121:5
+  --> $DIR/no_effect.rs:145:5
    |
 LL |     &6;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:123:5
+  --> $DIR/no_effect.rs:147:5
    |
 LL |     (5, 6, 7);
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:125:5
+  --> $DIR/no_effect.rs:149:5
    |
 LL |     ..;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:127:5
+  --> $DIR/no_effect.rs:151:5
    |
 LL |     5..;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:129:5
+  --> $DIR/no_effect.rs:153:5
    |
 LL |     ..5;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:131:5
+  --> $DIR/no_effect.rs:155:5
    |
 LL |     5..6;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:133:5
+  --> $DIR/no_effect.rs:157:5
    |
 LL |     5..=6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:135:5
+  --> $DIR/no_effect.rs:159:5
    |
 LL |     [42, 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:137:5
+  --> $DIR/no_effect.rs:161:5
    |
 LL |     [42, 55][1];
    |     ^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:139:5
+  --> $DIR/no_effect.rs:163:5
    |
 LL |     (42, 55).1;
    |     ^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:141:5
+  --> $DIR/no_effect.rs:165:5
    |
 LL |     [42; 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:143:5
+  --> $DIR/no_effect.rs:167:5
    |
 LL |     [42; 55][13];
    |     ^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:146:5
+  --> $DIR/no_effect.rs:170:5
    |
 LL |     || x += 5;
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:149:5
+  --> $DIR/no_effect.rs:173:5
    |
 LL |     FooString { s: s };
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:151:5
+  --> $DIR/no_effect.rs:175:5
    |
 LL |     let _unused = 1;
    |     ^^^^^^^^^^^^^^^^
@@ -161,19 +161,19 @@ LL |     let _unused = 1;
    = help: to override `-D warnings` add `#[allow(clippy::no_effect_underscore_binding)]`
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:154:5
+  --> $DIR/no_effect.rs:178:5
    |
 LL |     let _penguin = || println!("Some helpful closure");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:156:5
+  --> $DIR/no_effect.rs:180:5
    |
 LL |     let _duck = Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:158:5
+  --> $DIR/no_effect.rs:182:5
    |
 LL |     let _cat = [2, 4, 6, 8][2];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
index ca13b52ae3b..fa15c323540 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
@@ -71,3 +71,118 @@ fn _msrv_1_38() {
     let _ = ptr.cast::<i32>();
     let _ = mut_ptr.cast::<i32>();
 }
+
+#[allow(clippy::unnecessary_cast)]
+mod null {
+    fn use_path_mut() -> *mut u32 {
+        use std::ptr;
+        ptr::null_mut::<u32>()
+    }
+
+    fn full_path_mut() -> *mut u32 {
+        std::ptr::null_mut::<u32>()
+    }
+
+    fn core_path_mut() -> *mut u32 {
+        use core::ptr;
+        ptr::null_mut::<u32>()
+    }
+
+    fn full_core_path_mut() -> *mut u32 {
+        core::ptr::null_mut::<u32>()
+    }
+
+    fn use_path() -> *const u32 {
+        use std::ptr;
+        ptr::null::<u32>()
+    }
+
+    fn full_path() -> *const u32 {
+        std::ptr::null::<u32>()
+    }
+
+    fn core_path() -> *const u32 {
+        use core::ptr;
+        ptr::null::<u32>()
+    }
+
+    fn full_core_path() -> *const u32 {
+        core::ptr::null::<u32>()
+    }
+}
+
+mod null_ptr_infer {
+    fn use_path_mut() -> *mut u32 {
+        use std::ptr;
+        ptr::null_mut()
+    }
+
+    fn full_path_mut() -> *mut u32 {
+        std::ptr::null_mut()
+    }
+
+    fn core_path_mut() -> *mut u32 {
+        use core::ptr;
+        ptr::null_mut()
+    }
+
+    fn full_core_path_mut() -> *mut u32 {
+        core::ptr::null_mut()
+    }
+
+    fn use_path() -> *const u32 {
+        use std::ptr;
+        ptr::null()
+    }
+
+    fn full_path() -> *const u32 {
+        std::ptr::null()
+    }
+
+    fn core_path() -> *const u32 {
+        use core::ptr;
+        ptr::null()
+    }
+
+    fn full_core_path() -> *const u32 {
+        core::ptr::null()
+    }
+}
+
+mod null_entire_infer {
+    fn use_path_mut() -> *mut u32 {
+        use std::ptr;
+        ptr::null_mut()
+    }
+
+    fn full_path_mut() -> *mut u32 {
+        std::ptr::null_mut()
+    }
+
+    fn core_path_mut() -> *mut u32 {
+        use core::ptr;
+        ptr::null_mut()
+    }
+
+    fn full_core_path_mut() -> *mut u32 {
+        core::ptr::null_mut()
+    }
+
+    fn use_path() -> *const u32 {
+        use std::ptr;
+        ptr::null()
+    }
+
+    fn full_path() -> *const u32 {
+        std::ptr::null()
+    }
+
+    fn core_path() -> *const u32 {
+        use core::ptr;
+        ptr::null()
+    }
+
+    fn full_core_path() -> *const u32 {
+        core::ptr::null()
+    }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
index 942c8734444..7ab52e63da5 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
@@ -71,3 +71,118 @@ fn _msrv_1_38() {
     let _ = ptr as *const i32;
     let _ = mut_ptr as *mut i32;
 }
+
+#[allow(clippy::unnecessary_cast)]
+mod null {
+    fn use_path_mut() -> *mut u32 {
+        use std::ptr;
+        ptr::null_mut() as *mut u32
+    }
+
+    fn full_path_mut() -> *mut u32 {
+        std::ptr::null_mut() as *mut u32
+    }
+
+    fn core_path_mut() -> *mut u32 {
+        use core::ptr;
+        ptr::null_mut() as *mut u32
+    }
+
+    fn full_core_path_mut() -> *mut u32 {
+        core::ptr::null_mut() as *mut u32
+    }
+
+    fn use_path() -> *const u32 {
+        use std::ptr;
+        ptr::null() as *const u32
+    }
+
+    fn full_path() -> *const u32 {
+        std::ptr::null() as *const u32
+    }
+
+    fn core_path() -> *const u32 {
+        use core::ptr;
+        ptr::null() as *const u32
+    }
+
+    fn full_core_path() -> *const u32 {
+        core::ptr::null() as *const u32
+    }
+}
+
+mod null_ptr_infer {
+    fn use_path_mut() -> *mut u32 {
+        use std::ptr;
+        ptr::null_mut() as *mut _
+    }
+
+    fn full_path_mut() -> *mut u32 {
+        std::ptr::null_mut() as *mut _
+    }
+
+    fn core_path_mut() -> *mut u32 {
+        use core::ptr;
+        ptr::null_mut() as *mut _
+    }
+
+    fn full_core_path_mut() -> *mut u32 {
+        core::ptr::null_mut() as *mut _
+    }
+
+    fn use_path() -> *const u32 {
+        use std::ptr;
+        ptr::null() as *const _
+    }
+
+    fn full_path() -> *const u32 {
+        std::ptr::null() as *const _
+    }
+
+    fn core_path() -> *const u32 {
+        use core::ptr;
+        ptr::null() as *const _
+    }
+
+    fn full_core_path() -> *const u32 {
+        core::ptr::null() as *const _
+    }
+}
+
+mod null_entire_infer {
+    fn use_path_mut() -> *mut u32 {
+        use std::ptr;
+        ptr::null_mut() as _
+    }
+
+    fn full_path_mut() -> *mut u32 {
+        std::ptr::null_mut() as _
+    }
+
+    fn core_path_mut() -> *mut u32 {
+        use core::ptr;
+        ptr::null_mut() as _
+    }
+
+    fn full_core_path_mut() -> *mut u32 {
+        core::ptr::null_mut() as _
+    }
+
+    fn use_path() -> *const u32 {
+        use std::ptr;
+        ptr::null() as _
+    }
+
+    fn full_path() -> *const u32 {
+        std::ptr::null() as _
+    }
+
+    fn core_path() -> *const u32 {
+        use core::ptr;
+        ptr::null() as _
+    }
+
+    fn full_core_path() -> *const u32 {
+        core::ptr::null() as _
+    }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
index c0ce69b4357..ef64347e935 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
@@ -57,5 +57,149 @@ error: `as` casting between raw pointers without changing its mutability
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
-error: aborting due to 9 previous errors
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:79:9
+   |
+LL |         ptr::null_mut() as *mut u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:83:9
+   |
+LL |         std::ptr::null_mut() as *mut u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:88:9
+   |
+LL |         ptr::null_mut() as *mut u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:92:9
+   |
+LL |         core::ptr::null_mut() as *mut u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:97:9
+   |
+LL |         ptr::null() as *const u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:101:9
+   |
+LL |         std::ptr::null() as *const u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:106:9
+   |
+LL |         ptr::null() as *const u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:110:9
+   |
+LL |         core::ptr::null() as *const u32
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null::<u32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:117:9
+   |
+LL |         ptr::null_mut() as *mut _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:121:9
+   |
+LL |         std::ptr::null_mut() as *mut _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:126:9
+   |
+LL |         ptr::null_mut() as *mut _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:130:9
+   |
+LL |         core::ptr::null_mut() as *mut _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:135:9
+   |
+LL |         ptr::null() as *const _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:139:9
+   |
+LL |         std::ptr::null() as *const _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:144:9
+   |
+LL |         ptr::null() as *const _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:148:9
+   |
+LL |         core::ptr::null() as *const _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:155:9
+   |
+LL |         ptr::null_mut() as _
+   |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:159:9
+   |
+LL |         std::ptr::null_mut() as _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:164:9
+   |
+LL |         ptr::null_mut() as _
+   |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:168:9
+   |
+LL |         core::ptr::null_mut() as _
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:173:9
+   |
+LL |         ptr::null() as _
+   |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:177:9
+   |
+LL |         std::ptr::null() as _
+   |         ^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:182:9
+   |
+LL |         ptr::null() as _
+   |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
+
+error: `as` casting between raw pointers without changing its mutability
+  --> $DIR/ptr_as_ptr.rs:186:9
+   |
+LL |         core::ptr::null() as _
+   |         ^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()`
+
+error: aborting due to 33 previous errors
 
diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs
index 094d9574ae9..1ea0d65bf1e 100644
--- a/src/tools/clippy/tests/ui/regex.rs
+++ b/src/tools/clippy/tests/ui/regex.rs
@@ -112,6 +112,10 @@ fn trivial_regex() {
 
     // #6005: unicode classes in bytes::Regex
     let a_byte_of_unicode = BRegex::new(r"\p{C}");
+
+    // start and end word boundry, introduced in regex 0.10
+    let _ = BRegex::new(r"\<word\>");
+    let _ = BRegex::new(r"\b{start}word\b{end}");
 }
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index 31f1ee6a86d..f4ff0f0b88b 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -4,7 +4,7 @@
 
 #![allow(clippy::almost_complete_range)]
 #![allow(clippy::disallowed_names)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::blocks_in_conditions)]
 #![allow(clippy::box_collection)]
 #![allow(clippy::redundant_static_lifetimes)]
 #![allow(clippy::cognitive_complexity)]
@@ -54,8 +54,9 @@
 #![allow(ambiguous_wide_pointer_comparisons)]
 #![warn(clippy::almost_complete_range)]
 #![warn(clippy::disallowed_names)]
-#![warn(clippy::blocks_in_if_conditions)]
-#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::blocks_in_conditions)]
+#![warn(clippy::blocks_in_conditions)]
+#![warn(clippy::blocks_in_conditions)]
 #![warn(clippy::box_collection)]
 #![warn(clippy::redundant_static_lifetimes)]
 #![warn(clippy::cognitive_complexity)]
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index 325bc356c15..0df1098f5fb 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -4,7 +4,7 @@
 
 #![allow(clippy::almost_complete_range)]
 #![allow(clippy::disallowed_names)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::blocks_in_conditions)]
 #![allow(clippy::box_collection)]
 #![allow(clippy::redundant_static_lifetimes)]
 #![allow(clippy::cognitive_complexity)]
@@ -56,6 +56,7 @@
 #![warn(clippy::blacklisted_name)]
 #![warn(clippy::block_in_if_condition_expr)]
 #![warn(clippy::block_in_if_condition_stmt)]
+#![warn(clippy::blocks_in_if_conditions)]
 #![warn(clippy::box_vec)]
 #![warn(clippy::const_static_lifetime)]
 #![warn(clippy::cyclomatic_complexity)]
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index a6164338c20..f63ad82a757 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -13,335 +13,341 @@ error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_n
 LL | #![warn(clippy::blacklisted_name)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
 
-error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
+error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions`
   --> $DIR/rename.rs:57:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
-error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
+error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions`
   --> $DIR/rename.rs:58:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
-error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
+error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions`
   --> $DIR/rename.rs:59:9
    |
+LL | #![warn(clippy::blocks_in_if_conditions)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
+
+error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
+  --> $DIR/rename.rs:60:9
+   |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> $DIR/rename.rs:60:9
+  --> $DIR/rename.rs:61:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:61:9
+  --> $DIR/rename.rs:62:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
-  --> $DIR/rename.rs:62:9
+  --> $DIR/rename.rs:63:9
    |
 LL | #![warn(clippy::derive_hash_xor_eq)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
 
 error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-  --> $DIR/rename.rs:63:9
+  --> $DIR/rename.rs:64:9
    |
 LL | #![warn(clippy::disallowed_method)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
 error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-  --> $DIR/rename.rs:64:9
+  --> $DIR/rename.rs:65:9
    |
 LL | #![warn(clippy::disallowed_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
 error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
-  --> $DIR/rename.rs:65:9
+  --> $DIR/rename.rs:66:9
    |
 LL | #![warn(clippy::eval_order_dependence)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> $DIR/rename.rs:66:9
+  --> $DIR/rename.rs:67:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> $DIR/rename.rs:67:9
+  --> $DIR/rename.rs:68:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
 error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl`
-  --> $DIR/rename.rs:68:9
+  --> $DIR/rename.rs:69:9
    |
 LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl`
 
 error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl`
-  --> $DIR/rename.rs:69:9
+  --> $DIR/rename.rs:70:9
    |
 LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl`
 
 error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects`
-  --> $DIR/rename.rs:70:9
+  --> $DIR/rename.rs:71:9
    |
 LL | #![warn(clippy::integer_arithmetic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects`
 
 error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-  --> $DIR/rename.rs:71:9
+  --> $DIR/rename.rs:72:9
    |
 LL | #![warn(clippy::logic_bug)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> $DIR/rename.rs:72:9
+  --> $DIR/rename.rs:73:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> $DIR/rename.rs:73:9
+  --> $DIR/rename.rs:74:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:74:9
+  --> $DIR/rename.rs:75:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:75:9
+  --> $DIR/rename.rs:76:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:76:9
+  --> $DIR/rename.rs:77:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:77:9
+  --> $DIR/rename.rs:78:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-  --> $DIR/rename.rs:78:9
+  --> $DIR/rename.rs:79:9
    |
 LL | #![warn(clippy::ref_in_deref)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:79:9
+  --> $DIR/rename.rs:80:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:80:9
+  --> $DIR/rename.rs:81:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:81:9
+  --> $DIR/rename.rs:82:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> $DIR/rename.rs:82:9
+  --> $DIR/rename.rs:83:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> $DIR/rename.rs:83:9
+  --> $DIR/rename.rs:84:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
 error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-  --> $DIR/rename.rs:84:9
+  --> $DIR/rename.rs:85:9
    |
 LL | #![warn(clippy::to_string_in_display)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
 error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default`
-  --> $DIR/rename.rs:85:9
+  --> $DIR/rename.rs:86:9
    |
 LL | #![warn(clippy::unwrap_or_else_default)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> $DIR/rename.rs:86:9
+  --> $DIR/rename.rs:87:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting`
-  --> $DIR/rename.rs:87:9
+  --> $DIR/rename.rs:88:9
    |
 LL | #![warn(clippy::cast_ref_to_mut)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting`
 
 error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op`
-  --> $DIR/rename.rs:88:9
+  --> $DIR/rename.rs:89:9
    |
 LL | #![warn(clippy::clone_double_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op`
 
 error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons`
-  --> $DIR/rename.rs:89:9
+  --> $DIR/rename.rs:90:9
    |
 LL | #![warn(clippy::cmp_nan)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> $DIR/rename.rs:90:9
+  --> $DIR/rename.rs:91:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types`
-  --> $DIR/rename.rs:91:9
+  --> $DIR/rename.rs:92:9
    |
 LL | #![warn(clippy::drop_copy)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types`
 
 error: lint `clippy::drop_ref` has been renamed to `dropping_references`
-  --> $DIR/rename.rs:92:9
+  --> $DIR/rename.rs:93:9
    |
 LL | #![warn(clippy::drop_ref)]
    |         ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references`
 
 error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks`
-  --> $DIR/rename.rs:93:9
+  --> $DIR/rename.rs:94:9
    |
 LL | #![warn(clippy::fn_null_check)]
    |         ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:94:9
+  --> $DIR/rename.rs:95:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:95:9
+  --> $DIR/rename.rs:96:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:96:9
+  --> $DIR/rename.rs:97:9
    |
 LL | #![warn(clippy::for_loops_over_fallibles)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types`
-  --> $DIR/rename.rs:97:9
+  --> $DIR/rename.rs:98:9
    |
 LL | #![warn(clippy::forget_copy)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types`
 
 error: lint `clippy::forget_ref` has been renamed to `forgetting_references`
-  --> $DIR/rename.rs:98:9
+  --> $DIR/rename.rs:99:9
    |
 LL | #![warn(clippy::forget_ref)]
    |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> $DIR/rename.rs:99:9
+  --> $DIR/rename.rs:100:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> $DIR/rename.rs:100:9
+  --> $DIR/rename.rs:101:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> $DIR/rename.rs:101:9
+  --> $DIR/rename.rs:102:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked`
-  --> $DIR/rename.rs:102:9
+  --> $DIR/rename.rs:103:9
    |
 LL | #![warn(clippy::invalid_utf8_in_unchecked)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked`
 
 error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
-  --> $DIR/rename.rs:103:9
+  --> $DIR/rename.rs:104:9
    |
 LL | #![warn(clippy::let_underscore_drop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> $DIR/rename.rs:104:9
+  --> $DIR/rename.rs:105:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> $DIR/rename.rs:105:9
+  --> $DIR/rename.rs:106:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
-  --> $DIR/rename.rs:106:9
+  --> $DIR/rename.rs:107:9
    |
 LL | #![warn(clippy::positional_named_format_parameters)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> $DIR/rename.rs:107:9
+  --> $DIR/rename.rs:108:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
-  --> $DIR/rename.rs:108:9
+  --> $DIR/rename.rs:109:9
    |
 LL | #![warn(clippy::undropped_manually_drops)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> $DIR/rename.rs:109:9
+  --> $DIR/rename.rs:110:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> $DIR/rename.rs:110:9
+  --> $DIR/rename.rs:111:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons`
-  --> $DIR/rename.rs:111:9
+  --> $DIR/rename.rs:112:9
    |
 LL | #![warn(clippy::vtable_address_comparisons)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons`
 
-error: aborting due to 57 previous errors
+error: aborting due to 58 previous errors
 
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed
new file mode 100644
index 00000000000..2afe2f43325
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed
@@ -0,0 +1,38 @@
+#![warn(clippy::repeat_vec_with_capacity)]
+
+fn main() {
+    {
+        (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
+        //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
+    }
+
+    {
+        let n = 123;
+        (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
+        //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
+    }
+
+    {
+        macro_rules! from_macro {
+            ($x:expr) => {
+                vec![$x; 123];
+            };
+        }
+        // vec expansion is from another macro, don't lint
+        from_macro!(Vec::<()>::with_capacity(42));
+    }
+
+    {
+        std::iter::repeat_with(|| Vec::<()>::with_capacity(42));
+        //~^ ERROR: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity
+    }
+
+    {
+        macro_rules! from_macro {
+            ($x:expr) => {
+                std::iter::repeat($x)
+            };
+        }
+        from_macro!(Vec::<()>::with_capacity(42));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs
new file mode 100644
index 00000000000..659f2a3953d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs
@@ -0,0 +1,38 @@
+#![warn(clippy::repeat_vec_with_capacity)]
+
+fn main() {
+    {
+        vec![Vec::<()>::with_capacity(42); 123];
+        //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
+    }
+
+    {
+        let n = 123;
+        vec![Vec::<()>::with_capacity(42); n];
+        //~^ ERROR: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
+    }
+
+    {
+        macro_rules! from_macro {
+            ($x:expr) => {
+                vec![$x; 123];
+            };
+        }
+        // vec expansion is from another macro, don't lint
+        from_macro!(Vec::<()>::with_capacity(42));
+    }
+
+    {
+        std::iter::repeat(Vec::<()>::with_capacity(42));
+        //~^ ERROR: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity
+    }
+
+    {
+        macro_rules! from_macro {
+            ($x:expr) => {
+                std::iter::repeat($x)
+            };
+        }
+        from_macro!(Vec::<()>::with_capacity(42));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
new file mode 100644
index 00000000000..10b5f121420
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
@@ -0,0 +1,40 @@
+error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
+  --> $DIR/repeat_vec_with_capacity.rs:5:9
+   |
+LL |         vec![Vec::<()>::with_capacity(42); 123];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only the last `Vec` will have the capacity
+   = note: `-D clippy::repeat-vec-with-capacity` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]`
+help: if you intended to initialize multiple `Vec`s with an initial capacity, try
+   |
+LL |         (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
+  --> $DIR/repeat_vec_with_capacity.rs:11:9
+   |
+LL |         vec![Vec::<()>::with_capacity(42); n];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only the last `Vec` will have the capacity
+help: if you intended to initialize multiple `Vec`s with an initial capacity, try
+   |
+LL |         (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity
+  --> $DIR/repeat_vec_with_capacity.rs:26:9
+   |
+LL |         std::iter::repeat(Vec::<()>::with_capacity(42));
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: none of the yielded `Vec`s will have the requested capacity
+help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try
+   |
+LL |         std::iter::repeat_with(|| Vec::<()>::with_capacity(42));
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/uninhabited_references.rs b/src/tools/clippy/tests/ui/uninhabited_references.rs
new file mode 100644
index 00000000000..cd07b590a61
--- /dev/null
+++ b/src/tools/clippy/tests/ui/uninhabited_references.rs
@@ -0,0 +1,22 @@
+#![warn(clippy::uninhabited_references)]
+#![feature(never_type)]
+
+fn ret_uninh_ref() -> &'static std::convert::Infallible {
+    unsafe { std::mem::transmute(&()) }
+}
+
+macro_rules! ret_something {
+    ($name:ident, $ty:ty) => {
+        fn $name(x: &$ty) -> &$ty {
+            &*x
+        }
+    };
+}
+
+ret_something!(id_u32, u32);
+ret_something!(id_never, !);
+
+fn main() {
+    let x = ret_uninh_ref();
+    let _ = *x;
+}
diff --git a/src/tools/clippy/tests/ui/uninhabited_references.stderr b/src/tools/clippy/tests/ui/uninhabited_references.stderr
new file mode 100644
index 00000000000..2cdf320b809
--- /dev/null
+++ b/src/tools/clippy/tests/ui/uninhabited_references.stderr
@@ -0,0 +1,39 @@
+error: dereferencing a reference to an uninhabited type would be undefined behavior
+  --> $DIR/uninhabited_references.rs:4:23
+   |
+LL | fn ret_uninh_ref() -> &'static std::convert::Infallible {
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::uninhabited-references` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::uninhabited_references)]`
+
+error: dereferencing a reference to an uninhabited type would be undefined behavior
+  --> $DIR/uninhabited_references.rs:10:30
+   |
+LL |         fn $name(x: &$ty) -> &$ty {
+   |                              ^^^^
+...
+LL | ret_something!(id_never, !);
+   | --------------------------- in this macro invocation
+   |
+   = note: this error originates in the macro `ret_something` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: dereferencing a reference to an uninhabited type is undefined behavior
+  --> $DIR/uninhabited_references.rs:11:14
+   |
+LL |             &*x
+   |              ^^
+...
+LL | ret_something!(id_never, !);
+   | --------------------------- in this macro invocation
+   |
+   = note: this error originates in the macro `ret_something` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: dereferencing a reference to an uninhabited type is undefined behavior
+  --> $DIR/uninhabited_references.rs:21:13
+   |
+LL |     let _ = *x;
+   |             ^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index d0c0298ef4c..463412daec0 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -7,6 +7,9 @@
 )]
 #![warn(clippy::unnecessary_operation)]
 
+use std::fmt::Display;
+use std::ops::Shl;
+
 struct Tuple(i32);
 struct Struct {
     field: i32,
@@ -50,6 +53,19 @@ fn get_drop_struct() -> DropStruct {
     DropStruct { field: 0 }
 }
 
+struct Cout;
+
+impl<T> Shl<T> for Cout
+where
+    T: Display,
+{
+    type Output = Self;
+    fn shl(self, rhs: T) -> Self::Output {
+        println!("{}", rhs);
+        self
+    }
+}
+
 fn main() {
     get_number();
     get_number();
@@ -87,4 +103,7 @@ fn main() {
         ($($e:expr),*) => {{ $($e;)* }}
     }
     use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
+
+    // Issue #11885
+    Cout << 16;
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
index e8e3a2d5657..f0d28e28902 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -7,6 +7,9 @@
 )]
 #![warn(clippy::unnecessary_operation)]
 
+use std::fmt::Display;
+use std::ops::Shl;
+
 struct Tuple(i32);
 struct Struct {
     field: i32,
@@ -50,6 +53,19 @@ fn get_drop_struct() -> DropStruct {
     DropStruct { field: 0 }
 }
 
+struct Cout;
+
+impl<T> Shl<T> for Cout
+where
+    T: Display,
+{
+    type Output = Self;
+    fn shl(self, rhs: T) -> Self::Output {
+        println!("{}", rhs);
+        self
+    }
+}
+
 fn main() {
     Tuple(get_number());
     Struct { field: get_number() };
@@ -91,4 +107,7 @@ fn main() {
         ($($e:expr),*) => {{ $($e;)* }}
     }
     use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
+
+    // Issue #11885
+    Cout << 16;
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
index fbe495f518f..eeee9ad6006 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
@@ -1,5 +1,5 @@
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:54:5
+  --> $DIR/unnecessary_operation.rs:70:5
    |
 LL |     Tuple(get_number());
    |     ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
@@ -8,103 +8,103 @@ LL |     Tuple(get_number());
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:55:5
+  --> $DIR/unnecessary_operation.rs:71:5
    |
 LL |     Struct { field: get_number() };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:56:5
+  --> $DIR/unnecessary_operation.rs:72:5
    |
 LL |     Struct { ..get_struct() };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:57:5
+  --> $DIR/unnecessary_operation.rs:73:5
    |
 LL |     Enum::Tuple(get_number());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:58:5
+  --> $DIR/unnecessary_operation.rs:74:5
    |
 LL |     Enum::Struct { field: get_number() };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:59:5
+  --> $DIR/unnecessary_operation.rs:75:5
    |
 LL |     5 + get_number();
    |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:60:5
+  --> $DIR/unnecessary_operation.rs:76:5
    |
 LL |     *&get_number();
    |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:61:5
+  --> $DIR/unnecessary_operation.rs:77:5
    |
 LL |     &get_number();
    |     ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:62:5
+  --> $DIR/unnecessary_operation.rs:78:5
    |
 LL |     (5, 6, get_number());
    |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:63:5
+  --> $DIR/unnecessary_operation.rs:79:5
    |
 LL |     get_number()..;
    |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:64:5
+  --> $DIR/unnecessary_operation.rs:80:5
    |
 LL |     ..get_number();
    |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:65:5
+  --> $DIR/unnecessary_operation.rs:81:5
    |
 LL |     5..get_number();
    |     ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:66:5
+  --> $DIR/unnecessary_operation.rs:82:5
    |
 LL |     [42, get_number()];
    |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:67:5
+  --> $DIR/unnecessary_operation.rs:83:5
    |
 LL |     [42, 55][get_usize()];
    |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:68:5
+  --> $DIR/unnecessary_operation.rs:84:5
    |
 LL |     (42, get_number()).1;
    |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:69:5
+  --> $DIR/unnecessary_operation.rs:85:5
    |
 LL |     [get_number(); 55];
    |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:70:5
+  --> $DIR/unnecessary_operation.rs:86:5
    |
 LL |     [42; 55][get_usize()];
    |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:71:5
+  --> $DIR/unnecessary_operation.rs:87:5
    |
 LL | /     {
 LL | |         get_number()
@@ -112,7 +112,7 @@ LL | |     };
    | |______^ help: statement can be reduced to: `get_number();`
 
 error: unnecessary operation
-  --> $DIR/unnecessary_operation.rs:74:5
+  --> $DIR/unnecessary_operation.rs:90:5
    |
 LL | /     FooString {
 LL | |         s: String::from("blah"),
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
index 67faabc53cb..2dd1d746626 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -1,4 +1,10 @@
-#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
+#![allow(
+    clippy::needless_borrow,
+    clippy::needless_borrows_for_generic_args,
+    clippy::ptr_arg,
+    clippy::manual_async_fn,
+    clippy::needless_lifetimes
+)]
 #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
 
 use std::borrow::Cow;
@@ -506,3 +512,18 @@ mod issue_10033 {
         }
     }
 }
+
+mod issue_11952 {
+    use core::future::{Future, IntoFuture};
+
+    fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future<Output = Result<(), ()>> {
+        async move {
+            let _y = y;
+            Ok(())
+        }
+    }
+
+    fn bar() {
+        IntoFuture::into_future(foo([], &0));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
index 99f9136427d..17fad33402b 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -1,4 +1,10 @@
-#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
+#![allow(
+    clippy::needless_borrow,
+    clippy::needless_borrows_for_generic_args,
+    clippy::ptr_arg,
+    clippy::manual_async_fn,
+    clippy::needless_lifetimes
+)]
 #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
 
 use std::borrow::Cow;
@@ -506,3 +512,18 @@ mod issue_10033 {
         }
     }
 }
+
+mod issue_11952 {
+    use core::future::{Future, IntoFuture};
+
+    fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future<Output = Result<(), ()>> {
+        async move {
+            let _y = y;
+            Ok(())
+        }
+    }
+
+    fn bar() {
+        IntoFuture::into_future(foo([].to_vec(), &0));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
index d8971b51dca..ad6fa422b8c 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -1,11 +1,11 @@
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:148:64
+  --> $DIR/unnecessary_to_owned.rs:154:64
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                                                                ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:148:20
+  --> $DIR/unnecessary_to_owned.rs:154:20
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,55 +13,55 @@ LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned())
    = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]`
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:149:40
+  --> $DIR/unnecessary_to_owned.rs:155:40
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                                        ^^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:149:21
+  --> $DIR/unnecessary_to_owned.rs:155:21
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                     ^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:150:48
+  --> $DIR/unnecessary_to_owned.rs:156:48
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                                                ^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:150:19
+  --> $DIR/unnecessary_to_owned.rs:156:19
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:151:35
+  --> $DIR/unnecessary_to_owned.rs:157:35
    |
 LL |     require_str(&String::from("x").to_string());
    |                                   ^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:151:18
+  --> $DIR/unnecessary_to_owned.rs:157:18
    |
 LL |     require_str(&String::from("x").to_string());
    |                  ^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:152:39
+  --> $DIR/unnecessary_to_owned.rs:158:39
    |
 LL |     require_slice(&[String::from("x")].to_owned());
    |                                       ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:152:20
+  --> $DIR/unnecessary_to_owned.rs:158:20
    |
 LL |     require_slice(&[String::from("x")].to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:57:36
+  --> $DIR/unnecessary_to_owned.rs:63:36
    |
 LL |     require_c_str(&Cow::from(c_str).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
@@ -70,415 +70,415 @@ LL |     require_c_str(&Cow::from(c_str).into_owned());
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:58:19
+  --> $DIR/unnecessary_to_owned.rs:64:19
    |
 LL |     require_c_str(&c_str.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_os_string`
-  --> $DIR/unnecessary_to_owned.rs:60:20
+  --> $DIR/unnecessary_to_owned.rs:66:20
    |
 LL |     require_os_str(&os_str.to_os_string());
    |                    ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:61:38
+  --> $DIR/unnecessary_to_owned.rs:67:38
    |
 LL |     require_os_str(&Cow::from(os_str).into_owned());
    |                                      ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:62:20
+  --> $DIR/unnecessary_to_owned.rs:68:20
    |
 LL |     require_os_str(&os_str.to_owned());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_path_buf`
-  --> $DIR/unnecessary_to_owned.rs:64:18
+  --> $DIR/unnecessary_to_owned.rs:70:18
    |
 LL |     require_path(&path.to_path_buf());
    |                  ^^^^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:65:34
+  --> $DIR/unnecessary_to_owned.rs:71:34
    |
 LL |     require_path(&Cow::from(path).into_owned());
    |                                  ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:66:18
+  --> $DIR/unnecessary_to_owned.rs:72:18
    |
 LL |     require_path(&path.to_owned());
    |                  ^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:68:17
+  --> $DIR/unnecessary_to_owned.rs:74:17
    |
 LL |     require_str(&s.to_string());
    |                 ^^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:69:30
+  --> $DIR/unnecessary_to_owned.rs:75:30
    |
 LL |     require_str(&Cow::from(s).into_owned());
    |                              ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:70:17
+  --> $DIR/unnecessary_to_owned.rs:76:17
    |
 LL |     require_str(&s.to_owned());
    |                 ^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:71:17
+  --> $DIR/unnecessary_to_owned.rs:77:17
    |
 LL |     require_str(&x_ref.to_string());
    |                 ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:73:19
+  --> $DIR/unnecessary_to_owned.rs:79:19
    |
 LL |     require_slice(&slice.to_vec());
    |                   ^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:74:36
+  --> $DIR/unnecessary_to_owned.rs:80:36
    |
 LL |     require_slice(&Cow::from(slice).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:75:19
+  --> $DIR/unnecessary_to_owned.rs:81:19
    |
 LL |     require_slice(&array.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:76:19
+  --> $DIR/unnecessary_to_owned.rs:82:19
    |
 LL |     require_slice(&array_ref.to_owned());
    |                   ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:77:19
+  --> $DIR/unnecessary_to_owned.rs:83:19
    |
 LL |     require_slice(&slice.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:80:42
+  --> $DIR/unnecessary_to_owned.rs:86:42
    |
 LL |     require_x(&Cow::<X>::Owned(x.clone()).into_owned());
    |                                          ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:83:25
+  --> $DIR/unnecessary_to_owned.rs:89:25
    |
 LL |     require_deref_c_str(c_str.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:84:26
+  --> $DIR/unnecessary_to_owned.rs:90:26
    |
 LL |     require_deref_os_str(os_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:85:24
+  --> $DIR/unnecessary_to_owned.rs:91:24
    |
 LL |     require_deref_path(path.to_owned());
    |                        ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:86:23
+  --> $DIR/unnecessary_to_owned.rs:92:23
    |
 LL |     require_deref_str(s.to_owned());
    |                       ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:87:25
+  --> $DIR/unnecessary_to_owned.rs:93:25
    |
 LL |     require_deref_slice(slice.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:89:30
+  --> $DIR/unnecessary_to_owned.rs:95:30
    |
 LL |     require_impl_deref_c_str(c_str.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:90:31
+  --> $DIR/unnecessary_to_owned.rs:96:31
    |
 LL |     require_impl_deref_os_str(os_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:91:29
+  --> $DIR/unnecessary_to_owned.rs:97:29
    |
 LL |     require_impl_deref_path(path.to_owned());
    |                             ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:92:28
+  --> $DIR/unnecessary_to_owned.rs:98:28
    |
 LL |     require_impl_deref_str(s.to_owned());
    |                            ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:93:30
+  --> $DIR/unnecessary_to_owned.rs:99:30
    |
 LL |     require_impl_deref_slice(slice.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:95:29
+  --> $DIR/unnecessary_to_owned.rs:101:29
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:95:43
+  --> $DIR/unnecessary_to_owned.rs:101:43
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                                           ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:96:29
+  --> $DIR/unnecessary_to_owned.rs:102:29
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                             ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:96:47
+  --> $DIR/unnecessary_to_owned.rs:102:47
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                                               ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:98:26
+  --> $DIR/unnecessary_to_owned.rs:104:26
    |
 LL |     require_as_ref_c_str(c_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:99:27
+  --> $DIR/unnecessary_to_owned.rs:105:27
    |
 LL |     require_as_ref_os_str(os_str.to_owned());
    |                           ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:100:25
+  --> $DIR/unnecessary_to_owned.rs:106:25
    |
 LL |     require_as_ref_path(path.to_owned());
    |                         ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:101:24
+  --> $DIR/unnecessary_to_owned.rs:107:24
    |
 LL |     require_as_ref_str(s.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:102:24
+  --> $DIR/unnecessary_to_owned.rs:108:24
    |
 LL |     require_as_ref_str(x.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:103:26
+  --> $DIR/unnecessary_to_owned.rs:109:26
    |
 LL |     require_as_ref_slice(array.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:104:26
+  --> $DIR/unnecessary_to_owned.rs:110:26
    |
 LL |     require_as_ref_slice(array_ref.to_owned());
    |                          ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:105:26
+  --> $DIR/unnecessary_to_owned.rs:111:26
    |
 LL |     require_as_ref_slice(slice.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:107:31
+  --> $DIR/unnecessary_to_owned.rs:113:31
    |
 LL |     require_impl_as_ref_c_str(c_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:108:32
+  --> $DIR/unnecessary_to_owned.rs:114:32
    |
 LL |     require_impl_as_ref_os_str(os_str.to_owned());
    |                                ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:109:30
+  --> $DIR/unnecessary_to_owned.rs:115:30
    |
 LL |     require_impl_as_ref_path(path.to_owned());
    |                              ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:110:29
+  --> $DIR/unnecessary_to_owned.rs:116:29
    |
 LL |     require_impl_as_ref_str(s.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:111:29
+  --> $DIR/unnecessary_to_owned.rs:117:29
    |
 LL |     require_impl_as_ref_str(x.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:112:31
+  --> $DIR/unnecessary_to_owned.rs:118:31
    |
 LL |     require_impl_as_ref_slice(array.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:113:31
+  --> $DIR/unnecessary_to_owned.rs:119:31
    |
 LL |     require_impl_as_ref_slice(array_ref.to_owned());
    |                               ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:114:31
+  --> $DIR/unnecessary_to_owned.rs:120:31
    |
 LL |     require_impl_as_ref_slice(slice.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:116:30
+  --> $DIR/unnecessary_to_owned.rs:122:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:116:44
+  --> $DIR/unnecessary_to_owned.rs:122:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:117:30
+  --> $DIR/unnecessary_to_owned.rs:123:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:117:44
+  --> $DIR/unnecessary_to_owned.rs:123:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                                            ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:118:30
+  --> $DIR/unnecessary_to_owned.rs:124:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:118:44
+  --> $DIR/unnecessary_to_owned.rs:124:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:30
+  --> $DIR/unnecessary_to_owned.rs:125:30
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:48
+  --> $DIR/unnecessary_to_owned.rs:125:48
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:120:30
+  --> $DIR/unnecessary_to_owned.rs:126:30
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:120:52
+  --> $DIR/unnecessary_to_owned.rs:126:52
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                                                    ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:121:30
+  --> $DIR/unnecessary_to_owned.rs:127:30
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:121:48
+  --> $DIR/unnecessary_to_owned.rs:127:48
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:123:20
+  --> $DIR/unnecessary_to_owned.rs:129:20
    |
 LL |     let _ = x.join(&x_ref.to_string());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:125:13
+  --> $DIR/unnecessary_to_owned.rs:131:13
    |
 LL |     let _ = slice.to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:126:13
+  --> $DIR/unnecessary_to_owned.rs:132:13
    |
 LL |     let _ = slice.to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:127:13
+  --> $DIR/unnecessary_to_owned.rs:133:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:128:13
+  --> $DIR/unnecessary_to_owned.rs:134:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:130:13
+  --> $DIR/unnecessary_to_owned.rs:136:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:131:13
+  --> $DIR/unnecessary_to_owned.rs:137:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:132:13
+  --> $DIR/unnecessary_to_owned.rs:138:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:133:13
+  --> $DIR/unnecessary_to_owned.rs:139:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:195:14
+  --> $DIR/unnecessary_to_owned.rs:201:14
    |
 LL |     for t in file_types.to_vec() {
    |              ^^^^^^^^^^^^^^^^^^^
@@ -494,28 +494,34 @@ LL +         let path = match get_file_path(t) {
    |
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:218:14
+  --> $DIR/unnecessary_to_owned.rs:224:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:223:14
+  --> $DIR/unnecessary_to_owned.rs:229:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:270:24
+  --> $DIR/unnecessary_to_owned.rs:276:24
    |
 LL |         Box::new(build(y.to_string()))
    |                        ^^^^^^^^^^^^^ help: use: `y`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:378:12
+  --> $DIR/unnecessary_to_owned.rs:384:12
    |
 LL |         id("abc".to_string())
    |            ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
 
-error: aborting due to 79 previous errors
+error: unnecessary use of `to_vec`
+  --> $DIR/unnecessary_to_owned.rs:527:37
+   |
+LL |         IntoFuture::into_future(foo([].to_vec(), &0));
+   |                                     ^^^^^^^^^^^ help: use: `[]`
+
+error: aborting due to 80 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
index 373b18470f6..5ad117eb8db 100644
--- a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
@@ -1,6 +1,6 @@
 //@aux-build:proc_macros.rs
 
-#![allow(clippy::let_unit_value)]
+#![allow(clippy::let_unit_value, clippy::needless_pass_by_ref_mut)]
 #![warn(clippy::unnecessary_safety_doc)]
 
 extern crate proc_macros;
diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed
index bcbca971a78..81b8bd7da77 100644
--- a/src/tools/clippy/tests/ui/vec.fixed
+++ b/src/tools/clippy/tests/ui/vec.fixed
@@ -176,3 +176,37 @@ fn below() {
         let _: String = a;
     }
 }
+
+fn func_needing_vec(_bar: usize, _baz: Vec<usize>) {}
+fn func_not_needing_vec(_bar: usize, _baz: usize) {}
+
+fn issue11861() {
+    macro_rules! this_macro_needs_vec {
+        ($x:expr) => {{
+            func_needing_vec($x.iter().sum(), $x);
+            for _ in $x {}
+        }};
+    }
+    macro_rules! this_macro_doesnt_need_vec {
+        ($x:expr) => {{ func_not_needing_vec($x.iter().sum(), $x.iter().sum()) }};
+    }
+
+    // Do not lint the next line
+    this_macro_needs_vec!(vec![1]);
+    this_macro_doesnt_need_vec!([1]); //~ ERROR: useless use of `vec!`
+
+    macro_rules! m {
+        ($x:expr) => {
+            fn f2() {
+                let _x: Vec<i32> = $x;
+            }
+            fn f() {
+                let _x = $x;
+                $x.starts_with(&[]);
+            }
+        };
+    }
+
+    // should not lint
+    m!(vec![1]);
+}
diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs
index 087425585de..5aca9b2925c 100644
--- a/src/tools/clippy/tests/ui/vec.rs
+++ b/src/tools/clippy/tests/ui/vec.rs
@@ -176,3 +176,37 @@ fn below() {
         let _: String = a;
     }
 }
+
+fn func_needing_vec(_bar: usize, _baz: Vec<usize>) {}
+fn func_not_needing_vec(_bar: usize, _baz: usize) {}
+
+fn issue11861() {
+    macro_rules! this_macro_needs_vec {
+        ($x:expr) => {{
+            func_needing_vec($x.iter().sum(), $x);
+            for _ in $x {}
+        }};
+    }
+    macro_rules! this_macro_doesnt_need_vec {
+        ($x:expr) => {{ func_not_needing_vec($x.iter().sum(), $x.iter().sum()) }};
+    }
+
+    // Do not lint the next line
+    this_macro_needs_vec!(vec![1]);
+    this_macro_doesnt_need_vec!(vec![1]); //~ ERROR: useless use of `vec!`
+
+    macro_rules! m {
+        ($x:expr) => {
+            fn f2() {
+                let _x: Vec<i32> = $x;
+            }
+            fn f() {
+                let _x = $x;
+                $x.starts_with(&[]);
+            }
+        };
+    }
+
+    // should not lint
+    m!(vec![1]);
+}
diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr
index fc261838fe3..c9018f94f9d 100644
--- a/src/tools/clippy/tests/ui/vec.stderr
+++ b/src/tools/clippy/tests/ui/vec.stderr
@@ -115,5 +115,11 @@ error: useless use of `vec!`
 LL |     for a in vec![String::new(), String::new()] {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]`
 
-error: aborting due to 19 previous errors
+error: useless use of `vec!`
+  --> $DIR/vec.rs:196:33
+   |
+LL |     this_macro_doesnt_need_vec!(vec![1]);
+   |                                 ^^^^^^^ help: you can use an array directly: `[1]`
+
+error: aborting due to 20 previous errors
 
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index ab2fb1a3291..96085bcf9ee 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -19,6 +19,7 @@ new_pr = true
 
 [assign]
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
+users_on_vacation = ["blyxyas"]
 
 [assign.owners]
 "/.github" = ["@flip1995"]
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 3a23ff7fe6a..865d7172cd3 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -173,7 +173,7 @@ fn parse_args() -> (OutputFormat, PathBuf) {
 
 fn main() {
     let handler =
-        rustc_session::EarlyErrorHandler::new(rustc_session::config::ErrorOutputType::default());
+        rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
     rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUST_LOG"));
     let (format, dst) = parse_args();
     let result = main_with_result(format, &dst);
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 8390309889d..f83847d13f1 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -35,7 +35,7 @@ use rustc_middle::{
 };
 use rustc_session::config::{CrateType, ErrorOutputType, OptLevel};
 use rustc_session::search_paths::PathKind;
-use rustc_session::{CtfeBacktrace, EarlyErrorHandler};
+use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
 
 use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
 
@@ -69,8 +69,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
                 tcx.sess.fatal("miri cannot be run on programs that fail compilation");
             }
 
-            let handler = EarlyErrorHandler::new(tcx.sess.opts.error_format);
-            init_late_loggers(&handler, tcx);
+            let early_dcx = EarlyDiagCtxt::new(tcx.sess.opts.error_format);
+            init_late_loggers(&early_dcx, tcx);
             if !tcx.crate_types().contains(&CrateType::Executable) {
                 tcx.sess.fatal("miri only makes sense on bin crates");
             }
@@ -215,7 +215,7 @@ fn rustc_logger_config() -> rustc_log::LoggerConfig {
     cfg
 }
 
-fn init_early_loggers(handler: &EarlyErrorHandler) {
+fn init_early_loggers(early_dcx: &EarlyDiagCtxt) {
     // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
     // initialize them both, and we always initialize `miri`'s first.
     let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
@@ -224,15 +224,15 @@ fn init_early_loggers(handler: &EarlyErrorHandler) {
     // If it is not set, we avoid initializing now so that we can initialize later with our custom
     // settings, and *not* log anything for what happens before `miri` gets started.
     if env::var_os("RUSTC_LOG").is_some() {
-        rustc_driver::init_logger(handler, rustc_logger_config());
+        rustc_driver::init_logger(early_dcx, rustc_logger_config());
     }
 }
 
-fn init_late_loggers(handler: &EarlyErrorHandler, tcx: TyCtxt<'_>) {
+fn init_late_loggers(early_dcx: &EarlyDiagCtxt, tcx: TyCtxt<'_>) {
     // If `RUSTC_LOG` is not set, then `init_early_loggers` did not call
     // `rustc_driver::init_logger`, so we have to do this now.
     if env::var_os("RUSTC_LOG").is_none() {
-        rustc_driver::init_logger(handler, rustc_logger_config());
+        rustc_driver::init_logger(early_dcx, rustc_logger_config());
     }
 
     // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
@@ -300,7 +300,7 @@ fn parse_comma_list<T: FromStr>(input: &str) -> Result<Vec<T>, T::Err> {
 }
 
 fn main() {
-    let handler = EarlyErrorHandler::new(ErrorOutputType::default());
+    let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
     // Snapshot a copy of the environment before `rustc` starts messing with it.
     // (`install_ice_hook` might change `RUST_BACKTRACE`.)
@@ -311,7 +311,7 @@ fn main() {
         // Earliest rustc setup.
         let using_internal_features =
             rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ());
-        rustc_driver::init_rustc_env_logger(&handler);
+        rustc_driver::init_rustc_env_logger(&early_dcx);
 
         let target_crate = if crate_kind == "target" {
             true
@@ -335,7 +335,7 @@ fn main() {
         rustc_driver::install_ice_hook("https://github.com/rust-lang/miri/issues/new", |_| ());
 
     // Init loggers the Miri way.
-    init_early_loggers(&handler);
+    init_early_loggers(&early_dcx);
 
     // Parse our arguments and split them across `rustc` and `miri`.
     let mut miri_config = miri::MiriConfig::default();
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index e0f59144975..a70a9d4602e 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -384,7 +384,7 @@ pub fn report_error<'tcx, 'mir>(
 
     // Include a note like `std` does when we omit frames from a backtrace
     if was_pruned {
-        ecx.tcx.sess.diagnostic().note(
+        ecx.tcx.sess.dcx().note(
             "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
         );
     }
@@ -431,7 +431,7 @@ pub fn report_leaks<'mir, 'tcx>(
         );
     }
     if any_pruned {
-        ecx.tcx.sess.diagnostic().note(
+        ecx.tcx.sess.dcx().note(
             "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
         );
     }
@@ -456,7 +456,7 @@ pub fn report_msg<'tcx>(
     let mut err = match diag_level {
         DiagLevel::Error => sess.struct_span_err(span, title).forget_guarantee(),
         DiagLevel::Warning => sess.struct_span_warn(span, title),
-        DiagLevel::Note => sess.diagnostic().struct_span_note(span, title),
+        DiagLevel::Note => sess.dcx().struct_span_note(span, title),
     };
 
     // Show main message.
diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
index 741e559953f..e6a9917a0bf 100644
--- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
@@ -67,7 +67,7 @@ jobs:
   other_metrics:
     strategy:
       matrix:
-        names: [self, ripgrep-13.0.0, webrender-2022, diesel-1.4.8, hyper-0.14.18]
+        names: [self, rustc_tests, ripgrep-13.0.0, webrender-2022, diesel-1.4.8, hyper-0.14.18]
     runs-on: ubuntu-latest
     needs: [setup_cargo, build_metrics]
 
@@ -118,6 +118,11 @@ jobs:
         with:
           name: self-${{ github.sha }}
 
+      - name: Download rustc_tests metrics
+        uses: actions/download-artifact@v3
+        with:
+          name: rustc_tests-${{ github.sha }}
+
       - name: Download ripgrep-13.0.0 metrics
         uses: actions/download-artifact@v3
         with:
@@ -146,7 +151,7 @@ jobs:
           chmod 700 ~/.ssh
 
           git clone --depth 1 git@github.com:rust-analyzer/metrics.git
-          jq -s ".[0] * .[1] * .[2] * .[3] * .[4] * .[5]" build.json self.json ripgrep-13.0.0.json webrender-2022.json diesel-1.4.8.json hyper-0.14.18.json -c >> metrics/metrics.json
+          jq -s ".[0] * .[1] * .[2] * .[3] * .[4] * .[5] * .[6]" build.json self.json rustc_tests.json ripgrep-13.0.0.json webrender-2022.json diesel-1.4.8.json hyper-0.14.18.json -c >> metrics/metrics.json
           cd metrics
           git add .
           git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 46efbdd93c9..227d1db0ec7 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -684,7 +684,7 @@ dependencies = [
  "indexmap",
  "itertools",
  "limit",
- "line-index 0.1.0-pre.1",
+ "line-index 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr",
  "nohash-hasher",
  "once_cell",
@@ -881,9 +881,7 @@ version = "0.0.0"
 
 [[package]]
 name = "line-index"
-version = "0.1.0-pre.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cad96769710c1745e11d4f940a8ff36000ade4bbada4285b001cb8aa2f745ce"
+version = "0.1.1"
 dependencies = [
  "nohash-hasher",
  "text-size",
@@ -891,7 +889,9 @@ dependencies = [
 
 [[package]]
 name = "line-index"
-version = "0.1.0"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67d61795376ae2683928c218fda7d7d7db136fd38c06b7552904667f0d55580a"
 dependencies = [
  "nohash-hasher",
  "text-size",
@@ -1545,6 +1545,7 @@ dependencies = [
  "triomphe",
  "vfs",
  "vfs-notify",
+ "walkdir",
  "winapi",
  "xflags",
  "xshell",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index f3f01aab8ee..1213979c390 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -85,7 +85,7 @@ rustc-dependencies = { path = "./crates/rustc-dependencies", version = "0.0.0" }
 proc-macro-test = { path = "./crates/proc-macro-test" }
 
 # In-tree crates that are published separately and follow semver. See lib/README.md
-line-index = { version = "0.1.0-pre.1" }
+line-index = { version = "0.1.1" }
 la-arena = { version = "0.3.1" }
 lsp-server = { version = "0.7.4" }
 
diff --git a/src/tools/rust-analyzer/crates/base-db/src/span.rs b/src/tools/rust-analyzer/crates/base-db/src/span.rs
index 3464f4cb6d1..d8990eb7cae 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/span.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/span.rs
@@ -151,21 +151,26 @@ impl fmt::Debug for HirFileIdRepr {
 
 impl From<FileId> for HirFileId {
     fn from(id: FileId) -> Self {
-        assert!(id.index() < Self::MAX_FILE_ID);
+        _ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
+        assert!(id.index() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.index());
         HirFileId(id.index())
     }
 }
 
 impl From<MacroFileId> for HirFileId {
     fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self {
+        _ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
         let id = id.as_u32();
-        assert!(id < Self::MAX_FILE_ID);
+        assert!(id <= Self::MAX_HIR_FILE_ID, "MacroCallId index {} is too large", id);
         HirFileId(id | Self::MACRO_FILE_TAG_MASK)
     }
 }
 
 impl HirFileId {
-    const MAX_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
+    const ASSERT_MAX_FILE_ID_IS_SAME: () =
+        [()][(Self::MAX_HIR_FILE_ID != FileId::MAX_FILE_ID) as usize];
+
+    const MAX_HIR_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
     const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
 
     #[inline]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index 15dceeb8af2..48a596f7f53 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -2,7 +2,7 @@
 //!
 //! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
 //!
-//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6.
+//! It was last synchronized with upstream commit c3def263a44e07e09ae6d57abfc8650227fb4972.
 //!
 //! The macros were adjusted to only expand to the attribute name, since that is all we need to do
 //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
@@ -240,7 +240,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
         experimental!(no_sanitize)
     ),
-    gated!(coverage, Normal, template!(Word, List: "on|off"), WarnFollowing, experimental!(coverage)),
+    gated!(coverage, Normal, template!(Word, List: "on|off"), WarnFollowing, coverage_attribute, experimental!(coverage)),
 
     ungated!(
         doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
@@ -364,7 +364,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
         "allow_internal_unsafe side-steps the unsafe_code lint",
     ),
-    ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk),
     rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
     "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
     through unstable paths"),
@@ -453,6 +452,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         ErrorFollowing,
         INTERNAL_UNSTABLE
     ),
+    rustc_attr!(
+        rustc_confusables, Normal,
+        template!(List: r#""name1", "name2", ..."#),
+        ErrorFollowing,
+        INTERNAL_UNSTABLE,
+    ),
     // Enumerates "identity-like" conversion methods to suggest on type mismatch.
     rustc_attr!(
         rustc_conversion_suggestion, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
@@ -488,6 +493,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
     ),
+    // Ensure the argument to this function is &&str during const-check.
+    rustc_attr!(
+        rustc_const_panic_str, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
+    ),
 
     // ==========================================================================
     // Internal attributes, Layout related:
@@ -521,6 +530,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
     ),
     rustc_attr!(
+        rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing,
+        "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers."
+    ),
+    rustc_attr!(
         rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true,
         "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
     ),
@@ -533,7 +546,11 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
     ),
     rustc_attr!(
-        rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
+        rustc_deny_explicit_impl,
+        AttributeType::Normal,
+        template!(List: "implement_via_object = (true|false)"),
+        ErrorFollowing,
+        @only_local: true,
         "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
     ),
     rustc_attr!(
@@ -614,6 +631,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing,
         r#"`rustc_doc_primitive` is a rustc internal attribute"#,
     ),
+    rustc_attr!(
+        rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing,
+        "the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe"
+    ),
 
     // ==========================================================================
     // Internal attributes, Testing:
@@ -625,13 +646,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing),
+    rustc_attr!(TEST, rustc_variance_of_opaques, Normal, template!(Word), WarnFollowing),
+    rustc_attr!(TEST, rustc_hidden_type_of_opaques, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing),
+    rustc_attr!(TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."), WarnFollowing),
     rustc_attr!(TEST, rustc_regions, Normal, template!(Word), WarnFollowing),
     rustc_attr!(
         TEST, rustc_error, Normal,
         template!(Word, List: "span_delayed_bug_from_inside_query"), WarnFollowingWordOnly
     ),
-    rustc_attr!(TEST, rustc_dump_user_substs, Normal, template!(Word), WarnFollowing),
+    rustc_attr!(TEST, rustc_dump_user_args, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_evaluate_where_clauses, Normal, template!(Word), WarnFollowing),
     rustc_attr!(
         TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), DuplicatesOk
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 4cfd318a433..c82d2347de5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -5,8 +5,7 @@
 //! node for a *child*, and get its hir.
 
 use either::Either;
-use hir_expand::HirFileId;
-use syntax::ast::HasDocComments;
+use hir_expand::{attrs::collect_attrs, HirFileId};
 
 use crate::{
     db::DefDatabase,
@@ -118,8 +117,8 @@ impl ChildBySource for ItemScope {
             |(ast_id, calls)| {
                 let adt = ast_id.to_node(db.upcast());
                 calls.for_each(|(attr_id, call_id, calls)| {
-                    if let Some(Either::Left(attr)) =
-                        adt.doc_comments_and_attrs().nth(attr_id.ast_index())
+                    if let Some((_, Either::Left(attr))) =
+                        collect_attrs(&adt).nth(attr_id.ast_index())
                     {
                         res[keys::DERIVE_MACRO_CALL].insert(attr, (attr_id, call_id, calls.into()));
                     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index 0d95d916ff9..f5324f052e5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -222,11 +222,10 @@ impl GenericParams {
                 let module = loc.container.module(db);
                 let func_data = db.function_data(id);
 
-                // Don't create an `Expander` nor call `loc.source(db)` if not needed since this
-                // causes a reparse after the `ItemTree` has been created.
-                let mut expander = Lazy::new(|| {
-                    (module.def_map(db), Expander::new(db, loc.source(db).file_id, module))
-                });
+                // Don't create an `Expander` if not needed since this
+                // could cause a reparse after the `ItemTree` has been created due to the spanmap.
+                let mut expander =
+                    Lazy::new(|| (module.def_map(db), Expander::new(db, loc.id.file_id(), module)));
                 for param in func_data.params.iter() {
                     generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index d589fbe347a..26d333f9a0b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -9,6 +9,7 @@ use indexmap::IndexMap;
 use itertools::Itertools;
 use rustc_hash::{FxHashSet, FxHasher};
 use smallvec::SmallVec;
+use stdx::format_to;
 use triomphe::Arc;
 
 use crate::{
@@ -53,13 +54,25 @@ pub struct ImportMap {
     fst: fst::Map<Vec<u8>>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
 enum IsTraitAssocItem {
     Yes,
     No,
 }
 
 impl ImportMap {
+    pub fn dump(&self, db: &dyn DefDatabase) -> String {
+        let mut out = String::new();
+        for (k, v) in self.map.iter() {
+            format_to!(out, "{:?} ({:?}) -> ", k, v.1);
+            for v in &v.0 {
+                format_to!(out, "{}:{:?}, ", v.name.display(db.upcast()), v.container);
+            }
+            format_to!(out, "\n");
+        }
+        out
+    }
+
     pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
         let _p = profile::span("import_map_query");
 
@@ -68,26 +81,31 @@ impl ImportMap {
         let mut importables: Vec<_> = map
             .iter()
             // We've only collected items, whose name cannot be tuple field.
-            .flat_map(|(&item, (info, _))| {
-                info.iter()
-                    .map(move |info| (item, info.name.as_str().unwrap().to_ascii_lowercase()))
+            .flat_map(|(&item, (info, is_assoc))| {
+                info.iter().map(move |info| {
+                    (item, *is_assoc, info.name.as_str().unwrap().to_ascii_lowercase())
+                })
             })
             .collect();
-        importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name));
+        importables.sort_by(|(_, l_is_assoc, lhs_name), (_, r_is_assoc, rhs_name)| {
+            lhs_name.cmp(rhs_name).then_with(|| l_is_assoc.cmp(r_is_assoc))
+        });
         importables.dedup();
 
         // Build the FST, taking care not to insert duplicate values.
         let mut builder = fst::MapBuilder::memory();
-        let iter =
-            importables.iter().enumerate().dedup_by(|(_, (_, lhs)), (_, (_, rhs))| lhs == rhs);
-        for (start_idx, (_, name)) in iter {
+        let iter = importables
+            .iter()
+            .enumerate()
+            .dedup_by(|(_, (_, _, lhs)), (_, (_, _, rhs))| lhs == rhs);
+        for (start_idx, (_, _, name)) in iter {
             let _ = builder.insert(name, start_idx as u64);
         }
 
         Arc::new(ImportMap {
             map,
             fst: builder.into_map(),
-            importables: importables.into_iter().map(|(item, _)| item).collect(),
+            importables: importables.into_iter().map(|(item, _, _)| item).collect(),
         })
     }
 
@@ -328,20 +346,20 @@ impl Query {
     }
 
     /// Checks whether the import map entry matches the query.
-    fn import_matches(
-        &self,
-        db: &dyn DefDatabase,
-        import: &ImportInfo,
-        enforce_lowercase: bool,
-    ) -> bool {
+    fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
         let _p = profile::span("import_map::Query::import_matches");
 
         // FIXME: Can we get rid of the alloc here?
-        let mut input = import.name.display(db.upcast()).to_string();
+        let input = import.name.to_smol_str();
+        let mut _s_slot;
         let case_insensitive = enforce_lowercase || !self.case_sensitive;
-        if case_insensitive {
-            input.make_ascii_lowercase();
-        }
+        let input = if case_insensitive {
+            _s_slot = String::from(input);
+            _s_slot.make_ascii_lowercase();
+            &*_s_slot
+        } else {
+            &*input
+        };
 
         let query_string = if case_insensitive { &self.lowercased } else { &self.query };
 
@@ -351,7 +369,7 @@ impl Query {
             SearchMode::Fuzzy => {
                 let mut input_chars = input.chars();
                 for query_char in query_string.chars() {
-                    if input_chars.find(|&it| it == query_char).is_none() {
+                    if !input_chars.any(|it| it == query_char) {
                         return false;
                     }
                 }
@@ -372,6 +390,7 @@ pub fn search_dependencies(
     let _p = profile::span("search_dependencies").detail(|| format!("{query:?}"));
 
     let graph = db.crate_graph();
+
     let import_maps: Vec<_> =
         graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
 
@@ -386,22 +405,23 @@ pub fn search_dependencies(
 
     let mut res = FxHashSet::default();
     let mut common_importable_data_scratch = vec![];
+    // FIXME: Improve this, its rather unreadable and does duplicate amount of work
     while let Some((_, indexed_values)) = stream.next() {
         for &IndexedValue { index, value } in indexed_values {
             let import_map = &import_maps[index];
             let importables @ [importable, ..] = &import_map.importables[value as usize..] else {
                 continue;
             };
-
             let &(ref importable_data, is_trait_assoc_item) = &import_map.map[importable];
             if !query.matches_assoc_mode(is_trait_assoc_item) {
                 continue;
             }
 
+            // Fetch all the known names of this importable item (to handle import aliases/renames)
             common_importable_data_scratch.extend(
                 importable_data
                     .iter()
-                    .filter(|&info| query.import_matches(db, info, true))
+                    .filter(|&info| query.import_matches(info, true))
                     // Name shared by the importable items in this group.
                     .map(|info| info.name.to_smol_str()),
             );
@@ -415,6 +435,7 @@ pub fn search_dependencies(
                 common_importable_data_scratch.drain(..).flat_map(|common_importable_name| {
                     // Add the items from this name group. Those are all subsequent items in
                     // `importables` whose name match `common_importable_name`.
+
                     importables
                         .iter()
                         .copied()
@@ -430,11 +451,8 @@ pub fn search_dependencies(
                         .filter(move |item| {
                             !query.case_sensitive || {
                                 // we've already checked the common importables name case-insensitively
-                                let &(ref import_infos, assoc_mode) = &import_map.map[item];
-                                query.matches_assoc_mode(assoc_mode)
-                                    && import_infos
-                                        .iter()
-                                        .any(|info| query.import_matches(db, info, false))
+                                let &(ref import_infos, _) = &import_map.map[item];
+                                import_infos.iter().any(|info| query.import_matches(info, false))
                             }
                         })
                 });
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 16144394e3b..3d2cddffa3b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -106,11 +106,6 @@ impl ItemTree {
     pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
         let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
         let syntax = db.parse_or_expand(file_id);
-        if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax)
-        {
-            // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
-            return Default::default();
-        }
 
         let ctx = lower::Ctx::new(db, file_id);
         let mut top_attrs = None;
@@ -129,6 +124,9 @@ impl ItemTree {
                     ctx.lower_macro_stmts(stmts)
                 },
                 _ => {
+                    if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax) {
+                        return Default::default();
+                    }
                     panic!("cannot create item tree for file {file_id:?} from {syntax:?} {syntax}");
                 },
             }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 7cf13a202e0..b5333861cc8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -569,6 +569,8 @@ pub struct ConstBlockLoc {
     pub root: hir::ExprId,
 }
 
+/// Something that holds types, required for the current const arg lowering implementation as they
+/// need to be able to query where they are defined.
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
 pub enum TypeOwnerId {
     FunctionId(FunctionId),
@@ -581,9 +583,6 @@ pub enum TypeOwnerId {
     TypeAliasId(TypeAliasId),
     ImplId(ImplId),
     EnumVariantId(EnumVariantId),
-    // FIXME(const-generic-body): ModuleId should not be a type owner. This needs to be fixed to make `TypeOwnerId` actually
-    // useful for assigning ids to in type consts.
-    ModuleId(ModuleId),
 }
 
 impl TypeOwnerId {
@@ -597,9 +596,7 @@ impl TypeOwnerId {
             TypeOwnerId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
             TypeOwnerId::ImplId(it) => GenericDefId::ImplId(it),
             TypeOwnerId::EnumVariantId(it) => GenericDefId::EnumVariantId(it),
-            TypeOwnerId::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => {
-                return None
-            }
+            TypeOwnerId::InTypeConstId(_) | TypeOwnerId::StaticId(_) => return None,
         })
     }
 }
@@ -614,8 +611,7 @@ impl_from!(
     TraitAliasId,
     TypeAliasId,
     ImplId,
-    EnumVariantId,
-    ModuleId
+    EnumVariantId
     for TypeOwnerId
 );
 
@@ -713,12 +709,15 @@ pub struct InTypeConstLoc {
     pub id: AstId<ast::ConstArg>,
     /// The thing this const arg appears in
     pub owner: TypeOwnerId,
-    pub thing: Box<dyn OpaqueInternableThing>,
+    // FIXME(const-generic-body): The expected type should not be
+    pub expected_ty: Box<dyn OpaqueInternableThing>,
 }
 
 impl PartialEq for InTypeConstLoc {
     fn eq(&self, other: &Self) -> bool {
-        self.id == other.id && self.owner == other.owner && &*self.thing == &*other.thing
+        self.id == other.id
+            && self.owner == other.owner
+            && &*self.expected_ty == &*other.expected_ty
     }
 }
 
@@ -1041,7 +1040,6 @@ impl HasModule for TypeOwnerId {
             TypeOwnerId::TypeAliasId(it) => it.lookup(db).module(db),
             TypeOwnerId::ImplId(it) => it.lookup(db).container,
             TypeOwnerId::EnumVariantId(it) => it.parent.lookup(db).container,
-            TypeOwnerId::ModuleId(it) => *it,
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 9010050ee67..71ba4972174 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -13,37 +13,97 @@ fn test_vec() {
     check(
         r#"
 macro_rules! vec {
-   ($($item:expr),*) => {{
-           let mut v = Vec::new();
-           $( v.push($item); )*
-           v
-    }};
+    () => (
+        $crate::__rust_force_expr!($crate::vec::Vec::new())
+    );
+    ($elem:expr; $n:expr) => (
+        $crate::__rust_force_expr!($crate::vec::from_elem($elem, $n))
+    );
+    ($($x:expr),+ $(,)?) => (
+        $crate::__rust_force_expr!(<[_]>::into_vec(
+            // This rustc_box is not required, but it produces a dramatic improvement in compile
+            // time when constructing arrays with many elements.
+            #[rustc_box]
+            $crate::boxed::Box::new([$($x),+])
+        ))
+    );
+}
+
+macro_rules! __rust_force_expr {
+    ($e:expr) => {
+        $e
+    };
 }
+
 fn main() {
     vec!();
     vec![1u32,2];
+    vec![a.];
 }
 "#,
         expect![[r#"
 macro_rules! vec {
-   ($($item:expr),*) => {{
-           let mut v = Vec::new();
-           $( v.push($item); )*
-           v
-    }};
+    () => (
+        $crate::__rust_force_expr!($crate::vec::Vec::new())
+    );
+    ($elem:expr; $n:expr) => (
+        $crate::__rust_force_expr!($crate::vec::from_elem($elem, $n))
+    );
+    ($($x:expr),+ $(,)?) => (
+        $crate::__rust_force_expr!(<[_]>::into_vec(
+            // This rustc_box is not required, but it produces a dramatic improvement in compile
+            // time when constructing arrays with many elements.
+            #[rustc_box]
+            $crate::boxed::Box::new([$($x),+])
+        ))
+    );
+}
+
+macro_rules! __rust_force_expr {
+    ($e:expr) => {
+        $e
+    };
 }
+
 fn main() {
-     {
-        let mut v = Vec::new();
-        v
+    $crate::__rust_force_expr!($crate:: vec:: Vec:: new());
+    $crate::__rust_force_expr!(<[_]>:: into_vec(#[rustc_box]$crate:: boxed:: Box:: new([1u32, 2])));
+    /* error: expected Expr */$crate::__rust_force_expr!($crate:: vec:: from_elem((a.), $n));
+}
+"#]],
+    );
+    // FIXME we should ahev testing infra for multi level expansion tests
+    check(
+        r#"
+macro_rules! __rust_force_expr {
+    ($e:expr) => {
+        $e
     };
-     {
-        let mut v = Vec::new();
-        v.push(1u32);
-        v.push(2);
-        v
+}
+
+fn main() {
+    __rust_force_expr!(crate:: vec:: Vec:: new());
+    __rust_force_expr!(<[_]>:: into_vec(#[rustc_box] crate:: boxed:: Box:: new([1u32, 2])));
+    __rust_force_expr/*+errors*/!(crate:: vec:: from_elem((a.), $n));
+}
+"#,
+        expect![[r#"
+macro_rules! __rust_force_expr {
+    ($e:expr) => {
+        $e
     };
 }
+
+fn main() {
+    (crate ::vec::Vec::new());
+    (<[_]>::into_vec(#[rustc_box] crate ::boxed::Box::new([1u32, 2])));
+    /* error: expected Expr *//* parse error: expected field name or number */
+/* parse error: expected expression */
+/* parse error: expected R_PAREN */
+/* parse error: expected COMMA */
+/* parse error: expected expression, item or let statement */
+(crate ::vec::from_elem((a.), $n));
+}
 "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index ba0a2c0224a..2ac1516ec07 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -589,6 +589,16 @@ impl Resolver {
         })
     }
 
+    pub fn type_owner(&self) -> Option<TypeOwnerId> {
+        self.scopes().find_map(|scope| match scope {
+            Scope::BlockScope(_) => None,
+            &Scope::GenericParams { def, .. } => Some(def.into()),
+            &Scope::ImplDefScope(id) => Some(id.into()),
+            &Scope::AdtScope(adt) => Some(adt.into()),
+            Scope::ExprScope(it) => Some(it.owner.into()),
+        })
+    }
+
     pub fn impl_def(&self) -> Option<ImplId> {
         self.scopes().find_map(|scope| match scope {
             Scope::ImplDefScope(def) => Some(*def),
@@ -1079,7 +1089,6 @@ impl HasResolver for TypeOwnerId {
             TypeOwnerId::TypeAliasId(it) => it.resolver(db),
             TypeOwnerId::ImplId(it) => it.resolver(db),
             TypeOwnerId::EnumVariantId(it) => it.resolver(db),
-            TypeOwnerId::ModuleId(it) => it.resolver(db),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index d2c6559b06b..935669d49b5 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -10,17 +10,17 @@ use limit::Limit;
 use mbe::{syntax_node_to_token_tree, ValueResult};
 use rustc_hash::FxHashSet;
 use syntax::{
-    ast::{self, HasAttrs, HasDocComments},
+    ast::{self, HasAttrs},
     AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
 };
 use triomphe::Arc;
 
 use crate::{
     ast_id_map::AstIdMap,
-    attrs::RawAttrs,
+    attrs::{collect_attrs, RawAttrs},
     builtin_attr_macro::pseudo_derive_attr_expansion,
     builtin_fn_macro::EagerExpander,
-    fixup::{self, SyntaxFixupUndoInfo},
+    fixup::{self, reverse_fixups, SyntaxFixupUndoInfo},
     hygiene::{apply_mark, SyntaxContextData, Transparency},
     span::{RealSpanMap, SpanMap, SpanMapRef},
     tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
@@ -216,9 +216,9 @@ pub fn expand_speculative(
                 // Attributes may have an input token tree, build the subtree and map for this as well
                 // then try finding a token id for our token if it is inside this input subtree.
                 let item = ast::Item::cast(speculative_args.clone())?;
-                item.doc_comments_and_attrs()
+                collect_attrs(&item)
                     .nth(invoc_attr_index.ast_index())
-                    .and_then(Either::left)
+                    .and_then(|x| Either::left(x.1))
             }?;
             match attr.token_tree() {
                 Some(token_tree) => {
@@ -421,6 +421,15 @@ fn macro_arg(
                     syntax::NodeOrToken::Token(_) => true,
                 });
                 fixups.remove.extend(censor);
+                {
+                    let mut tt = mbe::syntax_node_to_token_tree_modified(
+                        &syntax,
+                        map.as_ref(),
+                        fixups.append.clone(),
+                        fixups.remove.clone(),
+                    );
+                    reverse_fixups(&mut tt, &fixups.undo_info);
+                }
                 (
                     mbe::syntax_node_to_token_tree_modified(
                         &syntax,
@@ -479,10 +488,9 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
             MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None,
             MacroCallKind::Attr { invoc_attr_index, .. } => {
                 cov_mark::hit!(attribute_macro_attr_censoring);
-                ast::Item::cast(node.clone())?
-                    .doc_comments_and_attrs()
+                collect_attrs(&ast::Item::cast(node.clone())?)
                     .nth(invoc_attr_index.ast_index())
-                    .and_then(Either::left)
+                    .and_then(|x| Either::left(x.1))
                     .map(|attr| attr.syntax().clone())
                     .into_iter()
                     .collect()
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
index 1e2722e8464..8d55240aef5 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
@@ -88,7 +88,7 @@ pub fn expand_eager_macro_input(
     let loc = MacroCallLoc {
         def,
         krate,
-        eager: Some(Box::new(EagerCallInfo { arg: Arc::new(subtree), arg_id, error: err.clone() })),
+        eager: Some(Arc::new(EagerCallInfo { arg: Arc::new(subtree), arg_id, error: err.clone() })),
         kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
         call_site,
     };
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
index 11775c531d4..346cd39a767 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -8,12 +8,13 @@ use base_db::{
 use la_arena::RawIdx;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
+use stdx::never;
 use syntax::{
     ast::{self, AstNode, HasLoopBody},
     match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize,
 };
 use triomphe::Arc;
-use tt::Spacing;
+use tt::{Spacing, Span};
 
 use crate::{
     span::SpanMapRef,
@@ -45,19 +46,20 @@ impl SyntaxFixupUndoInfo {
 // replacement -> censor + append
 // append -> insert a fake node, here we need to assemble some dummy span that we can figure out how
 // to remove later
+const FIXUP_DUMMY_FILE: FileId = FileId::from_raw(FileId::MAX_FILE_ID);
+const FIXUP_DUMMY_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(RawIdx::from_u32(!0));
+const FIXUP_DUMMY_RANGE: TextRange = TextRange::empty(TextSize::new(0));
+const FIXUP_DUMMY_RANGE_END: TextSize = TextSize::new(!0);
 
 pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> SyntaxFixups {
     let mut append = FxHashMap::<SyntaxElement, _>::default();
     let mut remove = FxHashSet::<SyntaxNode>::default();
     let mut preorder = node.preorder();
     let mut original = Vec::new();
-    let dummy_range = TextRange::empty(TextSize::new(0));
+    let dummy_range = FIXUP_DUMMY_RANGE;
     // we use a file id of `FileId(!0)` to signal a fake node, and the text range's start offset as
     // the index into the replacement vec but only if the end points to !0
-    let dummy_anchor = SpanAnchor {
-        file_id: FileId::from_raw(!0),
-        ast_id: ErasedFileAstId::from_raw(RawIdx::from(!0)),
-    };
+    let dummy_anchor = SpanAnchor { file_id: FIXUP_DUMMY_FILE, ast_id: FIXUP_DUMMY_AST_ID };
     let fake_span = |range| SpanData {
         range: dummy_range,
         anchor: dummy_anchor,
@@ -76,7 +78,7 @@ pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> Synta
             let replacement = Leaf::Ident(Ident {
                 text: "__ra_fixup".into(),
                 span: SpanData {
-                    range: TextRange::new(TextSize::new(idx), TextSize::new(!0)),
+                    range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END),
                     anchor: dummy_anchor,
                     ctx: span_map.span_for_range(node_range).ctx,
                 },
@@ -299,6 +301,13 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool {
 pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) {
     let Some(undo_info) = undo_info.original.as_deref() else { return };
     let undo_info = &**undo_info;
+    if never!(
+        tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE
+            || tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE
+    ) {
+        tt.delimiter.close = SpanData::DUMMY;
+        tt.delimiter.open = SpanData::DUMMY;
+    }
     reverse_fixups_(tt, undo_info);
 }
 
@@ -310,17 +319,28 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
         .filter(|tt| match tt {
             tt::TokenTree::Leaf(leaf) => {
                 let span = leaf.span();
-                span.anchor.file_id != FileId::from_raw(!0) || span.range.end() == TextSize::new(!0)
+                let is_real_leaf = span.anchor.file_id != FIXUP_DUMMY_FILE;
+                let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END;
+                is_real_leaf || is_replaced_node
             }
             tt::TokenTree::Subtree(_) => true,
         })
         .flat_map(|tt| match tt {
             tt::TokenTree::Subtree(mut tt) => {
+                if tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE
+                    || tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE
+                {
+                    // Even though fixup never creates subtrees with fixup spans, the old proc-macro server
+                    // might copy them if the proc-macro asks for it, so we need to filter those out
+                    // here as well.
+                    return SmallVec::new_const();
+                }
                 reverse_fixups_(&mut tt, undo_info);
                 SmallVec::from_const([tt.into()])
             }
             tt::TokenTree::Leaf(leaf) => {
-                if leaf.span().anchor.file_id == FileId::from_raw(!0) {
+                if leaf.span().anchor.file_id == FIXUP_DUMMY_FILE {
+                    // we have a fake node here, we need to replace it again with the original
                     let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone();
                     if original.delimiter.kind == tt::DelimiterKind::Invisible {
                         original.token_trees.into()
@@ -328,6 +348,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
                         SmallVec::from_const([original.into()])
                     }
                 } else {
+                    // just a normal leaf
                     SmallVec::from_const([leaf.into()])
                 }
             }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 74089593ac0..d7819b315c4 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -22,6 +22,7 @@ pub mod span;
 pub mod files;
 mod fixup;
 
+use attrs::collect_attrs;
 use triomphe::Arc;
 
 use std::{fmt, hash::Hash};
@@ -32,7 +33,7 @@ use base_db::{
 };
 use either::Either;
 use syntax::{
-    ast::{self, AstNode, HasDocComments},
+    ast::{self, AstNode},
     SyntaxNode, SyntaxToken, TextRange, TextSize,
 };
 
@@ -116,7 +117,7 @@ pub struct MacroCallLoc {
     pub krate: CrateId,
     /// Some if this is a macro call for an eager macro. Note that this is `None`
     /// for the eager input macro file.
-    eager: Option<Box<EagerCallInfo>>,
+    eager: Option<Arc<EagerCallInfo>>,
     pub kind: MacroCallKind,
     pub call_site: SyntaxContextId,
 }
@@ -438,9 +439,9 @@ impl MacroCallLoc {
             MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
                 // FIXME: handle `cfg_attr`
                 ast_id.with_value(ast_id.to_node(db)).map(|it| {
-                    it.doc_comments_and_attrs()
+                    collect_attrs(&it)
                         .nth(derive_attr_index.ast_index())
-                        .and_then(|it| match it {
+                        .and_then(|it| match it.1 {
                             Either::Left(attr) => Some(attr.syntax().clone()),
                             Either::Right(_) => None,
                         })
@@ -451,9 +452,9 @@ impl MacroCallLoc {
                 if self.def.is_attribute_derive() {
                     // FIXME: handle `cfg_attr`
                     ast_id.with_value(ast_id.to_node(db)).map(|it| {
-                        it.doc_comments_and_attrs()
+                        collect_attrs(&it)
                             .nth(invoc_attr_index.ast_index())
-                            .and_then(|it| match it {
+                            .and_then(|it| match it.1 {
                                 Either::Left(attr) => Some(attr.syntax().clone()),
                                 Either::Right(_) => None,
                             })
@@ -549,24 +550,24 @@ impl MacroCallKind {
             MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
                 // FIXME: should be the range of the macro name, not the whole derive
                 // FIXME: handle `cfg_attr`
-                ast_id
-                    .to_node(db)
-                    .doc_comments_and_attrs()
+                collect_attrs(&ast_id.to_node(db))
                     .nth(derive_attr_index.ast_index())
                     .expect("missing derive")
+                    .1
                     .expect_left("derive is a doc comment?")
                     .syntax()
                     .text_range()
             }
             // FIXME: handle `cfg_attr`
-            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => ast_id
-                .to_node(db)
-                .doc_comments_and_attrs()
-                .nth(invoc_attr_index.ast_index())
-                .expect("missing attribute")
-                .expect_left("attribute macro is a doc comment?")
-                .syntax()
-                .text_range(),
+            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
+                collect_attrs(&ast_id.to_node(db))
+                    .nth(invoc_attr_index.ast_index())
+                    .expect("missing attribute")
+                    .1
+                    .expect_left("attribute macro is a doc comment?")
+                    .syntax()
+                    .text_range()
+            }
         };
 
         FileRange { range, file_id }
@@ -737,11 +738,9 @@ impl ExpansionInfo {
         let attr_input_or_mac_def = def.or_else(|| match loc.kind {
             MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
                 // FIXME: handle `cfg_attr`
-                let tt = ast_id
-                    .to_node(db)
-                    .doc_comments_and_attrs()
+                let tt = collect_attrs(&ast_id.to_node(db))
                     .nth(invoc_attr_index.ast_index())
-                    .and_then(Either::left)?
+                    .and_then(|x| Either::left(x.1))?
                     .token_tree()?;
                 Some(InFile::new(ast_id.file_id, tt))
             }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span.rs
index 0a6c22fe42d..fe476a40feb 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/span.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/span.rs
@@ -75,27 +75,40 @@ pub struct RealSpanMap {
     /// Invariant: Sorted vec over TextSize
     // FIXME: SortedVec<(TextSize, ErasedFileAstId)>?
     pairs: Box<[(TextSize, ErasedFileAstId)]>,
+    end: TextSize,
 }
 
 impl RealSpanMap {
     /// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id).
     pub fn absolute(file_id: FileId) -> Self {
-        RealSpanMap { file_id, pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]) }
+        RealSpanMap {
+            file_id,
+            pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]),
+            end: TextSize::new(!0),
+        }
     }
 
     pub fn from_file(db: &dyn ExpandDatabase, file_id: FileId) -> Self {
         let mut pairs = vec![(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)];
         let ast_id_map = db.ast_id_map(file_id.into());
-        pairs.extend(
-            db.parse(file_id)
-                .tree()
-                .items()
-                .map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())),
-        );
-        RealSpanMap { file_id, pairs: pairs.into_boxed_slice() }
+        let tree = db.parse(file_id).tree();
+        pairs
+            .extend(tree.items().map(|item| {
+                (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())
+            }));
+        RealSpanMap {
+            file_id,
+            pairs: pairs.into_boxed_slice(),
+            end: tree.syntax().text_range().end(),
+        }
     }
 
     pub fn span_for_range(&self, range: TextRange) -> SpanData {
+        assert!(
+            range.end() <= self.end,
+            "range {range:?} goes beyond the end of the file {:?}",
+            self.end
+        );
         let start = range.start();
         let idx = self
             .pairs
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 8262edec22c..6f724e45874 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -113,7 +113,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
             // FIXME(const-generic-body): We should not get the return type in this way.
             ctx.return_ty = c
                 .lookup(db.upcast())
-                .thing
+                .expected_ty
                 .box_any()
                 .downcast::<InTypeConstIdMetadata>()
                 .unwrap()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 7ff12e5b7f8..acdb540289d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -262,7 +262,7 @@ impl InferenceContext<'_> {
     fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
         let mut expected = self.resolve_ty_shallow(expected);
 
-        if is_non_ref_pat(self.body, pat) {
+        if self.is_non_ref_pat(self.body, pat) {
             let mut pat_adjustments = Vec::new();
             while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
                 pat_adjustments.push(expected.clone());
@@ -496,24 +496,28 @@ impl InferenceContext<'_> {
 
         self.infer_expr(expr, &Expectation::has_type(expected.clone()))
     }
-}
 
-fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
-    match &body[pat] {
-        Pat::Tuple { .. }
-        | Pat::TupleStruct { .. }
-        | Pat::Record { .. }
-        | Pat::Range { .. }
-        | Pat::Slice { .. } => true,
-        Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)),
-        // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
-        Pat::Path(..) => true,
-        Pat::ConstBlock(..) => true,
-        Pat::Lit(expr) => !matches!(
-            body[*expr],
-            Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
-        ),
-        Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
+    fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool {
+        match &body[pat] {
+            Pat::Tuple { .. }
+            | Pat::TupleStruct { .. }
+            | Pat::Record { .. }
+            | Pat::Range { .. }
+            | Pat::Slice { .. } => true,
+            Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)),
+            Pat::Path(p) => {
+                let v = self.resolve_value_path_inner(p, pat.into());
+                v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_)))
+            }
+            Pat::ConstBlock(..) => false,
+            Pat::Lit(expr) => !matches!(
+                body[*expr],
+                Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
+            ),
+            Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => {
+                false
+            }
+        }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index fcfe1a3b5cf..49fb78f67a6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -40,33 +40,7 @@ impl InferenceContext<'_> {
     }
 
     fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> {
-        let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
-            let last = path.segments().last()?;
-
-            // Don't use `self.make_ty()` here as we need `orig_ns`.
-            let ctx =
-                crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
-            let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
-            let ty = self.table.insert_type_vars(ty);
-            let ty = self.table.normalize_associated_types_in(ty);
-
-            let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
-            let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
-            let ty = self.table.insert_type_vars(ty);
-            let ty = self.table.normalize_associated_types_in(ty);
-            self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
-        } else {
-            // FIXME: report error, unresolved first path segment
-            let value_or_partial =
-                self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
-
-            match value_or_partial {
-                ResolveValueResult::ValueNs(it, _) => (it, None),
-                ResolveValueResult::Partial(def, remaining_index, _) => self
-                    .resolve_assoc_item(def, path, remaining_index, id)
-                    .map(|(it, substs)| (it, Some(substs)))?,
-            }
-        };
+        let (value, self_subst) = self.resolve_value_path_inner(path, id)?;
 
         let value_def = match value {
             ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) {
@@ -144,6 +118,41 @@ impl InferenceContext<'_> {
         Some(ValuePathResolution::GenericDef(value_def, generic_def, substs))
     }
 
+    pub(super) fn resolve_value_path_inner(
+        &mut self,
+        path: &Path,
+        id: ExprOrPatId,
+    ) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> {
+        let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
+            let last = path.segments().last()?;
+
+            // Don't use `self.make_ty()` here as we need `orig_ns`.
+            let ctx =
+                crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
+            let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
+            let ty = self.table.insert_type_vars(ty);
+            let ty = self.table.normalize_associated_types_in(ty);
+
+            let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
+            let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
+            let ty = self.table.insert_type_vars(ty);
+            let ty = self.table.normalize_associated_types_in(ty);
+            self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
+        } else {
+            // FIXME: report error, unresolved first path segment
+            let value_or_partial =
+                self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
+
+            match value_or_partial {
+                ResolveValueResult::ValueNs(it, _) => (it, None),
+                ResolveValueResult::Partial(def, remaining_index, _) => self
+                    .resolve_assoc_item(def, path, remaining_index, id)
+                    .map(|(it, substs)| (it, Some(substs)))?,
+            }
+        };
+        Some((value, self_subst))
+    }
+
     fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) {
         let predicates = self.db.generic_predicates(def);
         for predicate in predicates.iter() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index c86fe9adff8..97c4a741ff2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -113,7 +113,9 @@ pub struct TyLoweringContext<'a> {
     pub db: &'a dyn HirDatabase,
     resolver: &'a Resolver,
     in_binders: DebruijnIndex,
-    owner: TypeOwnerId,
+    // FIXME: Should not be an `Option` but `Resolver` currently does not return owners in all cases
+    // where expected
+    owner: Option<TypeOwnerId>,
     /// Note: Conceptually, it's thinkable that we could be in a location where
     /// some type params should be represented as placeholders, and others
     /// should be converted to variables. I think in practice, this isn't
@@ -127,6 +129,14 @@ pub struct TyLoweringContext<'a> {
 
 impl<'a> TyLoweringContext<'a> {
     pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver, owner: TypeOwnerId) -> Self {
+        Self::new_maybe_unowned(db, resolver, Some(owner))
+    }
+
+    pub fn new_maybe_unowned(
+        db: &'a dyn HirDatabase,
+        resolver: &'a Resolver,
+        owner: Option<TypeOwnerId>,
+    ) -> Self {
         let impl_trait_mode = ImplTraitLoweringState::Disallowed;
         let type_param_mode = ParamLoweringMode::Placeholder;
         let in_binders = DebruijnIndex::INNERMOST;
@@ -213,10 +223,11 @@ impl<'a> TyLoweringContext<'a> {
     }
 
     pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const {
+        let Some(owner) = self.owner else { return unknown_const(const_type) };
         const_or_path_to_chalk(
             self.db,
             self.resolver,
-            self.owner,
+            owner,
             const_type,
             const_ref,
             self.type_param_mode,
@@ -1768,10 +1779,11 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
     let resolver = t.resolver(db.upcast());
     let ctx = TyLoweringContext::new(db, &resolver, t.into())
         .with_type_param_mode(ParamLoweringMode::Variable);
-    if db.type_alias_data(t).is_extern {
+    let type_alias_data = db.type_alias_data(t);
+    if type_alias_data.is_extern {
         Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
     } else {
-        let type_ref = &db.type_alias_data(t).type_ref;
+        let type_ref = &type_alias_data.type_ref;
         let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error));
         make_binders(db, &generics, inner)
     }
@@ -2042,7 +2054,7 @@ pub(crate) fn const_or_path_to_chalk(
                 .intern_in_type_const(InTypeConstLoc {
                     id: it,
                     owner,
-                    thing: Box::new(InTypeConstIdMetadata(expected_ty.clone())),
+                    expected_ty: Box::new(InTypeConstIdMetadata(expected_ty.clone())),
                 })
                 .into();
             intern_const_scalar(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
index bb15ca8c436..28e84e480d7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
@@ -9,11 +9,10 @@ use super::visit_module;
 fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
     let (mut db, pos) = TestDB::with_position(
         "
-        //- /lib.rs
-        fn foo() -> i32 {
-            $01 + 1
-        }
-    ",
+//- /lib.rs
+fn foo() -> i32 {
+    $01 + 1
+}",
     );
     {
         let events = db.log_executed(|| {
@@ -27,12 +26,11 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
     }
 
     let new_text = "
-        fn foo() -> i32 {
-            1
-            +
-            1
-        }
-    ";
+fn foo() -> i32 {
+    1
+    +
+    1
+}";
 
     db.set_file_text(pos.file_id, Arc::from(new_text));
 
@@ -47,3 +45,55 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
         assert!(!format!("{events:?}").contains("infer"), "{events:#?}")
     }
 }
+
+#[test]
+fn typing_inside_a_function_should_not_invalidate_types_in_another() {
+    let (mut db, pos) = TestDB::with_position(
+        "
+//- /lib.rs
+fn foo() -> f32 {
+    1.0 + 2.0
+}
+fn bar() -> i32 {
+    $01 + 1
+}
+fn baz() -> i32 {
+    1 + 1
+}",
+    );
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id);
+            let crate_def_map = module.def_map(&db);
+            visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
+                db.infer(def);
+            });
+        });
+        assert!(format!("{events:?}").contains("infer"))
+    }
+
+    let new_text = "
+fn foo() -> f32 {
+    1.0 + 2.0
+}
+fn bar() -> i32 {
+    53
+}
+fn baz() -> i32 {
+    1 + 1
+}
+";
+
+    db.set_file_text(pos.file_id, Arc::from(new_text));
+
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id);
+            let crate_def_map = module.def_map(&db);
+            visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
+                db.infer(def);
+            });
+        });
+        assert!(format!("{events:?}").matches("infer").count() == 1, "{events:#?}")
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 5d7bab09c26..7234af2d683 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -1153,3 +1153,41 @@ fn main() {
 "#,
     );
 }
+
+#[test]
+fn type_mismatch_pat_const_reference() {
+    check_no_mismatches(
+        r#"
+const TEST_STR: &'static str = "abcd";
+
+fn main() {
+    let s = "abcd";
+    match s {
+        TEST_STR => (),
+        _ => (),
+    }
+}
+
+            "#,
+    );
+    check(
+        r#"
+struct Foo<T>(T);
+
+impl<T> Foo<T> {
+    const TEST_I32_REF: &'static i32 = &3;
+    const TEST_I32: i32 = 3;
+}
+
+fn main() {
+    match &6 {
+        Foo::<i32>::TEST_I32_REF => (),
+        Foo::<i32>::TEST_I32 => (),
+      //^^^^^^^^^^^^^^^^^^^^ expected &i32, got i32
+        _ => (),
+    }
+}
+
+            "#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 92fa76c96fb..a03ff220745 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -20,8 +20,8 @@ use hir_def::{
     AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
-    db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, InMacroFile, MacroCallId,
-    MacroFileId, MacroFileIdExt,
+    attrs::collect_attrs, db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo,
+    InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
 };
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -29,7 +29,7 @@ use smallvec::{smallvec, SmallVec};
 use stdx::TupleExt;
 use syntax::{
     algo::skip_trivia_token,
-    ast::{self, HasAttrs as _, HasDocComments, HasGenericParams, HasLoopBody, IsString as _},
+    ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody, IsString as _},
     match_ast, AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
     TextRange, TextSize,
 };
@@ -673,11 +673,22 @@ impl<'db> SemanticsImpl<'db> {
                             }
                             _ => 0,
                         };
+                        // FIXME: here, the attribute's text range is used to strip away all
+                        // entries from the start of the attribute "list" up the the invoking
+                        // attribute. But in
+                        // ```
+                        // mod foo {
+                        //     #![inner]
+                        // }
+                        // ```
+                        // we don't wanna strip away stuff in the `mod foo {` range, that is
+                        // here if the id corresponds to an inner attribute we got strip all
+                        // text ranges of the outer ones, and then all of the inner ones up
+                        // to the invoking attribute so that the inbetween is ignored.
                         let text_range = item.syntax().text_range();
-                        let start = item
-                            .doc_comments_and_attrs()
+                        let start = collect_attrs(&item)
                             .nth(attr_id)
-                            .map(|attr| match attr {
+                            .map(|attr| match attr.1 {
                                 Either::Left(it) => it.syntax().text_range().start(),
                                 Either::Right(it) => it.syntax().text_range().start(),
                             })
@@ -937,10 +948,10 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
         let analyze = self.analyze(ty.syntax())?;
         let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
-        let ty = hir_ty::TyLoweringContext::new(
+        let ty = hir_ty::TyLoweringContext::new_maybe_unowned(
             self.db,
             &analyze.resolver,
-            analyze.resolver.module().into(),
+            analyze.resolver.type_owner(),
         )
         .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
         Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 73db6f8f0b8..d05118bbc28 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -1040,8 +1040,9 @@ fn resolve_hir_path_(
     let types = || {
         let (ty, unresolved) = match path.type_anchor() {
             Some(type_ref) => {
-                let (_, res) = TyLoweringContext::new(db, resolver, resolver.module().into())
-                    .lower_ty_ext(type_ref);
+                let (_, res) =
+                    TyLoweringContext::new_maybe_unowned(db, resolver, resolver.type_owner())
+                        .lower_ty_ext(type_ref);
                 res.map(|ty_ns| (ty_ns, path.segments().first()))
             }
             None => {
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 03112f6de5a..a2a30edeb03 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -23,6 +23,7 @@ pub struct FileSymbol {
     pub loc: DeclarationLocation,
     pub container_name: Option<SmolStr>,
     pub is_alias: bool,
+    pub is_assoc: bool,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -121,34 +122,34 @@ impl<'a> SymbolCollector<'a> {
             match module_def_id {
                 ModuleDefId::ModuleId(id) => self.push_module(id),
                 ModuleDefId::FunctionId(id) => {
-                    self.push_decl(id);
+                    self.push_decl(id, false);
                     self.collect_from_body(id);
                 }
-                ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id),
-                ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id),
-                ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id),
+                ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id, false),
+                ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, false),
+                ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, false),
                 ModuleDefId::ConstId(id) => {
-                    self.push_decl(id);
+                    self.push_decl(id, false);
                     self.collect_from_body(id);
                 }
                 ModuleDefId::StaticId(id) => {
-                    self.push_decl(id);
+                    self.push_decl(id, false);
                     self.collect_from_body(id);
                 }
                 ModuleDefId::TraitId(id) => {
-                    self.push_decl(id);
+                    self.push_decl(id, false);
                     self.collect_from_trait(id);
                 }
                 ModuleDefId::TraitAliasId(id) => {
-                    self.push_decl(id);
+                    self.push_decl(id, false);
                 }
                 ModuleDefId::TypeAliasId(id) => {
-                    self.push_decl(id);
+                    self.push_decl(id, false);
                 }
                 ModuleDefId::MacroId(id) => match id {
-                    MacroId::Macro2Id(id) => self.push_decl(id),
-                    MacroId::MacroRulesId(id) => self.push_decl(id),
-                    MacroId::ProcMacroId(id) => self.push_decl(id),
+                    MacroId::Macro2Id(id) => self.push_decl(id, false),
+                    MacroId::MacroRulesId(id) => self.push_decl(id, false),
+                    MacroId::ProcMacroId(id) => self.push_decl(id, false),
                 },
                 // Don't index these.
                 ModuleDefId::BuiltinType(_) => {}
@@ -190,6 +191,7 @@ impl<'a> SymbolCollector<'a> {
                     container_name: self.current_container_name.clone(),
                     loc: dec_loc,
                     is_alias: false,
+                    is_assoc: false,
                 });
             });
         }
@@ -202,9 +204,9 @@ impl<'a> SymbolCollector<'a> {
             for &id in id {
                 if id.module(self.db.upcast()) == module_id {
                     match id {
-                        MacroId::Macro2Id(id) => self.push_decl(id),
-                        MacroId::MacroRulesId(id) => self.push_decl(id),
-                        MacroId::ProcMacroId(id) => self.push_decl(id),
+                        MacroId::Macro2Id(id) => self.push_decl(id, false),
+                        MacroId::MacroRulesId(id) => self.push_decl(id, false),
+                        MacroId::ProcMacroId(id) => self.push_decl(id, false),
                     }
                 }
             }
@@ -266,13 +268,13 @@ impl<'a> SymbolCollector<'a> {
 
     fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) {
         match assoc_item_id {
-            AssocItemId::FunctionId(id) => self.push_decl(id),
-            AssocItemId::ConstId(id) => self.push_decl(id),
-            AssocItemId::TypeAliasId(id) => self.push_decl(id),
+            AssocItemId::FunctionId(id) => self.push_decl(id, true),
+            AssocItemId::ConstId(id) => self.push_decl(id, true),
+            AssocItemId::TypeAliasId(id) => self.push_decl(id, true),
         }
     }
 
-    fn push_decl<L>(&mut self, id: L)
+    fn push_decl<L>(&mut self, id: L, is_assoc: bool)
     where
         L: Lookup + Into<ModuleDefId>,
         <L as Lookup>::Data: HasSource,
@@ -296,6 +298,7 @@ impl<'a> SymbolCollector<'a> {
                     loc: dec_loc.clone(),
                     container_name: self.current_container_name.clone(),
                     is_alias: true,
+                    is_assoc,
                 });
             }
         }
@@ -306,6 +309,7 @@ impl<'a> SymbolCollector<'a> {
             container_name: self.current_container_name.clone(),
             loc: dec_loc,
             is_alias: false,
+            is_assoc,
         });
     }
 
@@ -331,6 +335,7 @@ impl<'a> SymbolCollector<'a> {
                     loc: dec_loc.clone(),
                     container_name: self.current_container_name.clone(),
                     is_alias: true,
+                    is_assoc: false,
                 });
             }
         }
@@ -341,6 +346,7 @@ impl<'a> SymbolCollector<'a> {
             container_name: self.current_container_name.clone(),
             loc: dec_loc,
             is_alias: false,
+            is_assoc: false,
         });
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 017853a4a20..435d7c4a537 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -216,7 +216,7 @@ fn edit_field_references(
             edit.edit_file(file_id);
             for r in refs {
                 if let Some(name_ref) = r.name.as_name_ref() {
-                    edit.replace(name_ref.syntax().text_range(), name.text());
+                    edit.replace(ctx.sema.original_range(name_ref.syntax()).range, name.text());
                 }
             }
         }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index b982322a734..de41a5bd70c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -458,13 +458,11 @@ impl Builder {
         }
         if let [import_edit] = &*self.imports_to_add {
             // snippets can have multiple imports, but normal completions only have up to one
-            if let Some(original_path) = import_edit.original_path.as_ref() {
-                label_detail.replace(SmolStr::from(format!(
-                    "{} (use {})",
-                    label_detail.as_deref().unwrap_or_default(),
-                    original_path.display(db)
-                )));
-            }
+            label_detail.replace(SmolStr::from(format!(
+                "{} (use {})",
+                label_detail.as_deref().unwrap_or_default(),
+                import_edit.import_path.display(db)
+            )));
         } else if let Some(trait_name) = self.trait_name {
             label_detail.replace(SmolStr::from(format!(
                 "{} (as {trait_name})",
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
index 50618296ee4..e667e2e0168 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
@@ -181,7 +181,7 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<V
             ctx.config.prefer_no_std,
             ctx.config.prefer_prelude,
         )?;
-        Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item, None)))
+        Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item)))
     };
     let mut res = Vec::with_capacity(requires.len());
     for import in requires {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index 9a4a94a2456..c58374f2e83 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -597,8 +597,8 @@ fn main() {
 }
 "#,
         expect![[r#"
-            ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
             fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED
+            ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
         "#]],
     );
 }
@@ -717,7 +717,7 @@ fn main() {
     check(
         fixture,
         expect![[r#"
-            st Item (use foo::bar::baz::Item) Item
+            st Item (use foo::bar) Item
         "#]],
     );
 
@@ -725,19 +725,19 @@ fn main() {
         "Item",
         fixture,
         r#"
-        use foo::bar;
+use foo::bar;
 
-        mod foo {
-            pub mod bar {
-                pub mod baz {
-                    pub struct Item;
-                }
-            }
+mod foo {
+    pub mod bar {
+        pub mod baz {
+            pub struct Item;
         }
+    }
+}
 
-        fn main() {
-            bar::baz::Item
-        }"#,
+fn main() {
+    bar::baz::Item
+}"#,
     );
 }
 
@@ -803,7 +803,7 @@ fn main() {
     check(
         fixture,
         expect![[r#"
-            ct TEST_ASSOC (use foo::bar::Item) usize
+            ct TEST_ASSOC (use foo::bar) usize
         "#]],
     );
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index 04263d15d0a..a4f0a6df781 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -1,14 +1,14 @@
 //! Look up accessible paths for items.
+
 use hir::{
-    AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef,
+    AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef, Name,
     PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
 };
-use itertools::Itertools;
-use rustc_hash::FxHashSet;
+use itertools::{EitherOrBoth, Itertools};
+use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::{
     ast::{self, make, HasName},
-    utils::path_to_string_stripping_turbo_fish,
-    AstNode, SyntaxNode,
+    AstNode, SmolStr, SyntaxNode,
 };
 
 use crate::{
@@ -51,18 +51,11 @@ pub struct TraitImportCandidate {
 #[derive(Debug)]
 pub struct PathImportCandidate {
     /// Optional qualifier before name.
-    pub qualifier: Option<FirstSegmentUnresolved>,
+    pub qualifier: Option<Vec<SmolStr>>,
     /// The name the item (struct, trait, enum, etc.) should have.
     pub name: NameToImport,
 }
 
-/// A qualifier that has a first segment and it's unresolved.
-#[derive(Debug)]
-pub struct FirstSegmentUnresolved {
-    fist_segment: ast::NameRef,
-    full_qualifier: ast::Path,
-}
-
 /// A name that will be used during item lookups.
 #[derive(Debug, Clone)]
 pub enum NameToImport {
@@ -195,18 +188,11 @@ pub struct LocatedImport {
     /// the original item is the associated constant, but the import has to be a trait that
     /// defines this constant.
     pub original_item: ItemInNs,
-    /// A path of the original item.
-    pub original_path: Option<ModPath>,
 }
 
 impl LocatedImport {
-    pub fn new(
-        import_path: ModPath,
-        item_to_import: ItemInNs,
-        original_item: ItemInNs,
-        original_path: Option<ModPath>,
-    ) -> Self {
-        Self { import_path, item_to_import, original_item, original_path }
+    pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self {
+        Self { import_path, item_to_import, original_item }
     }
 }
 
@@ -351,64 +337,75 @@ fn path_applicable_imports(
             )
             .filter_map(|item| {
                 let mod_path = mod_path(item)?;
-                Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path)))
-            })
-            .collect()
-        }
-        Some(first_segment_unresolved) => {
-            let unresolved_qualifier =
-                path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier);
-            let unresolved_first_segment = first_segment_unresolved.fist_segment.text();
-            items_locator::items_with_name(
-                sema,
-                current_crate,
-                path_candidate.name.clone(),
-                AssocSearchMode::Include,
-                Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
-            )
-            .filter_map(|item| {
-                import_for_item(
-                    sema.db,
-                    mod_path,
-                    &unresolved_first_segment,
-                    &unresolved_qualifier,
-                    item,
-                )
+                Some(LocatedImport::new(mod_path, item, item))
             })
             .collect()
         }
+        Some(qualifier) => items_locator::items_with_name(
+            sema,
+            current_crate,
+            path_candidate.name.clone(),
+            AssocSearchMode::Include,
+            Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
+        )
+        .filter_map(|item| import_for_item(sema.db, mod_path, &qualifier, item))
+        .collect(),
     }
 }
 
 fn import_for_item(
     db: &RootDatabase,
     mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
-    unresolved_first_segment: &str,
-    unresolved_qualifier: &str,
+    unresolved_qualifier: &[SmolStr],
     original_item: ItemInNs,
 ) -> Option<LocatedImport> {
     let _p = profile::span("import_assets::import_for_item");
+    let [first_segment, ..] = unresolved_qualifier else { return None };
 
-    let original_item_candidate = item_for_path_search(db, original_item)?;
+    let item_as_assoc = item_as_assoc(db, original_item);
+
+    let (original_item_candidate, trait_item_to_import) = match item_as_assoc {
+        Some(assoc_item) => match assoc_item.container(db) {
+            AssocItemContainer::Trait(trait_) => {
+                let trait_ = ItemInNs::from(ModuleDef::from(trait_));
+                (trait_, Some(trait_))
+            }
+            AssocItemContainer::Impl(impl_) => {
+                (ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), None)
+            }
+        },
+        None => (original_item, None),
+    };
     let import_path_candidate = mod_path(original_item_candidate)?;
-    let import_path_string = import_path_candidate.display(db).to_string();
 
-    let expected_import_end = if item_as_assoc(db, original_item).is_some() {
-        unresolved_qualifier.to_string()
-    } else {
-        format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db))
+    let mut import_path_candidate_segments = import_path_candidate.segments().iter().rev();
+    let predicate = |it: EitherOrBoth<&SmolStr, &Name>| match it {
+        // segments match, check next one
+        EitherOrBoth::Both(a, b) if b.as_str() == Some(&**a) => None,
+        // segments mismatch / qualifier is longer than the path, bail out
+        EitherOrBoth::Both(..) | EitherOrBoth::Left(_) => Some(false),
+        // all segments match and we have exhausted the qualifier, proceed
+        EitherOrBoth::Right(_) => Some(true),
     };
-    if !import_path_string.contains(unresolved_first_segment)
-        || !import_path_string.ends_with(&expected_import_end)
-    {
+    if item_as_assoc.is_none() {
+        let item_name = item_name(db, original_item)?.as_text()?;
+        let last_segment = import_path_candidate_segments.next()?;
+        if last_segment.as_str() != Some(&*item_name) {
+            return None;
+        }
+    }
+    let ends_with = unresolved_qualifier
+        .iter()
+        .rev()
+        .zip_longest(import_path_candidate_segments)
+        .find_map(predicate)
+        .unwrap_or(true);
+    if !ends_with {
         return None;
     }
 
-    let segment_import =
-        find_import_for_segment(db, original_item_candidate, unresolved_first_segment)?;
-    let trait_item_to_import = item_as_assoc(db, original_item)
-        .and_then(|assoc| assoc.containing_trait(db))
-        .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
+    let segment_import = find_import_for_segment(db, original_item_candidate, first_segment)?;
+
     Some(match (segment_import == original_item_candidate, trait_item_to_import) {
         (true, Some(_)) => {
             // FIXME we should be able to import both the trait and the segment,
@@ -416,42 +413,37 @@ fn import_for_item(
             // especially in case of lazy completion edit resolutions.
             return None;
         }
-        (false, Some(trait_to_import)) => LocatedImport::new(
-            mod_path(trait_to_import)?,
-            trait_to_import,
-            original_item,
-            mod_path(original_item),
-        ),
-        (true, None) => LocatedImport::new(
-            import_path_candidate,
-            original_item_candidate,
-            original_item,
-            mod_path(original_item),
-        ),
-        (false, None) => LocatedImport::new(
-            mod_path(segment_import)?,
-            segment_import,
-            original_item,
-            mod_path(original_item),
-        ),
+        (false, Some(trait_to_import)) => {
+            LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item)
+        }
+        (true, None) => {
+            LocatedImport::new(import_path_candidate, original_item_candidate, original_item)
+        }
+        (false, None) => {
+            LocatedImport::new(mod_path(segment_import)?, segment_import, original_item)
+        }
     })
 }
 
 pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
     Some(match item {
         ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
-            Some(assoc_item) => match assoc_item.container(db) {
-                AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
-                AssocItemContainer::Impl(impl_) => {
-                    ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
-                }
-            },
+            Some(assoc_item) => item_for_path_search_assoc(db, assoc_item)?,
             None => item,
         },
         ItemInNs::Macros(_) => item,
     })
 }
 
+fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Option<ItemInNs> {
+    Some(match assoc_item.container(db) {
+        AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
+        AssocItemContainer::Impl(impl_) => {
+            ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
+        }
+    })
+}
+
 fn find_import_for_segment(
     db: &RootDatabase,
     original_item: ItemInNs,
@@ -528,6 +520,7 @@ fn trait_applicable_items(
     .collect();
 
     let mut located_imports = FxHashSet::default();
+    let mut trait_import_paths = FxHashMap::default();
 
     if trait_assoc_item {
         trait_candidate.receiver_ty.iterate_path_candidates(
@@ -545,12 +538,14 @@ fn trait_applicable_items(
                     }
                     let located_trait = assoc.containing_trait(db)?;
                     let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
-                    let original_item = assoc_to_item(assoc);
+                    let import_path = trait_import_paths
+                        .entry(trait_item)
+                        .or_insert_with(|| mod_path(trait_item))
+                        .clone()?;
                     located_imports.insert(LocatedImport::new(
-                        mod_path(trait_item)?,
+                        import_path,
                         trait_item,
-                        original_item,
-                        mod_path(original_item),
+                        assoc_to_item(assoc),
                     ));
                 }
                 None::<()>
@@ -568,12 +563,14 @@ fn trait_applicable_items(
                 if required_assoc_items.contains(&assoc) {
                     let located_trait = assoc.containing_trait(db)?;
                     let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
-                    let original_item = assoc_to_item(assoc);
+                    let import_path = trait_import_paths
+                        .entry(trait_item)
+                        .or_insert_with(|| mod_path(trait_item))
+                        .clone()?;
                     located_imports.insert(LocatedImport::new(
-                        mod_path(trait_item)?,
+                        import_path,
                         trait_item,
-                        original_item,
-                        mod_path(original_item),
+                        assoc_to_item(assoc),
                     ));
                 }
                 None::<()>
@@ -671,18 +668,13 @@ fn path_import_candidate(
     Some(match qualifier {
         Some(qualifier) => match sema.resolve_path(&qualifier) {
             None => {
-                let qualifier_start =
-                    qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
-                let qualifier_start_path =
-                    qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
-                if sema.resolve_path(&qualifier_start_path).is_none() {
-                    ImportCandidate::Path(PathImportCandidate {
-                        qualifier: Some(FirstSegmentUnresolved {
-                            fist_segment: qualifier_start,
-                            full_qualifier: qualifier,
-                        }),
-                        name,
-                    })
+                if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) {
+                    let mut qualifier = qualifier
+                        .segments_of_this_path_only_rev()
+                        .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text())))
+                        .collect::<Option<Vec<_>>>()?;
+                    qualifier.reverse();
+                    ImportCandidate::Path(PathImportCandidate { qualifier: Some(qualifier), name })
                 } else {
                     return None;
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index 67ed44f08b7..4a5d234f73d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -3,13 +3,13 @@
 //! The main reason for this module to exist is the fact that project's items and dependencies' items
 //! are located in different caches, with different APIs.
 use either::Either;
-use hir::{import_map, AsAssocItem, Crate, ItemInNs, Semantics};
+use hir::{import_map, Crate, ItemInNs, Semantics};
 use limit::Limit;
 
 use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase};
 
 /// A value to use, when uncertain which limit to pick.
-pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40);
+pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100);
 
 pub use import_map::AssocSearchMode;
 
@@ -36,7 +36,9 @@ pub fn items_with_name<'a>(
         NameToImport::Prefix(exact_name, case_sensitive)
         | NameToImport::Exact(exact_name, case_sensitive) => {
             let mut local_query = symbol_index::Query::new(exact_name.clone());
-            let mut external_query = import_map::Query::new(exact_name);
+            let mut external_query =
+                // import_map::Query::new(exact_name).assoc_search_mode(assoc_item_search);
+                import_map::Query::new(exact_name);
             if prefix {
                 local_query.prefix();
                 external_query = external_query.prefix();
@@ -101,8 +103,8 @@ fn find_items<'a>(
         .into_iter()
         .filter(move |candidate| match assoc_item_search {
             AssocSearchMode::Include => true,
-            AssocSearchMode::Exclude => candidate.def.as_assoc_item(db).is_none(),
-            AssocSearchMode::AssocItemsOnly => candidate.def.as_assoc_item(db).is_some(),
+            AssocSearchMode::Exclude => !candidate.is_assoc,
+            AssocSearchMode::AssocItemsOnly => candidate.is_assoc,
         })
         .map(|local_candidate| match local_candidate.def {
             hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index 3e89159c2c6..be8566b759c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -50,7 +50,7 @@ enum SearchMode {
     Prefix,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Query {
     query: String,
     lowercased: String,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
index 4a72881fe5e..7c01ac06939 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -36,6 +36,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "Struct",
@@ -65,6 +66,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "mul1",
@@ -94,6 +96,7 @@
                 },
                 container_name: None,
                 is_alias: true,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "mul2",
@@ -123,6 +126,7 @@
                 },
                 container_name: None,
                 is_alias: true,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "s1",
@@ -152,6 +156,7 @@
                 },
                 container_name: None,
                 is_alias: true,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "s1",
@@ -181,6 +186,7 @@
                 },
                 container_name: None,
                 is_alias: true,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "s2",
@@ -210,6 +216,7 @@
                 },
                 container_name: None,
                 is_alias: true,
+                is_assoc: false,
             },
         ],
     ),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index da1f3167d7d..c9875c7f8f2 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -34,6 +34,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "CONST",
@@ -61,6 +62,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "CONST_WITH_INNER",
@@ -88,6 +90,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "Enum",
@@ -117,6 +120,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "ItemLikeMacro",
@@ -146,6 +150,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "Macro",
@@ -175,6 +180,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "STATIC",
@@ -202,6 +208,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "Struct",
@@ -231,6 +238,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "StructFromMacro",
@@ -260,6 +268,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "StructInFn",
@@ -291,6 +300,7 @@
                     "main",
                 ),
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "StructInNamedConst",
@@ -322,6 +332,7 @@
                     "CONST_WITH_INNER",
                 ),
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "StructInUnnamedConst",
@@ -351,6 +362,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "Trait",
@@ -378,6 +390,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "Trait",
@@ -407,6 +420,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "Union",
@@ -436,6 +450,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "a_mod",
@@ -465,6 +480,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "b_mod",
@@ -494,6 +510,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "define_struct",
@@ -523,6 +540,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "impl_fn",
@@ -550,6 +568,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: true,
             },
             FileSymbol {
                 name: "macro_rules_macro",
@@ -579,6 +598,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "main",
@@ -606,6 +626,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "really_define_struct",
@@ -635,6 +656,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "trait_fn",
@@ -664,6 +686,7 @@
                     "Trait",
                 ),
                 is_alias: false,
+                is_assoc: true,
             },
         ],
     ),
@@ -704,6 +727,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
         ],
     ),
@@ -744,6 +768,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "StructInModB",
@@ -773,6 +798,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "SuperItemLikeMacro",
@@ -802,6 +828,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "ThisStruct",
@@ -831,6 +858,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
             FileSymbol {
                 name: "ThisStruct",
@@ -860,6 +888,7 @@
                 },
                 container_name: None,
                 is_alias: false,
+                is_assoc: false,
             },
         ],
     ),
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
index 71c501a336b..f8265b63275 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
@@ -47,19 +47,4 @@ use foo::Foo as Bar;
 "#,
         );
     }
-
-    #[test]
-    fn regression_panic_with_inner_attribute_in_presence_of_unresolved_crate() {
-        check_diagnostics(
-            r#"
-//- /lib.rs
-  #[macro_use] extern crate doesnotexist;
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unresolved extern crate
-    mod _test_inner {
-        #![empty_attr]
-      //^^^^^^^^^^^^^^ error: unresolved macro `empty_attr`
-    }
-"#,
-        );
-    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 33e7c2e37c3..c8ff54cba3a 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -70,4 +70,16 @@ self::m!(); self::m2!();
 "#,
         );
     }
+
+    #[test]
+    fn regression_panic_with_inner_attribute_in_presence_of_unresolved_crate() {
+        check_diagnostics(
+            r#"
+    mod _test_inner {
+        #![empty_attr]
+      //^^^^^^^^^^^^^^ error: unresolved macro `empty_attr`
+    }
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 6541bf60579..579386c72ef 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -94,7 +94,7 @@ use syntax::{
 };
 
 // FIXME: Make this an enum
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub enum DiagnosticCode {
     RustcHardError(&'static str),
     RustcLint(&'static str),
@@ -198,7 +198,7 @@ impl Diagnostic {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum Severity {
     Error,
     Warning,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index ca334e91579..e82d730e4a3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -422,6 +422,11 @@ fn ty_to_text_edit(
     Some(builder.finish())
 }
 
+pub enum RangeLimit {
+    Fixed(TextRange),
+    NearestParent(TextSize),
+}
+
 // Feature: Inlay Hints
 //
 // rust-analyzer shows additional information inline with the source code.
@@ -443,7 +448,7 @@ fn ty_to_text_edit(
 pub(crate) fn inlay_hints(
     db: &RootDatabase,
     file_id: FileId,
-    range_limit: Option<TextRange>,
+    range_limit: Option<RangeLimit>,
     config: &InlayHintsConfig,
 ) -> Vec<InlayHint> {
     let _p = profile::span("inlay_hints");
@@ -458,13 +463,31 @@ pub(crate) fn inlay_hints(
 
         let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
         match range_limit {
-            Some(range) => match file.covering_element(range) {
+            Some(RangeLimit::Fixed(range)) => match file.covering_element(range) {
                 NodeOrToken::Token(_) => return acc,
                 NodeOrToken::Node(n) => n
                     .descendants()
                     .filter(|descendant| range.intersect(descendant.text_range()).is_some())
                     .for_each(hints),
             },
+            Some(RangeLimit::NearestParent(position)) => {
+                match file.token_at_offset(position).left_biased() {
+                    Some(token) => {
+                        if let Some(parent_block) =
+                            token.parent_ancestors().find_map(ast::BlockExpr::cast)
+                        {
+                            parent_block.syntax().descendants().for_each(hints)
+                        } else if let Some(parent_item) =
+                            token.parent_ancestors().find_map(ast::Item::cast)
+                        {
+                            parent_item.syntax().descendants().for_each(hints)
+                        } else {
+                            return acc;
+                        }
+                    }
+                    None => return acc,
+                }
+            }
             None => file.descendants().for_each(hints),
         };
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 680035c721b..45b51e35570 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -177,7 +177,11 @@ mod tests {
     use syntax::{TextRange, TextSize};
     use test_utils::extract_annotations;
 
-    use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};
+    use crate::{
+        fixture,
+        inlay_hints::{InlayHintsConfig, RangeLimit},
+        ClosureReturnTypeHints,
+    };
 
     use crate::inlay_hints::tests::{
         check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
@@ -400,7 +404,7 @@ fn main() {
             .inlay_hints(
                 &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
                 file_id,
-                Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
+                Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))),
             )
             .unwrap();
         let actual =
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index d8f6e4e1b1b..a19952e4cae 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -94,7 +94,7 @@ pub use crate::{
     inlay_hints::{
         AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
         InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition,
-        InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints,
+        InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, RangeLimit,
     },
     join_lines::JoinLinesConfig,
     markup::Markup,
@@ -133,7 +133,9 @@ pub use ide_db::{
     symbol_index::Query,
     RootDatabase, SymbolKind,
 };
-pub use ide_diagnostics::{Diagnostic, DiagnosticsConfig, ExprFillDefaultMode, Severity};
+pub use ide_diagnostics::{
+    Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode, Severity,
+};
 pub use ide_ssr::SsrError;
 pub use syntax::{TextRange, TextSize};
 pub use text_edit::{Indel, TextEdit};
@@ -397,7 +399,7 @@ impl Analysis {
         &self,
         config: &InlayHintsConfig,
         file_id: FileId,
-        range: Option<TextRange>,
+        range: Option<RangeLimit>,
     ) -> Cancellable<Vec<InlayHint>> {
         self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
     }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
index 1c46471a383..b89bfd74a6e 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -311,7 +311,7 @@ where
 
                             let ident = tt::Leaf::from(tt::Ident {
                                 text: SmolStr::new(&token.to_text(conv)[1..]),
-                                span: conv.span_for(TextRange::at(
+                                span: conv.span_for(TextRange::new(
                                     abs_range.start() + TextSize::of('\''),
                                     abs_range.end(),
                                 )),
@@ -625,25 +625,6 @@ impl<SpanMap, S> Converter<SpanMap, S> {
     }
 
     fn next_token(&mut self) -> Option<SyntaxToken> {
-        // while let Some(ev) = self.preorder.next() {
-        //     match ev {
-        //         WalkEvent::Enter(SyntaxElement::Token(t)) => {
-        //             if let Some(leafs) = self.append.remove(&t.clone().into()) {
-        //                 self.current_leafs.extend(leafs);
-        //             }
-        //             return Some(t);
-        //         }
-        //         WalkEvent::Enter(SyntaxElement::Node(n)) if self.remove.contains(&n) => {
-        //             self.preorder.skip_subtree();
-        //             if let Some(leafs) = self.append.remove(&n.into()) {
-        //                 self.current_leafs.extend(leafs);
-        //             }
-        //         }
-        //         _ => (),
-        //     }
-        // }
-        // None;
-
         while let Some(ev) = self.preorder.next() {
             match ev {
                 WalkEvent::Enter(SyntaxElement::Token(t)) => return Some(t),
diff --git a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
index 595691b1773..40e8a2385f4 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
@@ -131,7 +131,6 @@ impl<'a, S: Span> TtIter<'a, S> {
         let buffer = tt::buffer::TokenBuffer::from_tokens(self.inner.as_slice());
         let parser_input = to_parser_input(&buffer);
         let tree_traversal = entry_point.parse(&parser_input);
-
         let mut cursor = buffer.begin();
         let mut error = false;
         for step in tree_traversal.iter() {
@@ -163,12 +162,10 @@ impl<'a, S: Span> TtIter<'a, S> {
         let mut curr = buffer.begin();
         let mut res = vec![];
 
-        if cursor.is_root() {
-            while curr != cursor {
-                let Some(token) = curr.token_tree() else { break };
-                res.push(token.cloned());
-                curr = curr.bump();
-            }
+        while curr != cursor {
+            let Some(token) = curr.token_tree() else { break };
+            res.push(token.cloned());
+            curr = curr.bump();
         }
 
         self.inner = self.inner.as_slice()[res.len()..].iter();
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
index 87118a62650..5ff1f36c545 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
@@ -131,7 +131,7 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
             let len_bytes = &dot_rustc[8..16];
             let data_len = u64::from_le_bytes(len_bytes.try_into().unwrap()) as usize;
             (&dot_rustc[16..data_len + 12], 17)
-         }
+        }
         _ => {
             return Err(io::Error::new(
                 io::ErrorKind::InvalidData,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
index 54430e0d190..917d8a6e26a 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
@@ -58,6 +58,10 @@ impl server::Types for RustAnalyzer {
 }
 
 impl server::FreeFunctions for RustAnalyzer {
+    fn injected_env_var(&mut self, _var: &str) -> Option<String> {
+        None
+    }
+
     fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {
         // FIXME: track env var accesses
         // https://github.com/rust-lang/rust/pull/71858
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 408c1fb6f39..39ac338aa1a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -42,6 +42,7 @@ tracing-tree.workspace = true
 triomphe.workspace = true
 nohash-hasher.workspace = true
 always-assert = "0.1.2"
+walkdir = "2.3.2"
 
 cfg.workspace = true
 flycheck.workspace = true
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 29bd02f92da..8472e49de98 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -87,6 +87,7 @@ fn main() -> anyhow::Result<()> {
         flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Scip(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::RunTests(cmd) => cmd.run()?,
+        flags::RustAnalyzerCmd::RustcTests(cmd) => cmd.run()?,
     }
     Ok(())
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
index 64646b33ad4..de00c4192b4 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
@@ -10,6 +10,7 @@ mod ssr;
 mod lsif;
 mod scip;
 mod run_tests;
+mod rustc_tests;
 
 mod progress_report;
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index fe5022f8606..5633c0c488a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -98,6 +98,15 @@ xflags::xflags! {
             required path: PathBuf
         }
 
+        /// Run unit tests of the project using mir interpreter
+        cmd rustc-tests {
+            /// Directory with Cargo.toml.
+            required rustc_repo: PathBuf
+
+            /// Only run tests with filter as substring
+            optional --filter path: String
+        }
+
         cmd diagnostics {
             /// Directory with Cargo.toml.
             required path: PathBuf
@@ -159,6 +168,7 @@ pub enum RustAnalyzerCmd {
     Highlight(Highlight),
     AnalysisStats(AnalysisStats),
     RunTests(RunTests),
+    RustcTests(RustcTests),
     Diagnostics(Diagnostics),
     Ssr(Ssr),
     Search(Search),
@@ -212,6 +222,12 @@ pub struct RunTests {
 }
 
 #[derive(Debug)]
+pub struct RustcTests {
+    pub rustc_repo: PathBuf,
+    pub filter: Option<String>,
+}
+
+#[derive(Debug)]
 pub struct Diagnostics {
     pub path: PathBuf,
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
new file mode 100644
index 00000000000..c89b88ac0f9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -0,0 +1,236 @@
+//! Run all tests in a project, similar to `cargo test`, but using the mir interpreter.
+
+use std::{
+    cell::RefCell, collections::HashMap, fs::read_to_string, panic::AssertUnwindSafe, path::PathBuf,
+};
+
+use hir::Crate;
+use ide::{AnalysisHost, Change, DiagnosticCode, DiagnosticsConfig};
+use profile::StopWatch;
+use project_model::{CargoConfig, ProjectWorkspace, RustLibSource, Sysroot};
+
+use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
+use triomphe::Arc;
+use vfs::{AbsPathBuf, FileId};
+use walkdir::WalkDir;
+
+use crate::cli::{flags, report_metric, Result};
+
+struct Tester {
+    host: AnalysisHost,
+    root_file: FileId,
+    pass_count: u64,
+    ignore_count: u64,
+    fail_count: u64,
+    stopwatch: StopWatch,
+}
+
+fn string_to_diagnostic_code_leaky(code: &str) -> DiagnosticCode {
+    thread_local! {
+        static LEAK_STORE: RefCell<HashMap<String, DiagnosticCode>> = RefCell::new(HashMap::new());
+    }
+    LEAK_STORE.with_borrow_mut(|s| match s.get(code) {
+        Some(c) => *c,
+        None => {
+            let v = DiagnosticCode::RustcHardError(format!("E{code}").leak());
+            s.insert(code.to_owned(), v);
+            v
+        }
+    })
+}
+
+fn detect_errors_from_rustc_stderr_file(p: PathBuf) -> HashMap<DiagnosticCode, usize> {
+    let text = read_to_string(p).unwrap();
+    let mut result = HashMap::new();
+    {
+        let mut text = &*text;
+        while let Some(p) = text.find("error[E") {
+            text = &text[p + 7..];
+            let code = string_to_diagnostic_code_leaky(&text[..4]);
+            *result.entry(code).or_insert(0) += 1;
+        }
+    }
+    result
+}
+
+impl Tester {
+    fn new() -> Result<Self> {
+        let tmp_file = AbsPathBuf::assert("/tmp/ra-rustc-test.rs".into());
+        std::fs::write(&tmp_file, "")?;
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustLibSource::Discover);
+        let workspace = ProjectWorkspace::DetachedFiles {
+            files: vec![tmp_file.clone()],
+            sysroot: Ok(
+                Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env).unwrap()
+            ),
+            rustc_cfg: vec![],
+        };
+        let load_cargo_config = LoadCargoConfig {
+            load_out_dirs_from_check: false,
+            with_proc_macro_server: ProcMacroServerChoice::Sysroot,
+            prefill_caches: false,
+        };
+        let (host, _vfs, _proc_macro) =
+            load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
+        let db = host.raw_database();
+        let krates = Crate::all(db);
+        let root_crate = krates.iter().cloned().find(|krate| krate.origin(db).is_local()).unwrap();
+        let root_file = root_crate.root_file(db);
+        Ok(Self {
+            host,
+            root_file,
+            pass_count: 0,
+            ignore_count: 0,
+            fail_count: 0,
+            stopwatch: StopWatch::start(),
+        })
+    }
+
+    fn test(&mut self, p: PathBuf) {
+        if p.parent().unwrap().file_name().unwrap() == "auxiliary" {
+            // These are not tests
+            return;
+        }
+        if IGNORED_TESTS.iter().any(|ig| p.file_name().is_some_and(|x| x == *ig)) {
+            println!("{p:?} IGNORE");
+            self.ignore_count += 1;
+            return;
+        }
+        let stderr_path = p.with_extension("stderr");
+        let expected = if stderr_path.exists() {
+            detect_errors_from_rustc_stderr_file(stderr_path)
+        } else {
+            HashMap::new()
+        };
+        let text = read_to_string(&p).unwrap();
+        let mut change = Change::new();
+        // Ignore unstable tests, since they move too fast and we do not intend to support all of them.
+        let mut ignore_test = text.contains("#![feature");
+        // Ignore test with extern crates, as this infra don't support them yet.
+        ignore_test |= text.contains("// aux-build:") || text.contains("// aux-crate:");
+        // Ignore test with extern modules similarly.
+        ignore_test |= text.contains("mod ");
+        // These should work, but they don't, and I don't know why, so ignore them.
+        ignore_test |= text.contains("extern crate proc_macro");
+        let should_have_no_error = text.contains("// check-pass")
+            || text.contains("// build-pass")
+            || text.contains("// run-pass");
+        change.change_file(self.root_file, Some(Arc::from(text)));
+        self.host.apply_change(change);
+        let diagnostic_config = DiagnosticsConfig::test_sample();
+        let diags = self
+            .host
+            .analysis()
+            .diagnostics(&diagnostic_config, ide::AssistResolveStrategy::None, self.root_file)
+            .unwrap();
+        let mut actual = HashMap::new();
+        for diag in diags {
+            if !matches!(diag.code, DiagnosticCode::RustcHardError(_)) {
+                continue;
+            }
+            if !should_have_no_error && !SUPPORTED_DIAGNOSTICS.contains(&diag.code) {
+                continue;
+            }
+            *actual.entry(diag.code).or_insert(0) += 1;
+        }
+        // Ignore tests with diagnostics that we don't emit.
+        ignore_test |= expected.keys().any(|k| !SUPPORTED_DIAGNOSTICS.contains(k));
+        if ignore_test {
+            println!("{p:?} IGNORE");
+            self.ignore_count += 1;
+        } else if actual == expected {
+            println!("{p:?} PASS");
+            self.pass_count += 1;
+        } else {
+            println!("{p:?} FAIL");
+            println!("actual   (r-a)   = {:?}", actual);
+            println!("expected (rustc) = {:?}", expected);
+            self.fail_count += 1;
+        }
+    }
+
+    fn report(&mut self) {
+        println!(
+            "Pass count = {}, Fail count = {}, Ignore count = {}",
+            self.pass_count, self.fail_count, self.ignore_count
+        );
+        println!("Testing time and memory = {}", self.stopwatch.elapsed());
+        report_metric("rustc failed tests", self.fail_count, "#");
+        report_metric("rustc testing time", self.stopwatch.elapsed().time.as_millis() as u64, "ms");
+    }
+}
+
+/// These tests break rust-analyzer (either by panicking or hanging) so we should ignore them.
+const IGNORED_TESTS: &[&str] = &[
+    "trait-with-missing-associated-type-restriction.rs", // #15646
+    "trait-with-missing-associated-type-restriction-fixable.rs", // #15646
+    "resolve-self-in-impl.rs",
+    "basic.rs", // ../rust/tests/ui/associated-type-bounds/return-type-notation/basic.rs
+    "issue-26056.rs",
+    "float-field.rs",
+    "invalid_operator_trait.rs",
+    "type-alias-impl-trait-assoc-dyn.rs",
+    "deeply-nested_closures.rs",    // exponential time
+    "hang-on-deeply-nested-dyn.rs", // exponential time
+    "dyn-rpit-and-let.rs", // unexpected free variable with depth `^1.0` with outer binder ^0
+    "issue-16098.rs",      // Huge recursion limit for macros?
+    "issue-83471.rs", // crates/hir-ty/src/builder.rs:78:9: assertion failed: self.remaining() > 0
+];
+
+const SUPPORTED_DIAGNOSTICS: &[DiagnosticCode] = &[
+    DiagnosticCode::RustcHardError("E0023"),
+    DiagnosticCode::RustcHardError("E0046"),
+    DiagnosticCode::RustcHardError("E0063"),
+    DiagnosticCode::RustcHardError("E0107"),
+    DiagnosticCode::RustcHardError("E0117"),
+    DiagnosticCode::RustcHardError("E0133"),
+    DiagnosticCode::RustcHardError("E0210"),
+    DiagnosticCode::RustcHardError("E0268"),
+    DiagnosticCode::RustcHardError("E0308"),
+    DiagnosticCode::RustcHardError("E0384"),
+    DiagnosticCode::RustcHardError("E0407"),
+    DiagnosticCode::RustcHardError("E0432"),
+    DiagnosticCode::RustcHardError("E0451"),
+    DiagnosticCode::RustcHardError("E0507"),
+    DiagnosticCode::RustcHardError("E0583"),
+    DiagnosticCode::RustcHardError("E0559"),
+    DiagnosticCode::RustcHardError("E0616"),
+    DiagnosticCode::RustcHardError("E0618"),
+    DiagnosticCode::RustcHardError("E0624"),
+    DiagnosticCode::RustcHardError("E0774"),
+    DiagnosticCode::RustcHardError("E0767"),
+    DiagnosticCode::RustcHardError("E0777"),
+];
+
+impl flags::RustcTests {
+    pub fn run(self) -> Result<()> {
+        let mut tester = Tester::new()?;
+        let walk_dir = WalkDir::new(self.rustc_repo.join("tests/ui"));
+        for i in walk_dir {
+            let i = i?;
+            let p = i.into_path();
+            if let Some(f) = &self.filter {
+                if !p.as_os_str().to_string_lossy().contains(f) {
+                    continue;
+                }
+            }
+            if p.extension().map_or(true, |x| x != "rs") {
+                continue;
+            }
+            if let Err(e) = std::panic::catch_unwind({
+                let tester = AssertUnwindSafe(&mut tester);
+                let p = p.clone();
+                move || {
+                    let tester = tester;
+                    tester.0.test(p);
+                }
+            }) {
+                println!("panic detected at test {:?}", p);
+                std::panic::resume_unwind(e);
+            }
+        }
+        tester.report();
+        Ok(())
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 90d1d6b0555..258f7410639 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -1354,6 +1354,7 @@ impl Config {
         }
     }
 
+    // FIXME: This should be an AbsolutePathBuf
     fn target_dir_from_config(&self) -> Option<PathBuf> {
         self.data.rust_analyzerTargetDir.as_ref().and_then(|target_dir| match target_dir {
             TargetDirectory::UseSubdirectory(yes) if *yes => {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 57955ebf897..d8a590c8088 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -12,8 +12,8 @@ use anyhow::Context;
 
 use ide::{
     AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
-    HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory,
-    Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
+    HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, RangeLimit,
+    ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
 };
 use ide_db::SymbolKind;
 use lsp_server::ErrorCode;
@@ -1409,7 +1409,7 @@ pub(crate) fn handle_inlay_hints(
     let inlay_hints_config = snap.config.inlay_hints();
     Ok(Some(
         snap.analysis
-            .inlay_hints(&inlay_hints_config, file_id, Some(range))?
+            .inlay_hints(&inlay_hints_config, file_id, Some(RangeLimit::Fixed(range)))?
             .into_iter()
             .map(|it| {
                 to_proto::inlay_hint(
@@ -1440,22 +1440,13 @@ pub(crate) fn handle_inlay_hints_resolve(
     anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data");
 
     let line_index = snap.file_line_index(file_id)?;
-    let range = from_proto::text_range(
-        &line_index,
-        lsp_types::Range { start: original_hint.position, end: original_hint.position },
-    )?;
-    let range_start = range.start();
-    let range_end = range.end();
-    let large_range = TextRange::new(
-        range_start.checked_sub(1.into()).unwrap_or(range_start),
-        range_end.checked_add(1.into()).unwrap_or(range_end),
-    );
+    let hint_position = from_proto::offset(&line_index, original_hint.position)?;
     let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
     forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
     let resolve_hints = snap.analysis.inlay_hints(
         &forced_resolve_inlay_hints_config,
         file_id,
-        Some(large_range),
+        Some(RangeLimit::NearestParent(hint_position)),
     )?;
 
     let mut resolved_hints = resolve_hints
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index ed2cf07551b..41ff17f5e43 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -32,7 +32,10 @@ fn integrated_highlighting_benchmark() {
     let workspace_to_load = project_root();
     let file = "./crates/rust-analyzer/src/config.rs";
 
-    let cargo_config = CargoConfig::default();
+    let cargo_config = CargoConfig {
+        sysroot: Some(project_model::RustLibSource::Discover),
+        ..CargoConfig::default()
+    };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::None,
@@ -85,7 +88,10 @@ fn integrated_completion_benchmark() {
     let workspace_to_load = project_root();
     let file = "./crates/hir/src/lib.rs";
 
-    let cargo_config = CargoConfig::default();
+    let cargo_config = CargoConfig {
+        sysroot: Some(project_model::RustLibSource::Discover),
+        ..CargoConfig::default()
+    };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::None,
@@ -103,10 +109,46 @@ fn integrated_completion_benchmark() {
         vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
     };
 
+    // kick off parsing and index population
+
+    let completion_offset = {
+        let _it = stdx::timeit("change");
+        let mut text = host.analysis().file_text(file_id).unwrap().to_string();
+        let completion_offset =
+            patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
+                + "sel".len();
+        let mut change = Change::new();
+        change.change_file(file_id, Some(Arc::from(text)));
+        host.apply_change(change);
+        completion_offset
+    };
+
     {
-        let _it = stdx::timeit("initial");
+        let _span = profile::cpu_span();
         let analysis = host.analysis();
-        analysis.highlight_as_html(file_id, false).unwrap();
+        let config = CompletionConfig {
+            enable_postfix_completions: true,
+            enable_imports_on_the_fly: true,
+            enable_self_on_the_fly: true,
+            enable_private_editable: true,
+            full_function_signatures: false,
+            callable: Some(CallableSnippets::FillArguments),
+            snippet_cap: SnippetCap::new(true),
+            insert_use: InsertUseConfig {
+                granularity: ImportGranularity::Crate,
+                prefix_kind: hir::PrefixKind::ByCrate,
+                enforce_granularity: true,
+                group: true,
+                skip_glob_imports: true,
+            },
+            snippets: Vec::new(),
+            prefer_no_std: false,
+            prefer_prelude: true,
+            limit: None,
+        };
+        let position =
+            FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
+        analysis.completions(&config, position, None).unwrap();
     }
 
     profile::init_from("*>5");
@@ -116,8 +158,8 @@ fn integrated_completion_benchmark() {
         let _it = stdx::timeit("change");
         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
         let completion_offset =
-            patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
-                + "sel".len();
+            patch(&mut text, "sel;\ndb.struct_data(self.id)", ";sel;\ndb.struct_data(self.id)")
+                + ";sel".len();
         let mut change = Change::new();
         change.change_file(file_id, Some(Arc::from(text)));
         host.apply_change(change);
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index be5b954ad34..f81dff8840c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -275,10 +275,19 @@ impl ast::Path {
         successors(Some(self.clone()), ast::Path::qualifier).last().unwrap()
     }
 
+    pub fn first_qualifier(&self) -> Option<ast::Path> {
+        successors(self.qualifier(), ast::Path::qualifier).last()
+    }
+
     pub fn first_segment(&self) -> Option<ast::PathSegment> {
         self.first_qualifier_or_self().segment()
     }
 
+    // FIXME: Check usages of Self::segments, they might be wrong because of the logic of the bloew function
+    pub fn segments_of_this_path_only_rev(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
+        self.qualifiers_and_self().filter_map(|it| it.segment())
+    }
+
     pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
         successors(self.first_segment(), |p| {
             p.parent_path().parent_path().and_then(|p| p.segment())
@@ -289,6 +298,10 @@ impl ast::Path {
         successors(self.qualifier(), |p| p.qualifier())
     }
 
+    pub fn qualifiers_and_self(&self) -> impl Iterator<Item = ast::Path> + Clone {
+        successors(Some(self.clone()), |p| p.qualifier())
+    }
+
     pub fn top_path(&self) -> ast::Path {
         let mut this = self.clone();
         while let Some(path) = this.parent_path() {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
index 3e43df2d0d5..16f7356b1e3 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
@@ -76,9 +76,6 @@ pub trait HasDocComments: HasAttrs {
     fn doc_comments(&self) -> DocCommentIter {
         DocCommentIter { iter: self.syntax().children_with_tokens() }
     }
-    fn doc_comments_and_attrs(&self) -> AttrDocCommentIter {
-        AttrDocCommentIter { iter: self.syntax().children_with_tokens() }
-    }
 }
 
 impl DocCommentIter {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/utils.rs b/src/tools/rust-analyzer/crates/syntax/src/utils.rs
index 25f34ea9d39..a38f8b2b55d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/utils.rs
@@ -1,48 +1,8 @@
 //! A set of utils methods to reuse on other abstraction levels
 
-use itertools::Itertools;
-
-use crate::{ast, match_ast, AstNode, SyntaxKind};
-
-pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String {
-    path.syntax()
-        .children()
-        .filter_map(|node| {
-            match_ast! {
-                match node {
-                    ast::PathSegment(it) => {
-                        Some(it.name_ref()?.to_string())
-                    },
-                    ast::Path(it) => {
-                        Some(path_to_string_stripping_turbo_fish(&it))
-                    },
-                    _ => None,
-                }
-            }
-        })
-        .join("::")
-}
+use crate::SyntaxKind;
 
 pub fn is_raw_identifier(name: &str) -> bool {
     let is_keyword = SyntaxKind::from_keyword(name).is_some();
     is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
 }
-
-#[cfg(test)]
-mod tests {
-    use super::path_to_string_stripping_turbo_fish;
-    use crate::ast::make;
-
-    #[test]
-    fn turbofishes_are_stripped() {
-        assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),);
-        assert_eq!(
-            "Vec::new",
-            path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")),
-        );
-        assert_eq!(
-            "Vec::new",
-            path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")),
-        );
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
index 8ffda5d78d1..ef5b10ee9db 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
@@ -61,13 +61,17 @@ pub use paths::{AbsPath, AbsPathBuf};
 /// Most functions in rust-analyzer use this when they need to refer to a file.
 #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
 pub struct FileId(u32);
+// pub struct FileId(NonMaxU32);
 
 impl FileId {
     /// Think twice about using this outside of tests. If this ends up in a wrong place it will cause panics!
+    // FIXME: To be removed once we get rid of all `SpanData::DUMMY` usages.
     pub const BOGUS: FileId = FileId(0xe4e4e);
+    pub const MAX_FILE_ID: u32 = 0x7fff_ffff;
 
     #[inline]
-    pub fn from_raw(raw: u32) -> FileId {
+    pub const fn from_raw(raw: u32) -> FileId {
+        assert!(raw <= Self::MAX_FILE_ID);
         FileId(raw)
     }
 
diff --git a/src/tools/rust-analyzer/lib/line-index/Cargo.toml b/src/tools/rust-analyzer/lib/line-index/Cargo.toml
index b7b4a01818e..494a7fa979a 100644
--- a/src/tools/rust-analyzer/lib/line-index/Cargo.toml
+++ b/src/tools/rust-analyzer/lib/line-index/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "line-index"
-version = "0.1.0"
+version = "0.1.1"
 description = "Maps flat `TextSize` offsets to/from `(line, column)` representation."
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-index"
diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs
index e52cbfca3e6..092ab8c593c 100644
--- a/src/tools/rust-analyzer/xtask/src/flags.rs
+++ b/src/tools/rust-analyzer/xtask/src/flags.rs
@@ -110,6 +110,7 @@ pub struct PublishReleaseNotes {
 #[derive(Debug)]
 pub enum MeasurementType {
     Build,
+    RustcTests,
     AnalyzeSelf,
     AnalyzeRipgrep,
     AnalyzeWebRender,
@@ -122,6 +123,7 @@ impl FromStr for MeasurementType {
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
             "build" => Ok(Self::Build),
+            "rustc_tests" => Ok(Self::RustcTests),
             "self" => Ok(Self::AnalyzeSelf),
             "ripgrep-13.0.0" => Ok(Self::AnalyzeRipgrep),
             "webrender-2022" => Ok(Self::AnalyzeWebRender),
@@ -135,6 +137,7 @@ impl AsRef<str> for MeasurementType {
     fn as_ref(&self) -> &str {
         match self {
             Self::Build => "build",
+            Self::RustcTests => "rustc_tests",
             Self::AnalyzeSelf => "self",
             Self::AnalyzeRipgrep => "ripgrep-13.0.0",
             Self::AnalyzeWebRender => "webrender-2022",
diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs
index 59d41d8e4b8..845928432c4 100644
--- a/src/tools/rust-analyzer/xtask/src/metrics.rs
+++ b/src/tools/rust-analyzer/xtask/src/metrics.rs
@@ -36,6 +36,9 @@ impl flags::Metrics {
                     MeasurementType::Build => {
                         metrics.measure_build(sh)?;
                     }
+                    MeasurementType::RustcTests => {
+                        metrics.measure_rustc_tests(sh)?;
+                    }
                     MeasurementType::AnalyzeSelf => {
                         metrics.measure_analysis_stats_self(sh)?;
                     }
@@ -50,6 +53,7 @@ impl flags::Metrics {
             }
             None => {
                 metrics.measure_build(sh)?;
+                metrics.measure_rustc_tests(sh)?;
                 metrics.measure_analysis_stats_self(sh)?;
                 metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeRipgrep.as_ref())?;
                 metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeWebRender.as_ref())?;
@@ -78,6 +82,19 @@ impl Metrics {
         self.report("build", time.as_millis() as u64, "ms".into());
         Ok(())
     }
+
+    fn measure_rustc_tests(&mut self, sh: &Shell) -> anyhow::Result<()> {
+        eprintln!("\nMeasuring rustc tests");
+
+        cmd!(sh, "git clone --depth=1 https://github.com/rust-lang/rust").run()?;
+
+        let output = cmd!(sh, "./target/release/rust-analyzer rustc-tests ./rust").read()?;
+        for (metric, value, unit) in parse_metrics(&output) {
+            self.report(metric, value, unit.into());
+        }
+        Ok(())
+    }
+
     fn measure_analysis_stats_self(&mut self, sh: &Shell) -> anyhow::Result<()> {
         self.measure_analysis_stats_path(sh, "self", ".")
     }
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 6e630a80454..86881ef362e 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -396,16 +396,16 @@ function loadSearchJS(doc_folder, resource_suffix) {
     const staticFiles = path.join(doc_folder, "static.files");
     const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/));
     const searchModule = require(path.join(staticFiles, searchJs));
-    const searchWords = searchModule.initSearch(searchIndex.searchIndex);
+    searchModule.initSearch(searchIndex.searchIndex);
 
     return {
         doSearch: function(queryStr, filterCrate, currentCrate) {
-            return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
+            return searchModule.execQuery(searchModule.parseQuery(queryStr),
                 filterCrate, currentCrate);
         },
         getCorrections: function(queryStr, filterCrate, currentCrate) {
             const parsedQuery = searchModule.parseQuery(queryStr);
-            searchModule.execQuery(parsedQuery, searchWords, filterCrate, currentCrate);
+            searchModule.execQuery(parsedQuery, filterCrate, currentCrate);
             return parsedQuery.correction;
         },
         parseQuery: searchModule.parseQuery,
diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
index cbc4c90b8f9..bafef7b0f46 100644
--- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs
+++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
@@ -67,7 +67,7 @@ fn parse_cfg_if_inner<'a>(
                 Ok(None) => continue,
                 Err(err) => {
                     err.cancel();
-                    parser.sess.span_diagnostic.reset_err_count();
+                    parser.sess.dcx.reset_err_count();
                     return Err(
                         "Expected item inside cfg_if block, but failed to parse it as an item",
                     );
diff --git a/src/tools/rustfmt/src/parse/macros/lazy_static.rs b/src/tools/rustfmt/src/parse/macros/lazy_static.rs
index a8c2feec453..8b1dc6694d6 100644
--- a/src/tools/rustfmt/src/parse/macros/lazy_static.rs
+++ b/src/tools/rustfmt/src/parse/macros/lazy_static.rs
@@ -16,8 +16,8 @@ pub(crate) fn parse_lazy_static(
         ($method:ident $(,)* $($arg:expr),* $(,)*) => {
             match parser.$method($($arg,)*) {
                 Ok(val) => {
-                    if parser.sess.span_diagnostic.has_errors().is_some() {
-                        parser.sess.span_diagnostic.reset_err_count();
+                    if parser.sess.dcx.has_errors().is_some() {
+                        parser.sess.dcx.reset_err_count();
                         return None;
                     } else {
                         val
@@ -25,7 +25,7 @@ pub(crate) fn parse_lazy_static(
                 }
                 Err(err) => {
                     err.cancel();
-                    parser.sess.span_diagnostic.reset_err_count();
+                    parser.sess.dcx.reset_err_count();
                     return None;
                 }
             }
diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs
index 7a802f7a88e..2dd2622174f 100644
--- a/src/tools/rustfmt/src/parse/macros/mod.rs
+++ b/src/tools/rustfmt/src/parse/macros/mod.rs
@@ -28,8 +28,8 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
             let mut cloned_parser = (*parser).clone();
             match $parser(&mut cloned_parser) {
                 Ok(x) => {
-                    if parser.sess.span_diagnostic.has_errors().is_some() {
-                        parser.sess.span_diagnostic.reset_err_count();
+                    if parser.sess.dcx.has_errors().is_some() {
+                        parser.sess.dcx.reset_err_count();
                     } else {
                         // Parsing succeeded.
                         *parser = cloned_parser;
@@ -38,7 +38,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
                 }
                 Err(e) => {
                     e.cancel();
-                    parser.sess.span_diagnostic.reset_err_count();
+                    parser.sess.dcx.reset_err_count();
                 }
             }
         };
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 06f9c4c6243..e8f7b422ada 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
 use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
 use rustc_errors::emitter::{DynEmitter, Emitter, EmitterWriter};
 use rustc_errors::translation::Translate;
-use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
+use rustc_errors::{ColorConfig, DiagCtxt, Diagnostic, Level as DiagnosticLevel};
 use rustc_session::parse::ParseSess as RawParseSess;
 use rustc_span::{
     source_map::{FilePathMapping, SourceMap},
@@ -118,13 +118,13 @@ impl From<Color> for ColorConfig {
     }
 }
 
-fn default_handler(
+fn default_dcx(
     source_map: Lrc<SourceMap>,
     ignore_path_set: Lrc<IgnorePathSet>,
     can_reset: Lrc<AtomicBool>,
     hide_parse_errors: bool,
     color: Color,
-) -> Handler {
+) -> DiagCtxt {
     let supports_color = term::stderr().map_or(false, |term| term.supports_color());
     let emit_color = if supports_color {
         ColorConfig::from(color)
@@ -141,7 +141,7 @@ fn default_handler(
         );
         Box::new(EmitterWriter::stderr(emit_color, fallback_bundle).sm(Some(source_map.clone())))
     };
-    Handler::with_emitter(Box::new(SilentOnIgnoredFilesEmitter {
+    DiagCtxt::with_emitter(Box::new(SilentOnIgnoredFilesEmitter {
         has_non_ignorable_parser_errors: false,
         source_map,
         emitter,
@@ -159,14 +159,14 @@ impl ParseSess {
         let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let can_reset_errors = Lrc::new(AtomicBool::new(false));
 
-        let handler = default_handler(
+        let dcx = default_dcx(
             Lrc::clone(&source_map),
             Lrc::clone(&ignore_path_set),
             Lrc::clone(&can_reset_errors),
             config.hide_parse_errors(),
             config.color(),
         );
-        let parse_sess = RawParseSess::with_span_handler(handler, source_map);
+        let parse_sess = RawParseSess::with_dcx(dcx, source_map);
 
         Ok(ParseSess {
             parse_sess,
@@ -218,7 +218,7 @@ impl ParseSess {
     }
 
     pub(crate) fn set_silent_emitter(&mut self) {
-        self.parse_sess.span_diagnostic = Handler::with_emitter(silent_emitter());
+        self.parse_sess.dcx = DiagCtxt::with_emitter(silent_emitter());
     }
 
     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
@@ -285,7 +285,7 @@ impl ParseSess {
 impl ParseSess {
     pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
         for diagnostic in diagnostics {
-            self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic);
+            self.parse_sess.dcx.emit_diagnostic(diagnostic);
         }
     }
 
@@ -294,11 +294,11 @@ impl ParseSess {
     }
 
     pub(super) fn has_errors(&self) -> bool {
-        self.parse_sess.span_diagnostic.has_errors().is_some()
+        self.parse_sess.dcx.has_errors().is_some()
     }
 
     pub(super) fn reset_errors(&self) {
-        self.parse_sess.span_diagnostic.reset_err_count();
+        self.parse_sess.dcx.reset_err_count();
     }
 }
 
diff --git a/tests/codegen/debug-accessibility/crate-enum.rs b/tests/codegen/debug-accessibility/crate-enum.rs
new file mode 100644
index 00000000000..eeea18dd815
--- /dev/null
+++ b/tests/codegen/debug-accessibility/crate-enum.rs
@@ -0,0 +1,26 @@
+// compile-flags: -C debuginfo=2
+// ignore-tidy-linelength
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for crate-visibility enums.
+
+mod module {
+    use std::hint::black_box;
+
+    pub(crate) enum CrateFooEnum {
+        A,
+        B(u32),
+        C { x: u32 },
+    }
+
+    // NONMSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "CrateFooEnum"{{.*}}flags: DIFlagProtected{{.*}})
+    // MSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<crate_enum::module::CrateFooEnum>"{{.*}}flags: DIFlagProtected{{.*}})
+    pub fn use_everything() {
+        black_box(CrateFooEnum::A);
+    }
+}
+
+fn main() {
+    module::use_everything();
+}
diff --git a/tests/codegen/debug-accessibility/crate-struct.rs b/tests/codegen/debug-accessibility/crate-struct.rs
new file mode 100644
index 00000000000..68d126a3478
--- /dev/null
+++ b/tests/codegen/debug-accessibility/crate-struct.rs
@@ -0,0 +1,23 @@
+// compile-flags: -C debuginfo=2
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for crate-visibility structs.
+
+mod module {
+    use std::hint::black_box;
+
+    pub(crate) struct CrateFooStruct {
+        x: u32,
+    }
+
+    // CHECK: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "CrateFooStruct"{{.*}}flags: DIFlagProtected{{.*}})
+
+    pub fn use_everything() {
+        black_box(CrateFooStruct { x: 2 });
+    }
+}
+
+fn main() {
+    module::use_everything();
+}
diff --git a/tests/codegen/debug-accessibility/private-enum.rs b/tests/codegen/debug-accessibility/private-enum.rs
new file mode 100644
index 00000000000..7f81026ddec
--- /dev/null
+++ b/tests/codegen/debug-accessibility/private-enum.rs
@@ -0,0 +1,21 @@
+// compile-flags: -C debuginfo=2
+// ignore-tidy-linelength
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for private enums.
+
+use std::hint::black_box;
+
+enum PrivateFooEnum {
+    A,
+    B(u32),
+    C { x: u32 },
+}
+
+// NONMSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "PrivateFooEnum"{{.*}}flags: DIFlagPrivate{{.*}})
+// MSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<private_enum::PrivateFooEnum>"{{.*}}flags: DIFlagPrivate{{.*}})
+
+fn main() {
+    black_box(PrivateFooEnum::A);
+}
diff --git a/tests/codegen/debug-accessibility/private-struct.rs b/tests/codegen/debug-accessibility/private-struct.rs
new file mode 100644
index 00000000000..43b260f9024
--- /dev/null
+++ b/tests/codegen/debug-accessibility/private-struct.rs
@@ -0,0 +1,17 @@
+// compile-flags: -C debuginfo=2
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for private structs.
+
+use std::hint::black_box;
+
+struct PrivateFooStruct {
+    x: u32,
+}
+
+// CHECK: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "PrivateFooStruct"{{.*}}flags: DIFlagPrivate{{.*}})
+
+fn main() {
+    black_box(PrivateFooStruct { x: 1 });
+}
diff --git a/tests/codegen/debug-accessibility/public-enum.rs b/tests/codegen/debug-accessibility/public-enum.rs
new file mode 100644
index 00000000000..29ae5fd6421
--- /dev/null
+++ b/tests/codegen/debug-accessibility/public-enum.rs
@@ -0,0 +1,21 @@
+// compile-flags: -C debuginfo=2
+// ignore-tidy-linelength
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for types and their fields.
+
+use std::hint::black_box;
+
+pub enum PublicFooEnum {
+    A,
+    B(u32),
+    C { x: u32 },
+}
+
+// NONMSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "PublicFooEnum"{{.*}}flags: DIFlagPublic{{.*}})
+// MSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<public_enum::PublicFooEnum>"{{.*}}flags: DIFlagPublic{{.*}})
+
+fn main() {
+    black_box(PublicFooEnum::A);
+}
diff --git a/tests/codegen/debug-accessibility/public-struct.rs b/tests/codegen/debug-accessibility/public-struct.rs
new file mode 100644
index 00000000000..e7cd9b40d09
--- /dev/null
+++ b/tests/codegen/debug-accessibility/public-struct.rs
@@ -0,0 +1,17 @@
+// compile-flags: -C debuginfo=2
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for public structs.
+
+use std::hint::black_box;
+
+pub struct PublicFooStruct {
+    x: u32,
+}
+
+// CHECK: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "PublicFooStruct"{{.*}}flags: DIFlagPublic{{.*}})
+
+fn main() {
+    black_box(PublicFooStruct { x: 4 });
+}
diff --git a/tests/codegen/debug-accessibility/struct-fields.rs b/tests/codegen/debug-accessibility/struct-fields.rs
new file mode 100644
index 00000000000..76831bdc6c6
--- /dev/null
+++ b/tests/codegen/debug-accessibility/struct-fields.rs
@@ -0,0 +1,30 @@
+// compile-flags: -C debuginfo=2
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for struct fields.
+
+mod module {
+    use std::hint::black_box;
+
+    struct StructFields {
+        a: u32,
+        pub(crate) b: u32,
+        pub(super) c: u32,
+        pub d: u32,
+    }
+
+    // CHECK: [[StructFields:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "StructFields"{{.*}}flags: DIFlagPrivate{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: [[StructFields]]{{.*}}flags: DIFlagPrivate{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: [[StructFields]]{{.*}}flags: DIFlagProtected{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: [[StructFields]]{{.*}}flags: DIFlagProtected{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "d", scope: [[StructFields]]{{.*}}flags: DIFlagPublic{{.*}})
+
+    pub fn use_everything() {
+        black_box(StructFields { a: 1, b: 2, c: 3, d: 4 });
+    }
+}
+
+fn main() {
+    module::use_everything();
+}
diff --git a/tests/codegen/debug-accessibility/super-enum.rs b/tests/codegen/debug-accessibility/super-enum.rs
new file mode 100644
index 00000000000..9d83fb45bd0
--- /dev/null
+++ b/tests/codegen/debug-accessibility/super-enum.rs
@@ -0,0 +1,27 @@
+// compile-flags: -C debuginfo=2
+// ignore-tidy-linelength
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for super-visibility enums.
+
+mod module {
+    use std::hint::black_box;
+
+    pub(super) enum SuperFooEnum {
+        A,
+        B(u32),
+        C { x: u32 },
+    }
+
+    // NONMSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "SuperFooEnum"{{.*}}flags: DIFlagProtected{{.*}})
+    // MSVC: {{!.*}} = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<super_enum::module::SuperFooEnum>"{{.*}}flags: DIFlagProtected{{.*}})
+
+    pub fn use_everything() {
+        black_box(SuperFooEnum::A);
+    }
+}
+
+fn main() {
+    module::use_everything();
+}
diff --git a/tests/codegen/debug-accessibility/super-struct.rs b/tests/codegen/debug-accessibility/super-struct.rs
new file mode 100644
index 00000000000..481006c3965
--- /dev/null
+++ b/tests/codegen/debug-accessibility/super-struct.rs
@@ -0,0 +1,23 @@
+// compile-flags: -C debuginfo=2
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for super-visibility structs.
+
+mod module {
+    use std::hint::black_box;
+
+    pub(super) struct SuperFooStruct {
+        x: u32,
+    }
+
+    // CHECK: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "SuperFooStruct"{{.*}}flags: DIFlagProtected{{.*}})
+
+    pub fn use_everything() {
+        black_box(SuperFooStruct { x: 3 });
+    }
+}
+
+fn main() {
+    module::use_everything();
+}
diff --git a/tests/codegen/debug-accessibility/tuple-fields.rs b/tests/codegen/debug-accessibility/tuple-fields.rs
new file mode 100644
index 00000000000..1163ba2c7c3
--- /dev/null
+++ b/tests/codegen/debug-accessibility/tuple-fields.rs
@@ -0,0 +1,24 @@
+// compile-flags: -C debuginfo=2
+
+#![allow(dead_code)]
+
+// Checks that visibility information is present in the debuginfo for tuple struct fields.
+
+mod module {
+    use std::hint::black_box;
+
+    struct TupleFields(u32, pub(crate) u32, pub(super) u32, pub u32);
+
+    // CHECK: [[TupleFields:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "TupleFields"{{.*}}flags: DIFlagPrivate{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "__0", scope: [[TupleFields]]{{.*}}flags: DIFlagPrivate{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "__1", scope: [[TupleFields]]{{.*}}flags: DIFlagProtected{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "__2", scope: [[TupleFields]]{{.*}}flags: DIFlagProtected{{.*}})
+    // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "__3", scope: [[TupleFields]]{{.*}}flags: DIFlagPublic{{.*}})
+    pub fn use_everything() {
+        black_box(TupleFields(1, 2, 3, 4));
+    }
+}
+
+fn main() {
+    module::use_everything();
+}
diff --git a/tests/coverage/assert_not.cov-map b/tests/coverage/assert_not.cov-map
new file mode 100644
index 00000000000..788bc5dbf58
--- /dev/null
+++ b/tests/coverage/assert_not.cov-map
@@ -0,0 +1,16 @@
+Function name: assert_not::main
+Raw bytes (33): 0x[01, 01, 02, 05, 00, 0d, 00, 05, 01, 06, 01, 01, 12, 05, 02, 05, 00, 14, 02, 01, 05, 00, 14, 0d, 01, 05, 00, 16, 06, 01, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 2
+- expression 0 operands: lhs = Counter(1), rhs = Zero
+- expression 1 operands: lhs = Counter(3), rhs = Zero
+Number of file 0 mappings: 5
+- Code(Counter(0)) at (prev + 6, 1) to (start + 1, 18)
+- Code(Counter(1)) at (prev + 2, 5) to (start + 0, 20)
+- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 20)
+    = (c1 - Zero)
+- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 22)
+- Code(Expression(1, Sub)) at (prev + 1, 1) to (start + 0, 2)
+    = (c3 - Zero)
+
diff --git a/tests/coverage/assert_not.coverage b/tests/coverage/assert_not.coverage
new file mode 100644
index 00000000000..4cfdab974ed
--- /dev/null
+++ b/tests/coverage/assert_not.coverage
@@ -0,0 +1,12 @@
+   LL|       |// edition: 2021
+   LL|       |
+   LL|       |// Regression test for <https://github.com/rust-lang/rust/issues/118904>.
+   LL|       |// `assert!(true)` and `assert!(!false)` should have similar coverage spans.
+   LL|       |
+   LL|      1|fn main() {
+   LL|      1|    assert!(true);
+   LL|      1|    assert!(!false);
+   LL|      1|    assert!(!!true);
+   LL|      1|    assert!(!!!false);
+   LL|      1|}
+
diff --git a/tests/coverage/assert_not.rs b/tests/coverage/assert_not.rs
new file mode 100644
index 00000000000..95204fcadd1
--- /dev/null
+++ b/tests/coverage/assert_not.rs
@@ -0,0 +1,11 @@
+// edition: 2021
+
+// Regression test for <https://github.com/rust-lang/rust/issues/118904>.
+// `assert!(true)` and `assert!(!false)` should have similar coverage spans.
+
+fn main() {
+    assert!(true);
+    assert!(!false);
+    assert!(!!true);
+    assert!(!!!false);
+}
diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map
index e4354a1af87..6bdcca40ed6 100644
--- a/tests/coverage/async.cov-map
+++ b/tests/coverage/async.cov-map
@@ -1,20 +1,20 @@
 Function name: async::c
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 05, 01, 00, 19]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 07, 01, 00, 19]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 5, 1) to (start + 0, 25)
+- Code(Counter(0)) at (prev + 7, 1) to (start + 0, 25)
 
 Function name: async::c::{closure#0}
-Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 05, 19, 01, 0e, 05, 02, 09, 00, 0a, 02, 02, 09, 00, 0a, 07, 02, 01, 00, 02]
+Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 07, 19, 01, 0e, 05, 02, 09, 00, 0a, 02, 02, 09, 00, 0a, 07, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Counter(0), rhs = Counter(1)
 - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
 Number of file 0 mappings: 4
-- Code(Counter(0)) at (prev + 5, 25) to (start + 1, 14)
+- Code(Counter(0)) at (prev + 7, 25) to (start + 1, 14)
 - Code(Counter(1)) at (prev + 2, 9) to (start + 0, 10)
 - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10)
     = (c0 - c1)
@@ -22,136 +22,84 @@ Number of file 0 mappings: 4
     = (c1 + (c0 - c1))
 
 Function name: async::d
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 0d, 01, 00, 14]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 0f, 01, 00, 14]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 13, 1) to (start + 0, 20)
+- Code(Counter(0)) at (prev + 15, 1) to (start + 0, 20)
 
 Function name: async::d::{closure#0}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 0d, 14, 00, 19]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 0f, 14, 00, 19]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 13, 20) to (start + 0, 25)
+- Code(Counter(0)) at (prev + 15, 20) to (start + 0, 25)
 
 Function name: async::e (unused)
-Raw bytes (9): 0x[01, 01, 00, 01, 00, 0f, 01, 00, 14]
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 11, 01, 00, 14]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Zero) at (prev + 15, 1) to (start + 0, 20)
+- Code(Zero) at (prev + 17, 1) to (start + 0, 20)
 
 Function name: async::e::{closure#0} (unused)
-Raw bytes (9): 0x[01, 01, 00, 01, 00, 0f, 14, 00, 19]
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 11, 14, 00, 19]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Zero) at (prev + 15, 20) to (start + 0, 25)
-
-Function name: async::executor::block_on::<core::pin::Pin<&mut async::i::{closure#0}>>
-Raw bytes (40): 0x[01, 01, 03, 0b, 05, 01, 05, 01, 05, 06, 01, 6e, 05, 0a, 36, 02, 0d, 20, 00, 23, 0b, 00, 27, 00, 49, 02, 01, 17, 00, 1a, 05, 01, 0e, 00, 0f, 02, 02, 05, 00, 06]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 3
-- expression 0 operands: lhs = Expression(2, Add), rhs = Counter(1)
-- expression 1 operands: lhs = Counter(0), rhs = Counter(1)
-- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
-Number of file 0 mappings: 6
-- Code(Counter(0)) at (prev + 110, 5) to (start + 10, 54)
-- Code(Expression(0, Sub)) at (prev + 13, 32) to (start + 0, 35)
-    = ((c0 + c1) - c1)
-- Code(Expression(2, Add)) at (prev + 0, 39) to (start + 0, 73)
-    = (c0 + c1)
-- Code(Expression(0, Sub)) at (prev + 1, 23) to (start + 0, 26)
-    = ((c0 + c1) - c1)
-- Code(Counter(1)) at (prev + 1, 14) to (start + 0, 15)
-- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
-    = ((c0 + c1) - c1)
-
-Function name: async::executor::block_on::VTABLE::{closure#0}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 72, 11, 00, 31]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 114, 17) to (start + 0, 49)
-
-Function name: async::executor::block_on::VTABLE::{closure#1}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 73, 11, 00, 31]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 115, 17) to (start + 0, 49)
-
-Function name: async::executor::block_on::VTABLE::{closure#2}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 74, 11, 00, 31]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 116, 17) to (start + 0, 49)
-
-Function name: async::executor::block_on::VTABLE::{closure#3}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 75, 11, 00, 13]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 117, 17) to (start + 0, 19)
+- Code(Zero) at (prev + 17, 20) to (start + 0, 25)
 
 Function name: async::f
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 01, 00, 14]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 01, 00, 14]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 20)
+- Code(Counter(0)) at (prev + 19, 1) to (start + 0, 20)
 
 Function name: async::f::{closure#0}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 14, 00, 19]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 14, 00, 19]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 17, 20) to (start + 0, 25)
+- Code(Counter(0)) at (prev + 19, 20) to (start + 0, 25)
 
 Function name: async::foo (unused)
-Raw bytes (9): 0x[01, 01, 00, 01, 00, 13, 01, 00, 1e]
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 15, 01, 00, 1e]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Zero) at (prev + 19, 1) to (start + 0, 30)
+- Code(Zero) at (prev + 21, 1) to (start + 0, 30)
 
 Function name: async::foo::{closure#0} (unused)
-Raw bytes (9): 0x[01, 01, 00, 01, 00, 13, 1e, 00, 2d]
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 15, 1e, 00, 2d]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Zero) at (prev + 19, 30) to (start + 0, 45)
+- Code(Zero) at (prev + 21, 30) to (start + 0, 45)
 
 Function name: async::g
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 15, 01, 00, 17]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 17, 01, 00, 17]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 21, 1) to (start + 0, 23)
+- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 23)
 
 Function name: async::g::{closure#0} (unused)
-Raw bytes (69): 0x[01, 01, 00, 0d, 00, 15, 17, 01, 0c, 00, 02, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
+Raw bytes (69): 0x[01, 01, 00, 0d, 00, 17, 17, 01, 0c, 00, 02, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 13
-- Code(Zero) at (prev + 21, 23) to (start + 1, 12)
+- Code(Zero) at (prev + 23, 23) to (start + 1, 12)
 - Code(Zero) at (prev + 2, 9) to (start + 0, 10)
 - Code(Zero) at (prev + 0, 14) to (start + 0, 17)
 - Code(Zero) at (prev + 0, 18) to (start + 0, 23)
@@ -166,20 +114,20 @@ Number of file 0 mappings: 13
 - Code(Zero) at (prev + 2, 1) to (start + 0, 2)
 
 Function name: async::h
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 1d, 01, 00, 16]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 1f, 01, 00, 16]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 29, 1) to (start + 0, 22)
+- Code(Counter(0)) at (prev + 31, 1) to (start + 0, 22)
 
 Function name: async::h::{closure#0} (unused)
-Raw bytes (44): 0x[01, 01, 00, 08, 00, 1d, 16, 03, 0c, 00, 04, 09, 00, 0a, 00, 00, 0e, 00, 13, 00, 00, 14, 00, 19, 00, 00, 1a, 00, 1b, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
+Raw bytes (44): 0x[01, 01, 00, 08, 00, 1f, 16, 03, 0c, 00, 04, 09, 00, 0a, 00, 00, 0e, 00, 13, 00, 00, 14, 00, 19, 00, 00, 1a, 00, 1b, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 8
-- Code(Zero) at (prev + 29, 22) to (start + 3, 12)
+- Code(Zero) at (prev + 31, 22) to (start + 3, 12)
 - Code(Zero) at (prev + 4, 9) to (start + 0, 10)
 - Code(Zero) at (prev + 0, 14) to (start + 0, 19)
 - Code(Zero) at (prev + 0, 20) to (start + 0, 25)
@@ -189,22 +137,22 @@ Number of file 0 mappings: 8
 - Code(Zero) at (prev + 2, 1) to (start + 0, 2)
 
 Function name: async::i
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 26, 01, 00, 13]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 28, 01, 00, 13]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 38, 1) to (start + 0, 19)
+- Code(Counter(0)) at (prev + 40, 1) to (start + 0, 19)
 
 Function name: async::i::{closure#0}
-Raw bytes (78): 0x[01, 01, 02, 07, 21, 19, 1d, 0e, 01, 26, 13, 04, 0c, 0d, 05, 09, 00, 0a, 01, 00, 0e, 00, 12, 05, 00, 13, 00, 18, 09, 00, 1c, 00, 21, 0d, 00, 27, 00, 2a, 15, 00, 2b, 00, 30, 1d, 01, 09, 00, 0a, 11, 00, 0e, 00, 11, 25, 00, 12, 00, 17, 29, 00, 1b, 00, 20, 1d, 00, 24, 00, 26, 21, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
+Raw bytes (78): 0x[01, 01, 02, 07, 21, 19, 1d, 0e, 01, 28, 13, 04, 0c, 0d, 05, 09, 00, 0a, 01, 00, 0e, 00, 12, 05, 00, 13, 00, 18, 09, 00, 1c, 00, 21, 0d, 00, 27, 00, 2a, 15, 00, 2b, 00, 30, 1d, 01, 09, 00, 0a, 11, 00, 0e, 00, 11, 25, 00, 12, 00, 17, 29, 00, 1b, 00, 20, 1d, 00, 24, 00, 26, 21, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Expression(1, Add), rhs = Counter(8)
 - expression 1 operands: lhs = Counter(6), rhs = Counter(7)
 Number of file 0 mappings: 14
-- Code(Counter(0)) at (prev + 38, 19) to (start + 4, 12)
+- Code(Counter(0)) at (prev + 40, 19) to (start + 4, 12)
 - Code(Counter(3)) at (prev + 5, 9) to (start + 0, 10)
 - Code(Counter(0)) at (prev + 0, 14) to (start + 0, 18)
 - Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24)
@@ -221,14 +169,14 @@ Number of file 0 mappings: 14
     = ((c6 + c7) + c8)
 
 Function name: async::j
-Raw bytes (53): 0x[01, 01, 02, 07, 0d, 05, 09, 09, 01, 31, 01, 13, 0c, 05, 14, 09, 00, 0a, 01, 00, 0e, 00, 1b, 05, 00, 1f, 00, 27, 09, 01, 09, 00, 0a, 11, 00, 0e, 00, 1a, 09, 00, 1e, 00, 20, 0d, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
+Raw bytes (53): 0x[01, 01, 02, 07, 0d, 05, 09, 09, 01, 33, 01, 13, 0c, 05, 14, 09, 00, 0a, 01, 00, 0e, 00, 1b, 05, 00, 1f, 00, 27, 09, 01, 09, 00, 0a, 11, 00, 0e, 00, 1a, 09, 00, 1e, 00, 20, 0d, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Expression(1, Add), rhs = Counter(3)
 - expression 1 operands: lhs = Counter(1), rhs = Counter(2)
 Number of file 0 mappings: 9
-- Code(Counter(0)) at (prev + 49, 1) to (start + 19, 12)
+- Code(Counter(0)) at (prev + 51, 1) to (start + 19, 12)
 - Code(Counter(1)) at (prev + 20, 9) to (start + 0, 10)
 - Code(Counter(0)) at (prev + 0, 14) to (start + 0, 27)
 - Code(Counter(1)) at (prev + 0, 31) to (start + 0, 39)
@@ -240,14 +188,14 @@ Number of file 0 mappings: 9
     = ((c1 + c2) + c3)
 
 Function name: async::j::c
-Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 33, 05, 01, 12, 05, 02, 0d, 00, 0e, 02, 0a, 0d, 00, 0e, 07, 02, 05, 00, 06]
+Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 35, 05, 01, 12, 05, 02, 0d, 00, 0e, 02, 0a, 0d, 00, 0e, 07, 02, 05, 00, 06]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Counter(0), rhs = Counter(1)
 - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
 Number of file 0 mappings: 4
-- Code(Counter(0)) at (prev + 51, 5) to (start + 1, 18)
+- Code(Counter(0)) at (prev + 53, 5) to (start + 1, 18)
 - Code(Counter(1)) at (prev + 2, 13) to (start + 0, 14)
 - Code(Expression(0, Sub)) at (prev + 10, 13) to (start + 0, 14)
     = (c0 - c1)
@@ -255,35 +203,35 @@ Number of file 0 mappings: 4
     = (c1 + (c0 - c1))
 
 Function name: async::j::d
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 42, 05, 00, 17]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 44, 05, 00, 17]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 66, 5) to (start + 0, 23)
+- Code(Counter(0)) at (prev + 68, 5) to (start + 0, 23)
 
 Function name: async::j::f
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 43, 05, 00, 17]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 45, 05, 00, 17]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 67, 5) to (start + 0, 23)
+- Code(Counter(0)) at (prev + 69, 5) to (start + 0, 23)
 
 Function name: async::k (unused)
-Raw bytes (29): 0x[01, 01, 00, 05, 00, 4b, 01, 01, 0c, 00, 02, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
+Raw bytes (29): 0x[01, 01, 00, 05, 00, 4d, 01, 01, 0c, 00, 02, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 5
-- Code(Zero) at (prev + 75, 1) to (start + 1, 12)
+- Code(Zero) at (prev + 77, 1) to (start + 1, 12)
 - Code(Zero) at (prev + 2, 14) to (start + 0, 16)
 - Code(Zero) at (prev + 1, 14) to (start + 0, 16)
 - Code(Zero) at (prev + 1, 14) to (start + 0, 16)
 - Code(Zero) at (prev + 2, 1) to (start + 0, 2)
 
 Function name: async::l
-Raw bytes (37): 0x[01, 01, 04, 01, 07, 05, 09, 0f, 02, 09, 05, 05, 01, 53, 01, 01, 0c, 02, 02, 0e, 00, 10, 05, 01, 0e, 00, 10, 09, 01, 0e, 00, 10, 0b, 02, 01, 00, 02]
+Raw bytes (37): 0x[01, 01, 04, 01, 07, 05, 09, 0f, 02, 09, 05, 05, 01, 55, 01, 01, 0c, 02, 02, 0e, 00, 10, 05, 01, 0e, 00, 10, 09, 01, 0e, 00, 10, 0b, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 4
@@ -292,7 +240,7 @@ Number of expressions: 4
 - expression 2 operands: lhs = Expression(3, Add), rhs = Expression(0, Sub)
 - expression 3 operands: lhs = Counter(2), rhs = Counter(1)
 Number of file 0 mappings: 5
-- Code(Counter(0)) at (prev + 83, 1) to (start + 1, 12)
+- Code(Counter(0)) at (prev + 85, 1) to (start + 1, 12)
 - Code(Expression(0, Sub)) at (prev + 2, 14) to (start + 0, 16)
     = (c0 - (c1 + c2))
 - Code(Counter(1)) at (prev + 1, 14) to (start + 0, 16)
@@ -301,26 +249,26 @@ Number of file 0 mappings: 5
     = ((c2 + c1) + (c0 - (c1 + c2)))
 
 Function name: async::m
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 5b, 01, 00, 19]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 5d, 01, 00, 19]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 91, 1) to (start + 0, 25)
+- Code(Counter(0)) at (prev + 93, 1) to (start + 0, 25)
 
 Function name: async::m::{closure#0} (unused)
-Raw bytes (9): 0x[01, 01, 00, 01, 00, 5b, 19, 00, 22]
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 5d, 19, 00, 22]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Zero) at (prev + 91, 25) to (start + 0, 34)
+- Code(Zero) at (prev + 93, 25) to (start + 0, 34)
 
 Function name: async::main
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 5d, 01, 08, 02]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 5f, 01, 08, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 93, 1) to (start + 8, 2)
+- Code(Counter(0)) at (prev + 95, 1) to (start + 8, 2)
 
diff --git a/tests/coverage/async.coverage b/tests/coverage/async.coverage
index 07bc16c2d92..015e03d5165 100644
--- a/tests/coverage/async.coverage
+++ b/tests/coverage/async.coverage
@@ -1,6 +1,8 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |#![feature(noop_waker)]
    LL|       |#![allow(unused_assignments, dead_code)]
-   LL|       |
-   LL|       |// compile-flags: --edition=2018 -C opt-level=1
+   LL|       |// edition: 2018
+   LL|       |// compile-flags: -Copt-level=1
    LL|       |
    LL|      1|async fn c(x: u8) -> u8 {
    LL|      1|    if x == 8 {
@@ -108,32 +110,21 @@
    LL|      1|}
    LL|       |
    LL|       |mod executor {
-   LL|       |    use core::{
-   LL|       |        future::Future,
-   LL|       |        pin::Pin,
-   LL|       |        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
-   LL|       |    };
-   LL|       |
-   LL|      1|    pub fn block_on<F: Future>(mut future: F) -> F::Output {
-   LL|      1|        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-   LL|      1|        use std::hint::unreachable_unchecked;
-   LL|      1|        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-   LL|      1|            |_| unsafe { unreachable_unchecked() }, // clone
-                              ^0
-   LL|      1|            |_| unsafe { unreachable_unchecked() }, // wake
-                              ^0
-   LL|      1|            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-                              ^0
-   LL|      1|            |_| (),
-   LL|      1|        );
-   LL|      1|        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
-   LL|      1|        let mut context = Context::from_waker(&waker);
+   LL|       |    use core::future::Future;
+   LL|       |    use core::pin::pin;
+   LL|       |    use core::task::{Context, Poll, Waker};
+   LL|       |
+   LL|       |    #[coverage(off)]
+   LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+   LL|       |        let mut future = pin!(future);
+   LL|       |        let waker = Waker::noop();
+   LL|       |        let mut context = Context::from_waker(&waker);
    LL|       |
    LL|       |        loop {
-   LL|      1|            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
-   LL|      1|                break val;
-   LL|      0|            }
+   LL|       |            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+   LL|       |                break val;
+   LL|       |            }
    LL|       |        }
-   LL|      1|    }
+   LL|       |    }
    LL|       |}
 
diff --git a/tests/coverage/async.rs b/tests/coverage/async.rs
index efd9e62d64e..abc9e5f7f64 100644
--- a/tests/coverage/async.rs
+++ b/tests/coverage/async.rs
@@ -1,6 +1,8 @@
+#![feature(coverage_attribute)]
+#![feature(noop_waker)]
 #![allow(unused_assignments, dead_code)]
-
-// compile-flags: --edition=2018 -C opt-level=1
+// edition: 2018
+// compile-flags: -Copt-level=1
 
 async fn c(x: u8) -> u8 {
     if x == 8 {
@@ -101,22 +103,14 @@ fn main() {
 }
 
 mod executor {
-    use core::{
-        future::Future,
-        pin::Pin,
-        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
-    };
+    use core::future::Future;
+    use core::pin::pin;
+    use core::task::{Context, Poll, Waker};
 
+    #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
-        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-        use std::hint::unreachable_unchecked;
-        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-            |_| unsafe { unreachable_unchecked() }, // clone
-            |_| unsafe { unreachable_unchecked() }, // wake
-            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-            |_| (),
-        );
-        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+        let mut future = pin!(future);
+        let waker = Waker::noop();
         let mut context = Context::from_waker(&waker);
 
         loop {
diff --git a/tests/coverage/async2.cov-map b/tests/coverage/async2.cov-map
index 23f26ee4e5f..b120e28c464 100644
--- a/tests/coverage/async2.cov-map
+++ b/tests/coverage/async2.cov-map
@@ -1,20 +1,20 @@
 Function name: async2::async_func
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 01, 00, 17]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 0d, 01, 00, 17]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 11, 1) to (start + 0, 23)
+- Code(Counter(0)) at (prev + 13, 1) to (start + 0, 23)
 
 Function name: async2::async_func::{closure#0}
-Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 0b, 17, 03, 09, 05, 03, 0a, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02]
+Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 0d, 17, 03, 09, 05, 03, 0a, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Counter(0), rhs = Counter(1)
 - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
 Number of file 0 mappings: 4
-- Code(Counter(0)) at (prev + 11, 23) to (start + 3, 9)
+- Code(Counter(0)) at (prev + 13, 23) to (start + 3, 9)
 - Code(Counter(1)) at (prev + 3, 10) to (start + 2, 6)
 - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7)
     = (c0 - c1)
@@ -22,109 +22,37 @@ Number of file 0 mappings: 4
     = (c1 + (c0 - c1))
 
 Function name: async2::async_func_just_println
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 01, 00, 24]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 15, 01, 00, 24]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 19, 1) to (start + 0, 36)
+- Code(Counter(0)) at (prev + 21, 1) to (start + 0, 36)
 
 Function name: async2::async_func_just_println::{closure#0}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 24, 02, 02]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 15, 24, 02, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 19, 36) to (start + 2, 2)
-
-Function name: async2::executor::block_on::<async2::async_func::{closure#0}>
-Raw bytes (40): 0x[01, 01, 03, 0b, 05, 01, 05, 01, 05, 06, 01, 27, 05, 0a, 36, 02, 0d, 20, 00, 23, 0b, 00, 27, 00, 49, 02, 01, 17, 00, 1a, 05, 01, 0e, 00, 0f, 02, 02, 05, 00, 06]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 3
-- expression 0 operands: lhs = Expression(2, Add), rhs = Counter(1)
-- expression 1 operands: lhs = Counter(0), rhs = Counter(1)
-- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
-Number of file 0 mappings: 6
-- Code(Counter(0)) at (prev + 39, 5) to (start + 10, 54)
-- Code(Expression(0, Sub)) at (prev + 13, 32) to (start + 0, 35)
-    = ((c0 + c1) - c1)
-- Code(Expression(2, Add)) at (prev + 0, 39) to (start + 0, 73)
-    = (c0 + c1)
-- Code(Expression(0, Sub)) at (prev + 1, 23) to (start + 0, 26)
-    = ((c0 + c1) - c1)
-- Code(Counter(1)) at (prev + 1, 14) to (start + 0, 15)
-- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
-    = ((c0 + c1) - c1)
-
-Function name: async2::executor::block_on::<async2::async_func_just_println::{closure#0}>
-Raw bytes (40): 0x[01, 01, 03, 0b, 05, 01, 05, 01, 05, 06, 01, 27, 05, 0a, 36, 02, 0d, 20, 00, 23, 0b, 00, 27, 00, 49, 02, 01, 17, 00, 1a, 05, 01, 0e, 00, 0f, 02, 02, 05, 00, 06]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 3
-- expression 0 operands: lhs = Expression(2, Add), rhs = Counter(1)
-- expression 1 operands: lhs = Counter(0), rhs = Counter(1)
-- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
-Number of file 0 mappings: 6
-- Code(Counter(0)) at (prev + 39, 5) to (start + 10, 54)
-- Code(Expression(0, Sub)) at (prev + 13, 32) to (start + 0, 35)
-    = ((c0 + c1) - c1)
-- Code(Expression(2, Add)) at (prev + 0, 39) to (start + 0, 73)
-    = (c0 + c1)
-- Code(Expression(0, Sub)) at (prev + 1, 23) to (start + 0, 26)
-    = ((c0 + c1) - c1)
-- Code(Counter(1)) at (prev + 1, 14) to (start + 0, 15)
-- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6)
-    = ((c0 + c1) - c1)
-
-Function name: async2::executor::block_on::VTABLE::{closure#0}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 2b, 11, 00, 31]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 43, 17) to (start + 0, 49)
-
-Function name: async2::executor::block_on::VTABLE::{closure#1}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 2c, 11, 00, 31]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 44, 17) to (start + 0, 49)
-
-Function name: async2::executor::block_on::VTABLE::{closure#2}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 2d, 11, 00, 31]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 45, 17) to (start + 0, 49)
-
-Function name: async2::executor::block_on::VTABLE::{closure#3}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 2e, 11, 00, 13]
-Number of files: 1
-- file 0 => global file 1
-Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 46, 17) to (start + 0, 19)
+- Code(Counter(0)) at (prev + 21, 36) to (start + 2, 2)
 
 Function name: async2::main
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 17, 01, 07, 02]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 19, 01, 07, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 23, 1) to (start + 7, 2)
+- Code(Counter(0)) at (prev + 25, 1) to (start + 7, 2)
 
 Function name: async2::non_async_func
-Raw bytes (26): 0x[01, 01, 01, 05, 00, 04, 01, 03, 01, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 03, 01, 01, 00, 02]
+Raw bytes (26): 0x[01, 01, 01, 05, 00, 04, 01, 05, 01, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 03, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 1
 - expression 0 operands: lhs = Counter(1), rhs = Zero
 Number of file 0 mappings: 4
-- Code(Counter(0)) at (prev + 3, 1) to (start + 3, 9)
+- Code(Counter(0)) at (prev + 5, 1) to (start + 3, 9)
 - Code(Counter(1)) at (prev + 3, 10) to (start + 2, 6)
 - Code(Zero) at (prev + 2, 6) to (start + 0, 7)
 - Code(Expression(0, Add)) at (prev + 1, 1) to (start + 0, 2)
diff --git a/tests/coverage/async2.coverage b/tests/coverage/async2.coverage
index fcb0a3aed64..acd83de9493 100644
--- a/tests/coverage/async2.coverage
+++ b/tests/coverage/async2.coverage
@@ -1,4 +1,6 @@
-   LL|       |// compile-flags: --edition=2018
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |#![feature(noop_waker)]
+   LL|       |// edition: 2018
    LL|       |
    LL|      1|fn non_async_func() {
    LL|      1|    println!("non_async_func was covered");
@@ -32,73 +34,21 @@
    LL|      1|}
    LL|       |
    LL|       |mod executor {
-   LL|       |    use core::{
-   LL|       |        future::Future,
-   LL|       |        pin::Pin,
-   LL|       |        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
-   LL|       |    };
+   LL|       |    use core::future::Future;
+   LL|       |    use core::pin::pin;
+   LL|       |    use core::task::{Context, Poll, Waker};
    LL|       |
-   LL|      2|    pub fn block_on<F: Future>(mut future: F) -> F::Output {
-   LL|      2|        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-   LL|      2|        use std::hint::unreachable_unchecked;
-   LL|      2|        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-   LL|      2|            |_| unsafe { unreachable_unchecked() }, // clone
-                              ^0
-   LL|      2|            |_| unsafe { unreachable_unchecked() }, // wake
-                              ^0
-   LL|      2|            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-                              ^0
-   LL|      2|            |_| (),
-   LL|      2|        );
-   LL|      2|        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
-   LL|      2|        let mut context = Context::from_waker(&waker);
+   LL|       |    #[coverage(off)]
+   LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+   LL|       |        let mut future = pin!(future);
+   LL|       |        let waker = Waker::noop();
+   LL|       |        let mut context = Context::from_waker(&waker);
    LL|       |
    LL|       |        loop {
-   LL|      2|            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
-   LL|      2|                break val;
-   LL|      0|            }
+   LL|       |            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+   LL|       |                break val;
+   LL|       |            }
    LL|       |        }
-   LL|      2|    }
-  ------------------
-  | async2::executor::block_on::<async2::async_func::{closure#0}>:
-  |   LL|      1|    pub fn block_on<F: Future>(mut future: F) -> F::Output {
-  |   LL|      1|        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-  |   LL|      1|        use std::hint::unreachable_unchecked;
-  |   LL|      1|        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-  |   LL|      1|            |_| unsafe { unreachable_unchecked() }, // clone
-  |   LL|      1|            |_| unsafe { unreachable_unchecked() }, // wake
-  |   LL|      1|            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-  |   LL|      1|            |_| (),
-  |   LL|      1|        );
-  |   LL|      1|        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
-  |   LL|      1|        let mut context = Context::from_waker(&waker);
-  |   LL|       |
-  |   LL|       |        loop {
-  |   LL|      1|            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
-  |   LL|      1|                break val;
-  |   LL|      0|            }
-  |   LL|       |        }
-  |   LL|      1|    }
-  ------------------
-  | async2::executor::block_on::<async2::async_func_just_println::{closure#0}>:
-  |   LL|      1|    pub fn block_on<F: Future>(mut future: F) -> F::Output {
-  |   LL|      1|        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-  |   LL|      1|        use std::hint::unreachable_unchecked;
-  |   LL|      1|        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-  |   LL|      1|            |_| unsafe { unreachable_unchecked() }, // clone
-  |   LL|      1|            |_| unsafe { unreachable_unchecked() }, // wake
-  |   LL|      1|            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-  |   LL|      1|            |_| (),
-  |   LL|      1|        );
-  |   LL|      1|        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
-  |   LL|      1|        let mut context = Context::from_waker(&waker);
-  |   LL|       |
-  |   LL|       |        loop {
-  |   LL|      1|            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
-  |   LL|      1|                break val;
-  |   LL|      0|            }
-  |   LL|       |        }
-  |   LL|      1|    }
-  ------------------
+   LL|       |    }
    LL|       |}
 
diff --git a/tests/coverage/async2.rs b/tests/coverage/async2.rs
index 2884ff297af..393573f7dc9 100644
--- a/tests/coverage/async2.rs
+++ b/tests/coverage/async2.rs
@@ -1,4 +1,6 @@
-// compile-flags: --edition=2018
+#![feature(coverage_attribute)]
+#![feature(noop_waker)]
+// edition: 2018
 
 fn non_async_func() {
     println!("non_async_func was covered");
@@ -30,22 +32,14 @@ fn main() {
 }
 
 mod executor {
-    use core::{
-        future::Future,
-        pin::Pin,
-        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
-    };
+    use core::future::Future;
+    use core::pin::pin;
+    use core::task::{Context, Poll, Waker};
 
+    #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
-        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-        use std::hint::unreachable_unchecked;
-        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-            |_| unsafe { unreachable_unchecked() }, // clone
-            |_| unsafe { unreachable_unchecked() }, // wake
-            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-            |_| (),
-        );
-        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+        let mut future = pin!(future);
+        let waker = Waker::noop();
         let mut context = Context::from_waker(&waker);
 
         loop {
diff --git a/tests/coverage/closure_macro_async.cov-map b/tests/coverage/closure_macro_async.cov-map
index 7f8666948d9..14b1525ca0e 100644
--- a/tests/coverage/closure_macro_async.cov-map
+++ b/tests/coverage/closure_macro_async.cov-map
@@ -1,28 +1,28 @@
 Function name: closure_macro_async::load_configuration_files
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 1d, 01, 02, 02]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 1e, 01, 02, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 29, 1) to (start + 2, 2)
+- Code(Counter(0)) at (prev + 30, 1) to (start + 2, 2)
 
 Function name: closure_macro_async::test
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 21, 01, 00, 2b]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 22, 01, 00, 2b]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 33, 1) to (start + 0, 43)
+- Code(Counter(0)) at (prev + 34, 1) to (start + 0, 43)
 
 Function name: closure_macro_async::test::{closure#0}
-Raw bytes (43): 0x[01, 01, 02, 01, 05, 05, 02, 07, 01, 21, 2b, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 12, 00, 13, 02, 00, 12, 00, 13, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02]
+Raw bytes (43): 0x[01, 01, 02, 01, 05, 05, 02, 07, 01, 22, 2b, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 12, 00, 13, 02, 00, 12, 00, 13, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Counter(0), rhs = Counter(1)
 - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
 Number of file 0 mappings: 7
-- Code(Counter(0)) at (prev + 33, 43) to (start + 1, 33)
+- Code(Counter(0)) at (prev + 34, 43) to (start + 1, 33)
 - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15)
     = (c0 - c1)
 - Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19)
@@ -35,10 +35,10 @@ Number of file 0 mappings: 7
     = (c1 + (c0 - c1))
 
 Function name: closure_macro_async::test::{closure#0}::{closure#0}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 23, 12, 00, 54]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 24, 12, 00, 54]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 35, 18) to (start + 0, 84)
+- Code(Counter(0)) at (prev + 36, 18) to (start + 0, 84)
 
diff --git a/tests/coverage/closure_macro_async.coverage b/tests/coverage/closure_macro_async.coverage
index 74247f1bc6f..2c9bd4ac97a 100644
--- a/tests/coverage/closure_macro_async.coverage
+++ b/tests/coverage/closure_macro_async.coverage
@@ -1,5 +1,6 @@
-   LL|       |// compile-flags: --edition=2018
    LL|       |#![feature(coverage_attribute)]
+   LL|       |#![feature(noop_waker)]
+   LL|       |// edition: 2018
    LL|       |
    LL|       |macro_rules! bail {
    LL|       |    ($msg:literal $(,)?) => {
@@ -46,27 +47,14 @@
    LL|       |}
    LL|       |
    LL|       |mod executor {
-   LL|       |    use core::{
-   LL|       |        future::Future,
-   LL|       |        pin::Pin,
-   LL|       |        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
-   LL|       |    };
+   LL|       |    use core::future::Future;
+   LL|       |    use core::pin::pin;
+   LL|       |    use core::task::{Context, Poll, Waker};
    LL|       |
    LL|       |    #[coverage(off)]
    LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
-   LL|       |        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-   LL|       |        use std::hint::unreachable_unchecked;
-   LL|       |        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-   LL|       |            #[coverage(off)]
-   LL|       |            |_| unsafe { unreachable_unchecked() }, // clone
-   LL|       |            #[coverage(off)]
-   LL|       |            |_| unsafe { unreachable_unchecked() }, // wake
-   LL|       |            #[coverage(off)]
-   LL|       |            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-   LL|       |            #[coverage(off)]
-   LL|       |            |_| (),
-   LL|       |        );
-   LL|       |        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+   LL|       |        let mut future = pin!(future);
+   LL|       |        let waker = Waker::noop();
    LL|       |        let mut context = Context::from_waker(&waker);
    LL|       |
    LL|       |        loop {
diff --git a/tests/coverage/closure_macro_async.rs b/tests/coverage/closure_macro_async.rs
index b4275599e59..a7f0cabb4c2 100644
--- a/tests/coverage/closure_macro_async.rs
+++ b/tests/coverage/closure_macro_async.rs
@@ -1,5 +1,6 @@
-// compile-flags: --edition=2018
 #![feature(coverage_attribute)]
+#![feature(noop_waker)]
+// edition: 2018
 
 macro_rules! bail {
     ($msg:literal $(,)?) => {
@@ -45,27 +46,14 @@ fn main() {
 }
 
 mod executor {
-    use core::{
-        future::Future,
-        pin::Pin,
-        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
-    };
+    use core::future::Future;
+    use core::pin::pin;
+    use core::task::{Context, Poll, Waker};
 
     #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
-        let mut future = unsafe { Pin::new_unchecked(&mut future) };
-        use std::hint::unreachable_unchecked;
-        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-            #[coverage(off)]
-            |_| unsafe { unreachable_unchecked() }, // clone
-            #[coverage(off)]
-            |_| unsafe { unreachable_unchecked() }, // wake
-            #[coverage(off)]
-            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-            #[coverage(off)]
-            |_| (),
-        );
-        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+        let mut future = pin!(future);
+        let waker = Waker::noop();
         let mut context = Context::from_waker(&waker);
 
         loop {
diff --git a/tests/coverage/no_spans_if_not.cov-map b/tests/coverage/no_spans_if_not.cov-map
new file mode 100644
index 00000000000..5277267ec1b
--- /dev/null
+++ b/tests/coverage/no_spans_if_not.cov-map
@@ -0,0 +1,8 @@
+Function name: no_spans_if_not::main
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 01, 02, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 11, 1) to (start + 2, 2)
+
diff --git a/tests/coverage/no_spans_if_not.coverage b/tests/coverage/no_spans_if_not.coverage
new file mode 100644
index 00000000000..1b6bbc75a04
--- /dev/null
+++ b/tests/coverage/no_spans_if_not.coverage
@@ -0,0 +1,30 @@
+   LL|       |// edition: 2021
+   LL|       |
+   LL|       |// If the span extractor can't find any relevant spans for a function,
+   LL|       |// but the function contains coverage span-marker statements (e.g. inserted
+   LL|       |// for `if !`), coverage codegen may think that it is instrumented and
+   LL|       |// consequently complain that it has no spans.
+   LL|       |//
+   LL|       |// Regression test for <https://github.com/rust-lang/rust/issues/118850>,
+   LL|       |// "A used function should have had coverage mapping data but did not".
+   LL|       |
+   LL|      1|fn main() {
+   LL|      1|    affected_function();
+   LL|      1|}
+   LL|       |
+   LL|       |macro_rules! macro_that_defines_a_function {
+   LL|       |    (fn $name:ident () $body:tt) => {
+   LL|       |        fn $name () $body
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|       |macro_that_defines_a_function! {
+   LL|       |    fn affected_function() {
+   LL|       |        if !false {
+   LL|       |            ()
+   LL|       |        } else {
+   LL|       |            ()
+   LL|       |        }
+   LL|       |    }
+   LL|       |}
+
diff --git a/tests/coverage/no_spans_if_not.rs b/tests/coverage/no_spans_if_not.rs
new file mode 100644
index 00000000000..2bbdc11cd5e
--- /dev/null
+++ b/tests/coverage/no_spans_if_not.rs
@@ -0,0 +1,29 @@
+// edition: 2021
+
+// If the span extractor can't find any relevant spans for a function,
+// but the function contains coverage span-marker statements (e.g. inserted
+// for `if !`), coverage codegen may think that it is instrumented and
+// consequently complain that it has no spans.
+//
+// Regression test for <https://github.com/rust-lang/rust/issues/118850>,
+// "A used function should have had coverage mapping data but did not".
+
+fn main() {
+    affected_function();
+}
+
+macro_rules! macro_that_defines_a_function {
+    (fn $name:ident () $body:tt) => {
+        fn $name () $body
+    }
+}
+
+macro_that_defines_a_function! {
+    fn affected_function() {
+        if !false {
+            ()
+        } else {
+            ()
+        }
+    }
+}
diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff
new file mode 100644
index 00000000000..4b1e3379114
--- /dev/null
+++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff
@@ -0,0 +1,104 @@
+- // MIR for `constant_index_overflow` before GVN
++ // MIR for `constant_index_overflow` after GVN
+  
+  fn constant_index_overflow(_1: &[T]) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: usize;
+      let mut _4: bool;
+      let mut _5: usize;
+      let mut _6: usize;
+      let mut _7: &[T];
+      let _8: usize;
+      let mut _9: usize;
+      let mut _10: bool;
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: T;
+      scope 1 {
+          debug a => _2;
+          let _3: T;
+          scope 2 {
+              debug b => _3;
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);
+-         _2 = const _ as usize (IntToInt);
++         nop;
++         _2 = const usize::MAX;
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+-         _5 = _2;
++         _5 = const usize::MAX;
+          StorageLive(_6);
+          StorageLive(_7);
+          _7 = &(*_1);
+          _6 = core::slice::<impl [T]>::len(move _7) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_7);
+-         _4 = Lt(move _5, move _6);
++         _4 = Lt(const usize::MAX, move _6);
+          switchInt(move _4) -> [0: bb4, otherwise: bb2];
+      }
+  
+      bb2: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_8);
+-         _8 = _2;
++         _8 = const usize::MAX;
+          _9 = Len((*_1));
+-         _10 = Lt(_8, _9);
+-         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable];
++         _10 = Lt(const usize::MAX, _9);
++         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const usize::MAX) -> [success: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+-         _3 = (*_1)[_8];
++         _3 = (*_1)[_2];
+          StorageDead(_8);
+          goto -> bb6;
+      }
+  
+      bb4: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_11);
+          _11 = const 0_usize;
+          _12 = Len((*_1));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind unreachable];
++         _13 = Lt(const 0_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 0_usize) -> [success: bb5, unwind unreachable];
+      }
+  
+      bb5: {
+-         _3 = (*_1)[_11];
++         _3 = (*_1)[0 of 1];
+          StorageDead(_11);
+          goto -> bb6;
+      }
+  
+      bb6: {
+          StorageDead(_4);
+          StorageLive(_14);
+          _14 = _3;
+          _0 = opaque::<T>(move _14) -> [return: bb7, unwind unreachable];
+      }
+  
+      bb7: {
+          StorageDead(_14);
+          StorageDead(_3);
+-         StorageDead(_2);
++         nop;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff
new file mode 100644
index 00000000000..8abcd7e9387
--- /dev/null
+++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff
@@ -0,0 +1,104 @@
+- // MIR for `constant_index_overflow` before GVN
++ // MIR for `constant_index_overflow` after GVN
+  
+  fn constant_index_overflow(_1: &[T]) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: usize;
+      let mut _4: bool;
+      let mut _5: usize;
+      let mut _6: usize;
+      let mut _7: &[T];
+      let _8: usize;
+      let mut _9: usize;
+      let mut _10: bool;
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: T;
+      scope 1 {
+          debug a => _2;
+          let _3: T;
+          scope 2 {
+              debug b => _3;
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);
+-         _2 = const _ as usize (IntToInt);
++         nop;
++         _2 = const usize::MAX;
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+-         _5 = _2;
++         _5 = const usize::MAX;
+          StorageLive(_6);
+          StorageLive(_7);
+          _7 = &(*_1);
+          _6 = core::slice::<impl [T]>::len(move _7) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_7);
+-         _4 = Lt(move _5, move _6);
++         _4 = Lt(const usize::MAX, move _6);
+          switchInt(move _4) -> [0: bb4, otherwise: bb2];
+      }
+  
+      bb2: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_8);
+-         _8 = _2;
++         _8 = const usize::MAX;
+          _9 = Len((*_1));
+-         _10 = Lt(_8, _9);
+-         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue];
++         _10 = Lt(const usize::MAX, _9);
++         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const usize::MAX) -> [success: bb3, unwind continue];
+      }
+  
+      bb3: {
+-         _3 = (*_1)[_8];
++         _3 = (*_1)[_2];
+          StorageDead(_8);
+          goto -> bb6;
+      }
+  
+      bb4: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_11);
+          _11 = const 0_usize;
+          _12 = Len((*_1));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind continue];
++         _13 = Lt(const 0_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 0_usize) -> [success: bb5, unwind continue];
+      }
+  
+      bb5: {
+-         _3 = (*_1)[_11];
++         _3 = (*_1)[0 of 1];
+          StorageDead(_11);
+          goto -> bb6;
+      }
+  
+      bb6: {
+          StorageDead(_4);
+          StorageLive(_14);
+          _14 = _3;
+          _0 = opaque::<T>(move _14) -> [return: bb7, unwind continue];
+      }
+  
+      bb7: {
+          StorageDead(_14);
+          StorageDead(_3);
+-         StorageDead(_2);
++         nop;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 6e082acdbd3..db131f7f97d 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -609,6 +609,22 @@ fn indirect_static() {
     })
 }
 
+/// Verify that having constant index `u64::MAX` does not yield to an overflow in rustc.
+fn constant_index_overflow<T: Copy>(x: &[T]) {
+    // CHECK-LABEL: fn constant_index_overflow(
+    // CHECK: debug a => [[a:_.*]];
+    // CHECK: debug b => [[b:_.*]];
+    // CHECK: [[a]] = const usize::MAX;
+    // CHECK-NOT: = (*_1)[{{.*}} of 0];
+    // CHECK: [[b]] = (*_1)[[[a]]];
+    // CHECK-NOT: = (*_1)[{{.*}} of 0];
+    // CHECK: [[b]] = (*_1)[0 of 1];
+    // CHECK-NOT: = (*_1)[{{.*}} of 0];
+    let a = u64::MAX as usize;
+    let b = if a < x.len() { x[a] } else { x[0] };
+    opaque(b)
+}
+
 fn main() {
     subexpression_elimination(2, 4, 5);
     wrap_unwrap(5);
@@ -627,6 +643,7 @@ fn main() {
     repeat();
     fn_pointers();
     indirect_static();
+    constant_index_overflow(&[5, 3]);
 }
 
 #[inline(never)]
@@ -653,3 +670,4 @@ fn identity<T>(x: T) -> T {
 // EMIT_MIR gvn.repeat.GVN.diff
 // EMIT_MIR gvn.fn_pointers.GVN.diff
 // EMIT_MIR gvn.indirect_static.GVN.diff
+// EMIT_MIR gvn.constant_index_overflow.GVN.diff
diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml
new file mode 100644
index 00000000000..53f96ff086e
--- /dev/null
+++ b/tests/rustdoc-gui/globals.goml
@@ -0,0 +1,24 @@
+// Make sure search stores its data in `window`
+// It needs to use a global to avoid racing on search-index.js and search.js
+// https://github.com/rust-lang/rust/pull/118961
+
+// URL query
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds"
+wait-for: "#search-tabs"
+assert-window-property-false: {"searchIndex": null}
+assert-window-property: {"srcIndex": null}
+
+// Form input
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "Foo")
+press-key: 'Enter'
+wait-for: "#search-tabs"
+assert-window-property-false: {"searchIndex": null}
+assert-window-property: {"srcIndex": null}
+
+// source sidebar
+go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+click: "#src-sidebar-toggle"
+wait-for: "#src-sidebar details"
+assert-window-property-false: {"srcIndex": null}
+assert-window-property: {"searchIndex": null}
diff --git a/tests/rustdoc-gui/hide-mobile-topbar.goml b/tests/rustdoc-gui/hide-mobile-topbar.goml
new file mode 100644
index 00000000000..46eb8acfe8c
--- /dev/null
+++ b/tests/rustdoc-gui/hide-mobile-topbar.goml
@@ -0,0 +1,20 @@
+// Checks sidebar resizing stays synced with the setting
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+set-window-size: (400, 600)
+
+// Verify that the "hide" option is unchecked
+click: "#settings-menu"
+wait-for: "#settings"
+assert-css: ("#settings", {"display": "block"})
+assert-property: ("#hide-sidebar", {"checked": "false"})
+assert-css: (".mobile-topbar", {"display": "flex"})
+
+// Toggle it
+click: "#hide-sidebar"
+assert-property: ("#hide-sidebar", {"checked": "true"})
+assert-css: (".mobile-topbar", {"display": "none"})
+
+// Toggle it again
+click: "#hide-sidebar"
+assert-property: ("#hide-sidebar", {"checked": "false"})
+assert-css: (".mobile-topbar", {"display": "flex"})
diff --git a/tests/rustdoc-gui/links-color.goml b/tests/rustdoc-gui/links-color.goml
index 0789d785f58..d88ebfb40d7 100644
--- a/tests/rustdoc-gui/links-color.goml
+++ b/tests/rustdoc-gui/links-color.goml
@@ -26,12 +26,12 @@ define-function: (
         assert-css: (".item-table .keyword", {"color": |keyword|}, ALL)
         // Checking sidebar elements.
         assert-css: (
-            ".sidebar-elems a:not(.current)",
+            ".sidebar-elems li:not(.current) a",
             {"color": |sidebar|, "background-color": "rgba(0, 0, 0, 0)", "font-weight": "400"},
             ALL,
         )
         assert-css: (
-            ".sidebar-elems a.current",
+            ".sidebar-elems li.current a",
             {
                 "color": |sidebar_current|,
                 "background-color": |sidebar_current_background|,
diff --git a/tests/rustdoc-gui/sidebar-links-color.goml b/tests/rustdoc-gui/sidebar-links-color.goml
index 079d582a567..774fbcac1e2 100644
--- a/tests/rustdoc-gui/sidebar-links-color.goml
+++ b/tests/rustdoc-gui/sidebar-links-color.goml
@@ -17,10 +17,10 @@ define-function: (
         reload:
         // Struct
         assert-css: (
-            ".sidebar .block.struct a:not(.current)",
+            ".sidebar .block.struct li:not(.current) a",
             {"color": |struct|, "background-color": "rgba(0, 0, 0, 0)"},
         )
-        move-cursor-to: ".sidebar .block.struct a:not(.current)"
+        move-cursor-to: ".sidebar .block.struct li:not(.current) a"
         assert-css: (
             ".sidebar .block.struct a:hover",
             {"color": |struct_hover|, "background-color": |struct_hover_background|},
diff --git a/tests/rustdoc-gui/sidebar-resize-setting.goml b/tests/rustdoc-gui/sidebar-resize-setting.goml
new file mode 100644
index 00000000000..2fdb2faa864
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-resize-setting.goml
@@ -0,0 +1,23 @@
+// Checks sidebar resizing stays synced with the setting
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+show-text: true
+
+// Verify that the "hide" option is unchecked
+click: "#settings-menu"
+wait-for: "#settings"
+assert-css: ("#settings", {"display": "block"})
+assert-property: ("#hide-sidebar", {"checked": "false"})
+press-key: "Escape"
+wait-for-css: ("#settings", {"display": "none"})
+
+drag-and-drop: ((205, 100), (5, 100))
+assert-css: (".sidebar", {"display": "none"})
+
+// Verify that the "hide" option is checked
+focus: "#settings-menu a"
+press-key: "Enter"
+wait-for-css: ("#settings", {"display": "block"})
+assert-property: ("#hide-sidebar", {"checked": "true"})
+click: "#hide-sidebar"
+wait-for-css: (".sidebar", {"display": "block"})
diff --git a/tests/rustdoc-gui/sidebar-resize-window.goml b/tests/rustdoc-gui/sidebar-resize-window.goml
new file mode 100644
index 00000000000..fb6baafda71
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-resize-window.goml
@@ -0,0 +1,37 @@
+// Checks sidebar resizing
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 200}, [NEAR])
+
+// resize past maximum (don't grow past 500)
+drag-and-drop: ((205, 100), (600, 100))
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// make the window small enough that the sidebar has to shrink
+set-window-size: (750, 600)
+wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
+
+// grow the window again to make the sidebar bigger
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// make the window small enough that the sidebar has to shrink
+set-window-size: (750, 600)
+wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
+assert-local-storage: {"rustdoc-desktop-sidebar-width": "350"}
+set-window-size: (400, 600)
+wait-for-css: (".sidebar", {"display": "block", "left": "-1000px"})
+assert-local-storage: {"rustdoc-desktop-sidebar-width": "350"}
+
+// grow the window again to make the sidebar bigger
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// shrink back down again, then reload the page
+// the "desired size" is a bit of remembered implicit state,
+// and rustdoc tries to minimize things like this
+set-window-size: (800, 600)
+wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])
+reload:
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])
diff --git a/tests/rustdoc-gui/sidebar-resize.goml b/tests/rustdoc-gui/sidebar-resize.goml
new file mode 100644
index 00000000000..543d5d390c7
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-resize.goml
@@ -0,0 +1,28 @@
+// Checks sidebar resizing
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+show-text: true
+// normal resizing
+drag-and-drop: ((205, 100), (185, 100))
+assert-property: (".sidebar", {"clientWidth": "182"})
+// resize past maximum (don't grow past 500)
+drag-and-drop: ((185, 100), (600, 100))
+assert-property: (".sidebar", {"clientWidth": "500"})
+// resize past minimum (hide sidebar)
+drag-and-drop: ((501, 100), (5, 100))
+assert-property: (".sidebar", {"clientWidth": "0"})
+assert-css: (".sidebar", {"display": "none"})
+assert-local-storage: {"rustdoc-hide-sidebar": "true"}
+set-local-storage: {"rustdoc-hide-sidebar": "false"}
+
+// Now same thing, but for source code
+go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+assert-property: (".sidebar", {"clientWidth": "49"})
+drag-and-drop: ((52, 100), (185, 100))
+assert-property: (".sidebar", {"clientWidth": "181"})
+drag-and-drop: ((185, 100), (600, 100))
+assert-property: (".sidebar", {"clientWidth": "499"})
+drag-and-drop: ((500, 100), (5, 100))
+// instead of hiding the sidebar entirely, this
+// will switch to the toggle mode
+assert-property: (".sidebar", {"clientWidth": "49"})
diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml
index 0d72e670cf4..9fc1409e86f 100644
--- a/tests/rustdoc-gui/sidebar-source-code.goml
+++ b/tests/rustdoc-gui/sidebar-source-code.goml
@@ -48,6 +48,7 @@ call-function: (
 
 // Next, desktop mode layout.
 set-window-size: (1100, 800)
+wait-for: "#src-sidebar-toggle"
 // We check that the sidebar isn't expanded and has the expected width.
 assert-css: ("nav.sidebar", {"width": "50px"})
 // We now click on the button to expand the sidebar.
@@ -58,7 +59,7 @@ assert-css: (".src-sidebar-expanded nav.sidebar a", {"font-size": "14px"})
 // We collapse the sidebar.
 click: (10, 10)
 // We ensure that the class has been removed.
-wait-for: "html:not(.expanded)"
+wait-for: "html:not(.src-sidebar-expanded)"
 assert: "nav.sidebar"
 
 // Checking that only the path to the current file is "open".
diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml
index eff66d803d2..82b4f2e9429 100644
--- a/tests/rustdoc-gui/sidebar.goml
+++ b/tests/rustdoc-gui/sidebar.goml
@@ -57,7 +57,7 @@ assert-count: (".sidebar h2", 1)
 assert-text: ("#all-types", "All Items")
 assert-css: ("#all-types", {"color": "#356da4"})
 // We check that we have the crates list and that the "current" on is "test_docs".
-assert-text: (".sidebar-elems ul.crate > li > a.current", "test_docs")
+assert-text: (".sidebar-elems ul.crate > li.current > a", "test_docs")
 // And we're also supposed to have the list of items in the current module.
 assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Re-exports")
 assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Modules")
@@ -98,7 +98,7 @@ assert-property: (".sidebar", {"clientWidth": "200"})
 assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
 assert-count: (".sidebar .location", 0)
 // We check that we have the crates list and that the "current" on is now "lib2".
-assert-text: (".sidebar-elems ul.crate > li > a.current", "lib2")
+assert-text: (".sidebar-elems ul.crate > li.current > a", "lib2")
 // We now go to the "foobar" function page.
 assert-text: (".sidebar-elems > section ul.block > li:nth-child(1)", "Modules")
 assert-text: (".sidebar-elems > section ul.block > li:nth-child(2)", "Structs")
diff --git a/tests/rustdoc-gui/src/theme_css/custom-theme.css b/tests/rustdoc-gui/src/theme_css/custom-theme.css
index 260ef87f6ea..49227d9ea11 100644
--- a/tests/rustdoc-gui/src/theme_css/custom-theme.css
+++ b/tests/rustdoc-gui/src/theme_css/custom-theme.css
@@ -96,4 +96,6 @@
 	--scrape-example-help-hover-color: #000;
 	--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
 	--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+	--sidebar-resizer-hover: hsl(207, 90%, 66%);
+	--sidebar-resizer-active: hsl(207, 90%, 54%);
 }
diff --git a/tests/rustdoc-js/substring.js b/tests/rustdoc-js/substring.js
index 96efa992bb6..b791ac4bfd1 100644
--- a/tests/rustdoc-js/substring.js
+++ b/tests/rustdoc-js/substring.js
@@ -1,7 +1,15 @@
-const EXPECTED = {
-    'query': 'waker_from',
-    'others': [
-        { 'path': 'substring::SuperWaker', 'name': 'local_waker_from_nonlocal' },
-        { 'path': 'substring::SuperWakerTask', 'name': 'local_waker_from_nonlocal' },
-    ],
-};
+const EXPECTED = [
+    {
+        'query': 'waker_from',
+        'others': [
+            { 'path': 'substring::SuperWaker', 'name': 'local_waker_from_nonlocal' },
+            { 'path': 'substring::SuperWakerTask', 'name': 'local_waker_from_nonlocal' },
+        ],
+    },
+    {
+        'query': 'my',
+        'others': [
+            { 'path': 'substring', 'name': 'm_y_substringmatching' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/substring.rs b/tests/rustdoc-js/substring.rs
index e729c722c79..ea88894d03d 100644
--- a/tests/rustdoc-js/substring.rs
+++ b/tests/rustdoc-js/substring.rs
@@ -19,3 +19,5 @@ impl SuperWakerTask {
     pub fn waker_non_local() {}
     pub fn from_non_local() {}
 }
+
+pub fn m_y_substringmatching() {}
diff --git a/tests/rustdoc/reexport-cfg.rs b/tests/rustdoc/reexport-cfg.rs
new file mode 100644
index 00000000000..a6179fad873
--- /dev/null
+++ b/tests/rustdoc/reexport-cfg.rs
@@ -0,0 +1,30 @@
+// This test ensures that only the re-export `cfg` will be displayed and that it won't
+// include `cfg`s from the previous chained items.
+
+#![crate_name = "foo"]
+#![feature(doc_auto_cfg, doc_cfg)]
+
+mod foo {
+    #[cfg(not(feature = "foo"))]
+    pub struct Bar;
+
+    #[doc(cfg(not(feature = "bar")))]
+    pub struct Bar2;
+}
+
+// @has 'foo/index.html'
+// @has - '//*[@class="item-name"]' 'BabarNon-lie'
+#[cfg(not(feature = "lie"))]
+pub use crate::foo::Bar as Babar;
+
+// @has - '//*[@class="item-name"]' 'Babar2Non-cake'
+#[doc(cfg(not(feature = "cake")))]
+pub use crate::foo::Bar2 as Babar2;
+
+// @has - '//*[@class="item-table"]/li' 'pub use crate::Babar as Elephant;Non-robot'
+#[cfg(not(feature = "robot"))]
+pub use crate::Babar as Elephant;
+
+// @has - '//*[@class="item-table"]/li' 'pub use crate::Babar2 as Elephant2;Non-cat'
+#[doc(cfg(not(feature = "cat")))]
+pub use crate::Babar2 as Elephant2;
diff --git a/tests/rustdoc/reexport-stability-tags-deprecated-and-portability.rs b/tests/rustdoc/reexport-stability-tags-deprecated-and-portability.rs
index a79d05904e3..c81c654a20e 100644
--- a/tests/rustdoc/reexport-stability-tags-deprecated-and-portability.rs
+++ b/tests/rustdoc/reexport-stability-tags-deprecated-and-portability.rs
@@ -27,7 +27,7 @@ pub mod mod1 {
 pub mod mod2 {
     // @has - '//code' 'pub use tag::Portability;'
     // @!has - '//span' 'Deprecated'
-    // @has - '//span' 'sync'
+    // @!has - '//span' 'sync'
     pub use tag::Portability;
 }
 
@@ -35,7 +35,7 @@ pub mod mod2 {
 pub mod mod3 {
     // @has - '//code' 'pub use tag::Both;'
     // @has - '//span' 'Deprecated'
-    // @has - '//span' 'sync'
+    // @!has - '//span' 'sync'
     pub use tag::Both;
 }
 
diff --git a/tests/rustdoc/reexport-stability-tags-unstable-and-portability.rs b/tests/rustdoc/reexport-stability-tags-unstable-and-portability.rs
index ff8a910f59f..423838e251b 100644
--- a/tests/rustdoc/reexport-stability-tags-unstable-and-portability.rs
+++ b/tests/rustdoc/reexport-stability-tags-unstable-and-portability.rs
@@ -35,7 +35,7 @@ pub mod mod1 {
 pub mod mod2 {
     // @has - '//code' 'pub use tag::Portability;'
     // @!has - '//span' 'Experimental'
-    // @has - '//span' 'sync'
+    // @!has - '//span' 'sync'
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use tag::Portability;
 }
@@ -45,7 +45,7 @@ pub mod mod2 {
 pub mod mod3 {
     // @has - '//code' 'pub use tag::Both;'
     // @has - '//span' 'Experimental'
-    // @has - '//span' 'sync'
+    // @!has - '//span' 'sync'
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use tag::Both;
 }
diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.rs b/tests/ui-fulldeps/internal-lints/diagnostics.rs
index 7f545ead152..4b006151c64 100644
--- a/tests/ui-fulldeps/internal-lints/diagnostics.rs
+++ b/tests/ui-fulldeps/internal-lints/diagnostics.rs
@@ -13,7 +13,7 @@ extern crate rustc_session;
 extern crate rustc_span;
 
 use rustc_errors::{
-    AddToDiagnostic, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, Handler,
+    AddToDiagnostic, DiagCtxt, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
     IntoDiagnostic, SubdiagnosticMessage,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -38,8 +38,8 @@ struct Note {
 pub struct UntranslatableInIntoDiagnostic;
 
 impl<'a> IntoDiagnostic<'a, ErrorGuaranteed> for UntranslatableInIntoDiagnostic {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        handler.struct_err("untranslatable diagnostic")
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        dcx.struct_err("untranslatable diagnostic")
         //~^ ERROR diagnostics should be created using translatable messages
     }
 }
@@ -47,8 +47,8 @@ impl<'a> IntoDiagnostic<'a, ErrorGuaranteed> for UntranslatableInIntoDiagnostic
 pub struct TranslatableInIntoDiagnostic;
 
 impl<'a> IntoDiagnostic<'a, ErrorGuaranteed> for TranslatableInIntoDiagnostic {
-    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        handler.struct_err(crate::fluent_generated::no_crate_example)
+    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        dcx.struct_err(crate::fluent_generated::no_crate_example)
     }
 }
 
@@ -75,11 +75,11 @@ impl AddToDiagnostic for TranslatableInAddToDiagnostic {
     }
 }
 
-pub fn make_diagnostics<'a>(handler: &'a Handler) {
-    let _diag = handler.struct_err(crate::fluent_generated::no_crate_example);
+pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) {
+    let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example);
     //~^ ERROR diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
 
-    let _diag = handler.struct_err("untranslatable diagnostic");
+    let _diag = dcx.struct_err("untranslatable diagnostic");
     //~^ ERROR diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
     //~^^ ERROR diagnostics should be created using translatable messages
 }
@@ -87,6 +87,6 @@ pub fn make_diagnostics<'a>(handler: &'a Handler) {
 // Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted.
 
 #[rustc_lint_diagnostics]
-pub fn skipped_because_of_annotation<'a>(handler: &'a Handler) {
-    let _diag = handler.struct_err("untranslatable diagnostic"); // okay!
+pub fn skipped_because_of_annotation<'a>(dcx: &'a DiagCtxt) {
+    let _diag = dcx.struct_err("untranslatable diagnostic"); // okay!
 }
diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.stderr b/tests/ui-fulldeps/internal-lints/diagnostics.stderr
index 8e0535e021b..d18db3cbbd3 100644
--- a/tests/ui-fulldeps/internal-lints/diagnostics.stderr
+++ b/tests/ui-fulldeps/internal-lints/diagnostics.stderr
@@ -1,8 +1,8 @@
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:42:17
+  --> $DIR/diagnostics.rs:42:13
    |
-LL |         handler.struct_err("untranslatable diagnostic")
-   |                 ^^^^^^^^^^
+LL |         dcx.struct_err("untranslatable diagnostic")
+   |             ^^^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/diagnostics.rs:6:9
@@ -17,10 +17,10 @@ LL |         diag.note("untranslatable diagnostic");
    |              ^^^^
 
 error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
-  --> $DIR/diagnostics.rs:79:25
+  --> $DIR/diagnostics.rs:79:21
    |
-LL |     let _diag = handler.struct_err(crate::fluent_generated::no_crate_example);
-   |                         ^^^^^^^^^^
+LL |     let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example);
+   |                     ^^^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/diagnostics.rs:7:9
@@ -29,16 +29,16 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
-  --> $DIR/diagnostics.rs:82:25
+  --> $DIR/diagnostics.rs:82:21
    |
-LL |     let _diag = handler.struct_err("untranslatable diagnostic");
-   |                         ^^^^^^^^^^
+LL |     let _diag = dcx.struct_err("untranslatable diagnostic");
+   |                     ^^^^^^^^^^
 
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:82:25
+  --> $DIR/diagnostics.rs:82:21
    |
-LL |     let _diag = handler.struct_err("untranslatable diagnostic");
-   |                         ^^^^^^^^^^
+LL |     let _diag = dcx.struct_err("untranslatable diagnostic");
+   |                     ^^^^^^^^^^
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs
index 88c41537d9f..8554630e9c9 100644
--- a/tests/ui-fulldeps/stable-mir/check_allocation.rs
+++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs
@@ -33,6 +33,7 @@ use std::ascii::Char;
 use std::assert_matches::assert_matches;
 use std::cmp::{max, min};
 use std::collections::HashMap;
+use std::ffi::CStr;
 use std::io::Write;
 use std::ops::ControlFlow;
 
@@ -45,6 +46,7 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
     check_foo(*get_item(&items, (ItemKind::Static, "FOO")).unwrap());
     check_bar(*get_item(&items, (ItemKind::Static, "BAR")).unwrap());
     check_len(*get_item(&items, (ItemKind::Static, "LEN")).unwrap());
+    check_cstr(*get_item(&items, (ItemKind::Static, "C_STR")).unwrap());
     check_other_consts(*get_item(&items, (ItemKind::Fn, "other_consts")).unwrap());
     check_type_id(*get_item(&items, (ItemKind::Fn, "check_type_id")).unwrap());
     ControlFlow::Continue(())
@@ -86,6 +88,24 @@ fn check_bar(item: CrateItem) {
     assert_eq!(std::str::from_utf8(&allocation.raw_bytes().unwrap()), Ok("Bar"));
 }
 
+/// Check the allocation data for static `C_STR`.
+///
+/// ```no_run
+/// static C_STR: &core::ffi::cstr = c"cstr";
+/// ```
+fn check_cstr(item: CrateItem) {
+    let def = StaticDef::try_from(item).unwrap();
+    let alloc = def.eval_initializer().unwrap();
+    assert_eq!(alloc.provenance.ptrs.len(), 1);
+    let deref = item.ty().kind().builtin_deref(true).unwrap();
+    assert!(deref.ty.kind().is_cstr(), "Expected CStr, but got: {:?}", item.ty());
+
+    let alloc_id_0 = alloc.provenance.ptrs[0].1.0;
+    let GlobalAlloc::Memory(allocation) = GlobalAlloc::from(alloc_id_0) else { unreachable!() };
+    assert_eq!(allocation.bytes.len(), 5);
+    assert_eq!(CStr::from_bytes_until_nul(&allocation.raw_bytes().unwrap()), Ok(c"cstr"));
+}
+
 /// Check the allocation data for constants used in `other_consts` function.
 fn check_other_consts(item: CrateItem) {
     // Instance body will force constant evaluation.
@@ -206,6 +226,7 @@ fn main() {
     generate_input(&path).unwrap();
     let args = vec![
         "rustc".to_string(),
+        "--edition=2021".to_string(),
         "--crate-name".to_string(),
         CRATE_NAME.to_string(),
         path.to_string(),
@@ -224,6 +245,7 @@ fn generate_input(path: &str) -> std::io::Result<()> {
     static LEN: usize = 2;
     static FOO: [&str; 2] = ["hi", "there"];
     static BAR: &str = "Bar";
+    static C_STR: &std::ffi::CStr = c"cstr";
     const NULL: *const u8 = std::ptr::null();
     const TUPLE: (u32, u32) = (10, u32::MAX);
 
diff --git a/tests/ui-fulldeps/stable-mir/check_defs.rs b/tests/ui-fulldeps/stable-mir/check_defs.rs
index d311be5982d..ad667511332 100644
--- a/tests/ui-fulldeps/stable-mir/check_defs.rs
+++ b/tests/ui-fulldeps/stable-mir/check_defs.rs
@@ -69,7 +69,7 @@ fn extract_elem_ty(ty: Ty) -> Ty {
 
 /// Check signature and type of `Vec::<u8>::new` and its generic version.
 fn test_vec_new(instance: mir::mono::Instance) {
-    let sig = instance.fn_sig();
+    let sig = instance.ty().kind().fn_sig().unwrap().skip_binder();
     assert_matches!(sig.inputs(), &[]);
     let elem_ty = extract_elem_ty(sig.output());
     assert_matches!(elem_ty.kind(), TyKind::RigidTy(RigidTy::Uint(UintTy::U8)));
diff --git a/tests/ui/closures/2229_closure_analysis/issue-118144.rs b/tests/ui/closures/2229_closure_analysis/issue-118144.rs
new file mode 100644
index 00000000000..3e5d9f9739a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-118144.rs
@@ -0,0 +1,16 @@
+// Regression test for ICE #118144
+
+struct V(i32);
+
+fn func(func_arg: &mut V) {
+    || {
+        // Declaring `x` separately instead of using
+        // a destructuring binding like `let V(x) = ...`
+        // becaue only `V(x) = ...` triggers the ICE
+        let x;
+        V(x) = func_arg; //~ ERROR: mismatched types
+        func_arg.0 = 0;
+     };
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/issue-118144.stderr b/tests/ui/closures/2229_closure_analysis/issue-118144.stderr
new file mode 100644
index 00000000000..85cb5adc07e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-118144.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-118144.rs:11:9
+   |
+LL |         V(x) = func_arg;
+   |         ^^^^   -------- this expression has type `&mut V`
+   |         |
+   |         expected `&mut V`, found `V`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr
index 69f541cba05..f64e8b39798 100644
--- a/tests/ui/coherence/occurs-check/associated-type.next.stderr
+++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr
@@ -1,11 +1,11 @@
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
 error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())`
   --> $DIR/associated-type.rs:31:1
    |
diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr
index c2c951af0db..8e852ec796e 100644
--- a/tests/ui/coherence/occurs-check/associated-type.old.stderr
+++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr
@@ -1,11 +1,11 @@
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
-WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
 error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)`
   --> $DIR/associated-type.rs:31:1
    |
diff --git a/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
new file mode 100644
index 00000000000..aac74d3eacb
--- /dev/null
+++ b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
@@ -0,0 +1,17 @@
+// compile-flags: --edition 2024 -Zunstable-options
+// check-pass
+
+#![feature(async_iterator, gen_blocks, noop_waker)]
+
+use std::{async_iter::AsyncIterator, pin::pin, task::{Context, Waker}};
+
+async gen fn gen_fn() -> &'static str {
+    yield "hello"
+}
+
+pub fn main() {
+    let async_iterator = pin!(gen_fn());
+    let waker = Waker::noop();
+    let ctx = &mut Context::from_waker(&waker);
+    async_iterator.poll_next(ctx);
+}
diff --git a/tests/ui/coroutine/return-types-diverge.rs b/tests/ui/coroutine/return-types-diverge.rs
new file mode 100644
index 00000000000..5f21c8cbf34
--- /dev/null
+++ b/tests/ui/coroutine/return-types-diverge.rs
@@ -0,0 +1,20 @@
+// compile-flags: --edition 2024 -Zunstable-options
+// check-pass
+
+#![feature(gen_blocks)]
+
+fn diverge() -> ! { loop {} }
+
+async gen fn async_gen_fn() -> i32 { diverge() }
+
+gen fn gen_fn() -> i32 { diverge() }
+
+fn async_gen_block() {
+    async gen { yield (); diverge() };
+}
+
+fn gen_block() {
+    gen { yield (); diverge() };
+}
+
+fn main() {}
diff --git a/tests/ui/coroutine/return-types.rs b/tests/ui/coroutine/return-types.rs
new file mode 100644
index 00000000000..3543d6293f7
--- /dev/null
+++ b/tests/ui/coroutine/return-types.rs
@@ -0,0 +1,21 @@
+// compile-flags: --edition 2024 -Zunstable-options
+
+#![feature(gen_blocks)]
+
+async gen fn async_gen_fn() -> i32 { 0 }
+//~^ ERROR mismatched types
+
+gen fn gen_fn() -> i32 { 0 }
+//~^ ERROR mismatched types
+
+fn async_gen_block() {
+    async gen { yield (); 1 };
+    //~^ ERROR mismatched types
+}
+
+fn gen_block() {
+    gen { yield (); 1 };
+    //~^ ERROR mismatched types
+}
+
+fn main() {}
diff --git a/tests/ui/coroutine/return-types.stderr b/tests/ui/coroutine/return-types.stderr
new file mode 100644
index 00000000000..7be96e538d9
--- /dev/null
+++ b/tests/ui/coroutine/return-types.stderr
@@ -0,0 +1,31 @@
+error[E0308]: mismatched types
+  --> $DIR/return-types.rs:5:38
+   |
+LL | async gen fn async_gen_fn() -> i32 { 0 }
+   |                                ---   ^ expected `()`, found integer
+   |                                |
+   |                                expected `()` because of return type
+
+error[E0308]: mismatched types
+  --> $DIR/return-types.rs:8:26
+   |
+LL | gen fn gen_fn() -> i32 { 0 }
+   |                    ---   ^ expected `()`, found integer
+   |                    |
+   |                    expected `()` because of return type
+
+error[E0308]: mismatched types
+  --> $DIR/return-types.rs:12:27
+   |
+LL |     async gen { yield (); 1 };
+   |                           ^ expected `()`, found integer
+
+error[E0308]: mismatched types
+  --> $DIR/return-types.rs:17:21
+   |
+LL |     gen { yield (); 1 };
+   |                     ^ expected `()`, found integer
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/duplicate_entry_error.stderr b/tests/ui/duplicate_entry_error.stderr
index 592640a884c..3b5998df353 100644
--- a/tests/ui/duplicate_entry_error.stderr
+++ b/tests/ui/duplicate_entry_error.stderr
@@ -1,8 +1,11 @@
 error[E0152]: found duplicate lang item `panic_impl`
   --> $DIR/duplicate_entry_error.rs:11:1
    |
-LL | fn panic_impl(info: &PanicInfo) -> ! {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | / fn panic_impl(info: &PanicInfo) -> ! {
+LL | |
+LL | |     loop {}
+LL | | }
+   | |_^
    |
    = note: the lang item is first defined in crate `std` (which `duplicate_entry_error` depends on)
    = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib
diff --git a/tests/ui/entry-point/issue-118772.rs b/tests/ui/entry-point/issue-118772.rs
new file mode 100644
index 00000000000..1a4173e1252
--- /dev/null
+++ b/tests/ui/entry-point/issue-118772.rs
@@ -0,0 +1,3 @@
+fn main(_: &i32) { //~ ERROR `main` function has wrong type
+    println!("Hello, world!");
+}
diff --git a/tests/ui/entry-point/issue-118772.stderr b/tests/ui/entry-point/issue-118772.stderr
new file mode 100644
index 00000000000..cc33427f59d
--- /dev/null
+++ b/tests/ui/entry-point/issue-118772.stderr
@@ -0,0 +1,12 @@
+error[E0580]: `main` function has wrong type
+  --> $DIR/issue-118772.rs:1:1
+   |
+LL | fn main(_: &i32) {
+   | ^^^^^^^^^^^^^^^^ incorrect number of function parameters
+   |
+   = note: expected signature `fn()`
+              found signature `for<'a> fn(&'a i32)`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0580`.
diff --git a/tests/ui/error-codes/E0152.stderr b/tests/ui/error-codes/E0152.stderr
index 816ba2d569b..dbea7e6d27f 100644
--- a/tests/ui/error-codes/E0152.stderr
+++ b/tests/ui/error-codes/E0152.stderr
@@ -2,7 +2,7 @@ error[E0152]: found duplicate lang item `owned_box`
   --> $DIR/E0152.rs:5:1
    |
 LL | struct Foo<T>(T);
-   | ^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^
    |
    = note: the lang item is first defined in crate `alloc` (which `std` depends on)
    = note: first definition in `alloc` loaded from SYSROOT/liballoc-*.rlib
diff --git a/tests/ui/error-codes/E0264.stderr b/tests/ui/error-codes/E0264.stderr
index 14a4ffbe316..3503fb229e4 100644
--- a/tests/ui/error-codes/E0264.stderr
+++ b/tests/ui/error-codes/E0264.stderr
@@ -2,7 +2,7 @@ error[E0264]: unknown external lang item: `cake`
   --> $DIR/E0264.rs:5:5
    |
 LL |     fn cake();
-   |     ^^^^^^^^^
+   |     ^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/hygiene/cross-crate-define-and-use.rs b/tests/ui/hygiene/cross-crate-define-and-use.rs
index 94f1adff626..62b1820235c 100644
--- a/tests/ui/hygiene/cross-crate-define-and-use.rs
+++ b/tests/ui/hygiene/cross-crate-define-and-use.rs
@@ -6,7 +6,6 @@
 // check-pass
 // aux-build:use_by_macro.rs
 
-#![feature(type_name_of_val)]
 extern crate use_by_macro;
 
 use use_by_macro::*;
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs
index 6ccbb5bb266..280856805e5 100644
--- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs
@@ -1,5 +1,3 @@
-#![deny(implied_bounds_entailment)]
-
 trait Project {
     type Ty;
 }
@@ -11,8 +9,7 @@ trait Trait {
 }
 impl Trait for () {
     fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
-        //~^ ERROR impl method assumes more implied bounds than the corresponding trait method
-        //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+        //~^ ERROR cannot infer an appropriate lifetime for lifetime parameter 's in generic type due to conflicting requirements
         s
     }
 }
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr
index 86b12cf8fa2..c8d1614a7f3 100644
--- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr
@@ -1,31 +1,28 @@
-error: impl method assumes more implied bounds than the corresponding trait method
-  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 's in generic type due to conflicting requirements
+  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
    |
 LL |     fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
-note: the lint level is defined here
-  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
-   |
-LL | #![deny(implied_bounds_entailment)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 1 previous error
-
-Future incompatibility report: Future breakage diagnostic:
-error: impl method assumes more implied bounds than the corresponding trait method
-  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
+note: first, the lifetime cannot outlive the lifetime `'s` as defined here...
+  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:12
    |
 LL |     fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
+   |            ^^
+note: ...so that the method type is compatible with trait
+  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
-note: the lint level is defined here
-  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
+LL |     fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: expected `fn(&'s _, ()) -> &'static _`
+              found `fn(&_, ()) -> &'static _`
+   = note: but, the lifetime must be valid for the static lifetime...
+note: ...so that the reference type `&'static &()` does not outlive the data it points at
+  --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
    |
-LL | #![deny(implied_bounds_entailment)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0495`.
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs
index d097bc16a22..7f3817b326a 100644
--- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs
@@ -1,5 +1,3 @@
-#![deny(implied_bounds_entailment)]
-
 use std::cell::RefCell;
 
 pub struct MessageListeners<'a> {
@@ -12,8 +10,7 @@ pub trait MessageListenersInterface {
 
 impl<'a> MessageListenersInterface for MessageListeners<'a> {
     fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
-        //~^ ERROR impl method assumes more implied bounds than the corresponding trait method
-        //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+        //~^ ERROR cannot infer an appropriate lifetime for lifetime parameter 'b in generic type due to conflicting requirements
         self
     }
 }
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr
index a89645c128b..6a412eb5e4b 100644
--- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr
@@ -1,31 +1,32 @@
-error: impl method assumes more implied bounds than the corresponding trait method
-  --> $DIR/impl-implied-bounds-compatibility.rs:14:35
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'b in generic type due to conflicting requirements
+  --> $DIR/impl-implied-bounds-compatibility.rs:12:5
    |
 LL |     fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
-note: the lint level is defined here
-  --> $DIR/impl-implied-bounds-compatibility.rs:1:9
+note: first, the lifetime cannot outlive the lifetime `'c` as defined here...
+  --> $DIR/impl-implied-bounds-compatibility.rs:12:5
    |
-LL | #![deny(implied_bounds_entailment)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 1 previous error
-
-Future incompatibility report: Future breakage diagnostic:
-error: impl method assumes more implied bounds than the corresponding trait method
-  --> $DIR/impl-implied-bounds-compatibility.rs:14:35
+LL |     fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...so that the method type is compatible with trait
+  --> $DIR/impl-implied-bounds-compatibility.rs:12:5
    |
 LL |     fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: expected `fn(&'c MessageListeners<'_>) -> &'c MessageListeners<'c>`
+              found `fn(&MessageListeners<'_>) -> &'a MessageListeners<'_>`
+note: but, the lifetime must be valid for the lifetime `'a` as defined here...
+  --> $DIR/impl-implied-bounds-compatibility.rs:11:6
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
-note: the lint level is defined here
-  --> $DIR/impl-implied-bounds-compatibility.rs:1:9
+LL | impl<'a> MessageListenersInterface for MessageListeners<'a> {
+   |      ^^
+note: ...so that the reference type `&'a MessageListeners<'_>` does not outlive the data it points at
+  --> $DIR/impl-implied-bounds-compatibility.rs:12:5
    |
-LL | #![deny(implied_bounds_entailment)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0495`.
diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr
index bf91dc72cc1..85e22178c4a 100644
--- a/tests/ui/intrinsics/const-eval-select-bad.stderr
+++ b/tests/ui/intrinsics/const-eval-select-bad.stderr
@@ -16,20 +16,24 @@ LL |     const_eval_select((), || {}, || {});
    = note: expected a function item, found {closure@$DIR/const-eval-select-bad.rs:7:34: 7:36}
    = help: consult the documentation on `const_eval_select` for more information
 
-error: this argument must be a function item
+error[E0277]: expected a `FnOnce()` closure, found `{integer}`
   --> $DIR/const-eval-select-bad.rs:10:27
    |
 LL |     const_eval_select((), 42, 0xDEADBEEF);
-   |                           ^^
+   |     -----------------     ^^ expected an `FnOnce()` closure, found `{integer}`
+   |     |
+   |     required by a bound introduced by this call
    |
-   = note: expected a function item, found {integer}
-   = help: consult the documentation on `const_eval_select` for more information
+   = help: the trait `FnOnce<()>` is not implemented for `{integer}`
+   = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
+note: required by a bound in `const_eval_select`
+  --> $SRC_DIR/core/src/intrinsics.rs:LL:COL
 
 error[E0277]: expected a `FnOnce()` closure, found `{integer}`
-  --> $DIR/const-eval-select-bad.rs:10:27
+  --> $DIR/const-eval-select-bad.rs:10:31
    |
 LL |     const_eval_select((), 42, 0xDEADBEEF);
-   |     -----------------     ^^ expected an `FnOnce()` closure, found `{integer}`
+   |     -----------------         ^^^^^^^^^^ expected an `FnOnce()` closure, found `{integer}`
    |     |
    |     required by a bound introduced by this call
    |
@@ -39,26 +43,22 @@ note: required by a bound in `const_eval_select`
   --> $SRC_DIR/core/src/intrinsics.rs:LL:COL
 
 error: this argument must be a function item
-  --> $DIR/const-eval-select-bad.rs:10:31
+  --> $DIR/const-eval-select-bad.rs:10:27
    |
 LL |     const_eval_select((), 42, 0xDEADBEEF);
-   |                               ^^^^^^^^^^
+   |                           ^^
    |
    = note: expected a function item, found {integer}
    = help: consult the documentation on `const_eval_select` for more information
 
-error[E0277]: expected a `FnOnce()` closure, found `{integer}`
+error: this argument must be a function item
   --> $DIR/const-eval-select-bad.rs:10:31
    |
 LL |     const_eval_select((), 42, 0xDEADBEEF);
-   |     -----------------         ^^^^^^^^^^ expected an `FnOnce()` closure, found `{integer}`
-   |     |
-   |     required by a bound introduced by this call
+   |                               ^^^^^^^^^^
    |
-   = help: the trait `FnOnce<()>` is not implemented for `{integer}`
-   = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
-note: required by a bound in `const_eval_select`
-  --> $SRC_DIR/core/src/intrinsics.rs:LL:COL
+   = note: expected a function item, found {integer}
+   = help: consult the documentation on `const_eval_select` for more information
 
 error[E0271]: expected `bar` to be a fn item that returns `i32`, but it returns `bool`
   --> $DIR/const-eval-select-bad.rs:32:34
diff --git a/tests/ui/lang-items/lang-item-generic-requirements.rs b/tests/ui/lang-items/lang-item-generic-requirements.rs
index 3d33adf6831..21bd7187e15 100644
--- a/tests/ui/lang-items/lang-item-generic-requirements.rs
+++ b/tests/ui/lang-items/lang-item-generic-requirements.rs
@@ -22,6 +22,8 @@ trait MyIndex<'a, T> {}
 #[lang = "phantom_data"]
 //~^ ERROR `phantom_data` language item must be applied to a struct with 1 generic argument
 struct MyPhantomData<T, U>;
+//~^ ERROR parameter `T` is never used
+//~| ERROR parameter `U` is never used
 
 #[lang = "owned_box"]
 //~^ ERROR `owned_box` language item must be applied to a struct with at least 1 generic argument
@@ -40,6 +42,7 @@ fn ice() {
     let r = 5;
     let a = 6;
     r + a;
+    //~^ ERROR cannot add `{integer}` to `{integer}`
 
     // Use drop in place
     my_ptr_drop();
diff --git a/tests/ui/lang-items/lang-item-generic-requirements.stderr b/tests/ui/lang-items/lang-item-generic-requirements.stderr
index 4d349a25f9c..8072e6797e4 100644
--- a/tests/ui/lang-items/lang-item-generic-requirements.stderr
+++ b/tests/ui/lang-items/lang-item-generic-requirements.stderr
@@ -33,7 +33,7 @@ LL | struct MyPhantomData<T, U>;
    |                     ------ this struct has 2 generic arguments
 
 error[E0718]: `owned_box` language item must be applied to a struct with at least 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:26:1
+  --> $DIR/lang-item-generic-requirements.rs:28:1
    |
 LL | #[lang = "owned_box"]
    | ^^^^^^^^^^^^^^^^^^^^^
@@ -42,7 +42,7 @@ LL | struct Foo;
    |           - this struct has 0 generic arguments
 
 error[E0718]: `start` language item must be applied to a function with 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:32:1
+  --> $DIR/lang-item-generic-requirements.rs:34:1
    |
 LL | #[lang = "start"]
    | ^^^^^^^^^^^^^^^^^
@@ -50,6 +50,35 @@ LL |
 LL | fn start(_: *const u8, _: isize, _: *const *const u8) -> isize {
    |         - this function has 0 generic arguments
 
-error: aborting due to 6 previous errors
+error[E0392]: parameter `T` is never used
+  --> $DIR/lang-item-generic-requirements.rs:24:22
+   |
+LL | struct MyPhantomData<T, U>;
+   |                      ^ unused parameter
+   |
+   = help: consider removing `T` or referring to it in a field
+   = help: if you intended `T` to be a const parameter, use `const T: usize` instead
+
+error[E0392]: parameter `U` is never used
+  --> $DIR/lang-item-generic-requirements.rs:24:25
+   |
+LL | struct MyPhantomData<T, U>;
+   |                         ^ unused parameter
+   |
+   = help: consider removing `U` or referring to it in a field
+   = help: if you intended `U` to be a const parameter, use `const U: usize` instead
+
+error[E0369]: cannot add `{integer}` to `{integer}`
+  --> $DIR/lang-item-generic-requirements.rs:44:7
+   |
+LL |     r + a;
+   |     - ^ - {integer}
+   |     |
+   |     {integer}
+
+error: requires `copy` lang_item
+
+error: aborting due to 10 previous errors
 
-For more information about this error, try `rustc --explain E0718`.
+Some errors have detailed explanations: E0369, E0392, E0718.
+For more information about an error, try `rustc --explain E0369`.
diff --git a/tests/ui/lint/ptr_null_checks.rs b/tests/ui/lint/ptr_null_checks.rs
index 3028084e962..4925019be1e 100644
--- a/tests/ui/lint/ptr_null_checks.rs
+++ b/tests/ui/lint/ptr_null_checks.rs
@@ -1,7 +1,5 @@
 // check-pass
 
-#![feature(ptr_from_ref)]
-
 use std::ptr;
 
 extern "C" fn c_fn() {}
diff --git a/tests/ui/lint/ptr_null_checks.stderr b/tests/ui/lint/ptr_null_checks.stderr
index 0edc1b86536..70a27790c5b 100644
--- a/tests/ui/lint/ptr_null_checks.stderr
+++ b/tests/ui/lint/ptr_null_checks.stderr
@@ -1,5 +1,5 @@
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:14:8
+  --> $DIR/ptr_null_checks.rs:12:8
    |
 LL |     if (fn_ptr as *mut ()).is_null() {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^
@@ -10,7 +10,7 @@ LL |     if (fn_ptr as *mut ()).is_null() {}
    = note: `#[warn(useless_ptr_null_checks)]` on by default
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:16:8
+  --> $DIR/ptr_null_checks.rs:14:8
    |
 LL |     if (fn_ptr as *const u8).is_null() {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^^^
@@ -20,7 +20,7 @@ LL |     if (fn_ptr as *const u8).is_null() {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:18:8
+  --> $DIR/ptr_null_checks.rs:16:8
    |
 LL |     if (fn_ptr as *const ()) == std::ptr::null() {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -30,7 +30,7 @@ LL |     if (fn_ptr as *const ()) == std::ptr::null() {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:20:8
+  --> $DIR/ptr_null_checks.rs:18:8
    |
 LL |     if (fn_ptr as *mut ()) == std::ptr::null_mut() {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     if (fn_ptr as *mut ()) == std::ptr::null_mut() {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:22:8
+  --> $DIR/ptr_null_checks.rs:20:8
    |
 LL |     if (fn_ptr as *const ()) == (0 as *const ()) {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -50,7 +50,7 @@ LL |     if (fn_ptr as *const ()) == (0 as *const ()) {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:24:8
+  --> $DIR/ptr_null_checks.rs:22:8
    |
 LL |     if <*const _>::is_null(fn_ptr as *const ()) {}
    |        ^^^^^^^^^^^^^^^^^^^^------^^^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL |     if <*const _>::is_null(fn_ptr as *const ()) {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:26:8
+  --> $DIR/ptr_null_checks.rs:24:8
    |
 LL |     if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -70,7 +70,7 @@ LL |     if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:28:8
+  --> $DIR/ptr_null_checks.rs:26:8
    |
 LL |     if (fn_ptr as *mut fn() as *const fn()).cast_mut().is_null() {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +80,7 @@ LL |     if (fn_ptr as *mut fn() as *const fn()).cast_mut().is_null() {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:30:8
+  --> $DIR/ptr_null_checks.rs:28:8
    |
 LL |     if ((fn_ptr as *mut fn()).cast() as *const fn()).cast_mut().is_null() {}
    |        ^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -90,7 +90,7 @@ LL |     if ((fn_ptr as *mut fn()).cast() as *const fn()).cast_mut().is_null() {
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:32:8
+  --> $DIR/ptr_null_checks.rs:30:8
    |
 LL |     if (fn_ptr as fn() as *const ()).is_null() {}
    |        ^--------------^^^^^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +100,7 @@ LL |     if (fn_ptr as fn() as *const ()).is_null() {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: function pointers are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:34:8
+  --> $DIR/ptr_null_checks.rs:32:8
    |
 LL |     if (c_fn as *const fn()).is_null() {}
    |        ^----^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL |     if (c_fn as *const fn()).is_null() {}
    = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:38:8
+  --> $DIR/ptr_null_checks.rs:36:8
    |
 LL |     if (&mut 8 as *mut i32).is_null() {}
    |        ^------^^^^^^^^^^^^^^^^^^^^^^^
@@ -118,13 +118,13 @@ LL |     if (&mut 8 as *mut i32).is_null() {}
    |         expression has type `&mut i32`
 
 warning: returned pointer of `from_mut` call is never null, so checking it for null will always return false
-  --> $DIR/ptr_null_checks.rs:40:8
+  --> $DIR/ptr_null_checks.rs:38:8
    |
 LL |     if ptr::from_mut(&mut 8).is_null() {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:42:8
+  --> $DIR/ptr_null_checks.rs:40:8
    |
 LL |     if (&8 as *const i32).is_null() {}
    |        ^--^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,25 +132,25 @@ LL |     if (&8 as *const i32).is_null() {}
    |         expression has type `&i32`
 
 warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
-  --> $DIR/ptr_null_checks.rs:44:8
+  --> $DIR/ptr_null_checks.rs:42:8
    |
 LL |     if ptr::from_ref(&8).is_null() {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
-  --> $DIR/ptr_null_checks.rs:46:8
+  --> $DIR/ptr_null_checks.rs:44:8
    |
 LL |     if ptr::from_ref(&8).cast_mut().is_null() {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
-  --> $DIR/ptr_null_checks.rs:48:8
+  --> $DIR/ptr_null_checks.rs:46:8
    |
 LL |     if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:50:8
+  --> $DIR/ptr_null_checks.rs:48:8
    |
 LL |     if (&8 as *const i32) == std::ptr::null() {}
    |        ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -158,7 +158,7 @@ LL |     if (&8 as *const i32) == std::ptr::null() {}
    |         expression has type `&i32`
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:53:8
+  --> $DIR/ptr_null_checks.rs:51:8
    |
 LL |     if (ref_num as *const i32) == std::ptr::null() {}
    |        ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -166,7 +166,7 @@ LL |     if (ref_num as *const i32) == std::ptr::null() {}
    |         expression has type `&i32`
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:55:8
+  --> $DIR/ptr_null_checks.rs:53:8
    |
 LL |     if (b"\0" as *const u8).is_null() {}
    |        ^-----^^^^^^^^^^^^^^^^^^^^^^^^
@@ -174,7 +174,7 @@ LL |     if (b"\0" as *const u8).is_null() {}
    |         expression has type `&[u8; 1]`
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:57:8
+  --> $DIR/ptr_null_checks.rs:55:8
    |
 LL |     if ("aa" as *const str).is_null() {}
    |        ^----^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -182,7 +182,7 @@ LL |     if ("aa" as *const str).is_null() {}
    |         expression has type `&str`
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:59:8
+  --> $DIR/ptr_null_checks.rs:57:8
    |
 LL |     if (&[1, 2] as *const i32).is_null() {}
    |        ^-------^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -190,7 +190,7 @@ LL |     if (&[1, 2] as *const i32).is_null() {}
    |         expression has type `&[i32; 2]`
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:61:8
+  --> $DIR/ptr_null_checks.rs:59:8
    |
 LL |     if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {}
    |        ^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -198,7 +198,7 @@ LL |     if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {}
    |         expression has type `&mut [i32; 2]`
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:63:8
+  --> $DIR/ptr_null_checks.rs:61:8
    |
 LL |     if (static_i32() as *const i32).is_null() {}
    |        ^------------^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -206,7 +206,7 @@ LL |     if (static_i32() as *const i32).is_null() {}
    |         expression has type `&i32`
 
 warning: references are not nullable, so checking them for null will always return false
-  --> $DIR/ptr_null_checks.rs:65:8
+  --> $DIR/ptr_null_checks.rs:63:8
    |
 LL |     if (&*{ static_i32() } as *const i32).is_null() {}
    |        ^------------------^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -214,13 +214,13 @@ LL |     if (&*{ static_i32() } as *const i32).is_null() {}
    |         expression has type `&i32`
 
 warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false
-  --> $DIR/ptr_null_checks.rs:69:8
+  --> $DIR/ptr_null_checks.rs:67:8
    |
 LL |     if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false
-  --> $DIR/ptr_null_checks.rs:71:8
+  --> $DIR/ptr_null_checks.rs:69:8
    |
 LL |     if ptr::NonNull::<u8>::dangling().as_ptr().is_null() {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/lint/reference_casting.rs b/tests/ui/lint/reference_casting.rs
index 25e0c75f70d..84267c0af03 100644
--- a/tests/ui/lint/reference_casting.rs
+++ b/tests/ui/lint/reference_casting.rs
@@ -1,7 +1,5 @@
 // check-fail
 
-#![feature(ptr_from_ref)]
-
 extern "C" {
     // N.B., mutability can be easily incorrect in FFI calls -- as
     // in C, the default is mutable pointers.
@@ -116,6 +114,13 @@ unsafe fn assign_to_ref() {
     let value = num as *const i32 as *mut i32;
     *value = 1;
     //~^ ERROR assigning to `&T` is undefined behavior
+    let value = num as *const i32;
+    let value = value as *mut i32;
+    *value = 1;
+    //~^ ERROR assigning to `&T` is undefined behavior
+    let value = num as *const i32 as *mut i32;
+    *value = 1;
+    //~^ ERROR assigning to `&T` is undefined behavior
     let value_rebind = value;
     *value_rebind = 1;
     //~^ ERROR assigning to `&T` is undefined behavior
diff --git a/tests/ui/lint/reference_casting.stderr b/tests/ui/lint/reference_casting.stderr
index 8d5f8da6852..374a58d7b7b 100644
--- a/tests/ui/lint/reference_casting.stderr
+++ b/tests/ui/lint/reference_casting.stderr
@@ -1,5 +1,5 @@
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:19:16
+  --> $DIR/reference_casting.rs:17:16
    |
 LL |     let _num = &mut *(num as *const i32 as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _num = &mut *(num as *const i32 as *mut i32);
    = note: `#[deny(invalid_reference_casting)]` on by default
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:21:16
+  --> $DIR/reference_casting.rs:19:16
    |
 LL |     let _num = &mut *(num as *const i32).cast_mut();
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _num = &mut *(num as *const i32).cast_mut();
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:23:16
+  --> $DIR/reference_casting.rs:21:16
    |
 LL |     let _num = &mut *std::ptr::from_ref(num).cast_mut();
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _num = &mut *std::ptr::from_ref(num).cast_mut();
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:25:16
+  --> $DIR/reference_casting.rs:23:16
    |
 LL |     let _num = &mut *std::ptr::from_ref({ num }).cast_mut();
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _num = &mut *std::ptr::from_ref({ num }).cast_mut();
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:27:16
+  --> $DIR/reference_casting.rs:25:16
    |
 LL |     let _num = &mut *{ std::ptr::from_ref(num) }.cast_mut();
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _num = &mut *{ std::ptr::from_ref(num) }.cast_mut();
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:29:16
+  --> $DIR/reference_casting.rs:27:16
    |
 LL |     let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:31:16
+  --> $DIR/reference_casting.rs:29:16
    |
 LL |     let _num = &mut *(num as *const i32).cast::<i32>().cast_mut();
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _num = &mut *(num as *const i32).cast::<i32>().cast_mut();
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:33:16
+  --> $DIR/reference_casting.rs:31:16
    |
 LL |     let _num = &mut *(num as *const i32).cast::<i32>().cast_mut().cast_const().cast_mut();
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     let _num = &mut *(num as *const i32).cast::<i32>().cast_mut().cast_cons
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:35:16
+  --> $DIR/reference_casting.rs:33:16
    |
 LL |     let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:37:16
+  --> $DIR/reference_casting.rs:35:16
    |
 LL |     let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +80,7 @@ LL |     let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:39:16
+  --> $DIR/reference_casting.rs:37:16
    |
 LL |     let _num = &mut *(std::mem::transmute::<_, *mut i32>(num) as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL |     let _num = &mut *(std::mem::transmute::<_, *mut i32>(num) as *mut i32);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:41:16
+  --> $DIR/reference_casting.rs:39:16
    |
 LL |       let _num = &mut *std::cell::UnsafeCell::raw_get(
    |  ________________^
@@ -100,7 +100,7 @@ LL | |     );
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:47:16
+  --> $DIR/reference_casting.rs:45:16
    |
 LL |     let deferred = num as *const i32 as *mut i32;
    |                    ----------------------------- casting happend here
@@ -110,7 +110,7 @@ LL |     let _num = &mut *deferred;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:50:16
+  --> $DIR/reference_casting.rs:48:16
    |
 LL |     let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32;
    |                    ---------------------------------------------------------------------------- casting happend here
@@ -120,7 +120,7 @@ LL |     let _num = &mut *deferred;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:53:16
+  --> $DIR/reference_casting.rs:51:16
    |
 LL |     let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32;
    |                    ---------------------------------------------------------------------------- casting happend here
@@ -131,7 +131,7 @@ LL |     let _num = &mut *deferred_rebind;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:55:16
+  --> $DIR/reference_casting.rs:53:16
    |
 LL |     let _num = &mut *(num as *const _ as usize as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -139,7 +139,7 @@ LL |     let _num = &mut *(num as *const _ as usize as *mut i32);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:57:16
+  --> $DIR/reference_casting.rs:55:16
    |
 LL |     let _num = &mut *(std::mem::transmute::<_, *mut _>(num as *const i32) as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -147,7 +147,7 @@ LL |     let _num = &mut *(std::mem::transmute::<_, *mut _>(num as *const i32) a
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:64:16
+  --> $DIR/reference_casting.rs:62:16
    |
 LL |     let num = NUM as *const i32 as *mut i32;
    |               ----------------------------- casting happend here
@@ -158,7 +158,7 @@ LL |     let _num = &mut *num;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:68:16
+  --> $DIR/reference_casting.rs:66:16
    |
 LL |     let _num = &mut *(cell as *const _ as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -167,7 +167,7 @@ LL |     let _num = &mut *(cell as *const _ as *mut i32);
    = note: even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:72:9
+  --> $DIR/reference_casting.rs:70:9
    |
 LL |         &mut *((this as *const _) as *mut _)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -175,7 +175,7 @@ LL |         &mut *((this as *const _) as *mut _)
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:77:18
+  --> $DIR/reference_casting.rs:75:18
    |
 LL |         unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -183,7 +183,7 @@ LL |         unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *con
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:82:18
+  --> $DIR/reference_casting.rs:80:18
    |
 LL |         unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -191,7 +191,7 @@ LL |         unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *con
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:92:5
+  --> $DIR/reference_casting.rs:90:5
    |
 LL |     *(a as *const _ as *mut _) = String::from("Replaced");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -199,7 +199,7 @@ LL |     *(a as *const _ as *mut _) = String::from("Replaced");
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:94:5
+  --> $DIR/reference_casting.rs:92:5
    |
 LL |     *(a as *const _ as *mut String) += " world";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -207,7 +207,7 @@ LL |     *(a as *const _ as *mut String) += " world";
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:96:5
+  --> $DIR/reference_casting.rs:94:5
    |
 LL |     *std::ptr::from_ref(num).cast_mut() += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -215,7 +215,7 @@ LL |     *std::ptr::from_ref(num).cast_mut() += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:98:5
+  --> $DIR/reference_casting.rs:96:5
    |
 LL |     *std::ptr::from_ref({ num }).cast_mut() += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -223,7 +223,7 @@ LL |     *std::ptr::from_ref({ num }).cast_mut() += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:100:5
+  --> $DIR/reference_casting.rs:98:5
    |
 LL |     *{ std::ptr::from_ref(num) }.cast_mut() += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -231,7 +231,7 @@ LL |     *{ std::ptr::from_ref(num) }.cast_mut() += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:102:5
+  --> $DIR/reference_casting.rs:100:5
    |
 LL |     *(std::ptr::from_ref({ num }) as *mut i32) += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -239,7 +239,7 @@ LL |     *(std::ptr::from_ref({ num }) as *mut i32) += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:104:5
+  --> $DIR/reference_casting.rs:102:5
    |
 LL |     *std::mem::transmute::<_, *mut i32>(num) += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -247,7 +247,7 @@ LL |     *std::mem::transmute::<_, *mut i32>(num) += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:106:5
+  --> $DIR/reference_casting.rs:104:5
    |
 LL |     *(std::mem::transmute::<_, *mut i32>(num) as *mut i32) += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -255,7 +255,7 @@ LL |     *(std::mem::transmute::<_, *mut i32>(num) as *mut i32) += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:108:5
+  --> $DIR/reference_casting.rs:106:5
    |
 LL | /     std::ptr::write(
 LL | |
@@ -267,7 +267,7 @@ LL | |     );
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:113:5
+  --> $DIR/reference_casting.rs:111:5
    |
 LL |     *((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -276,7 +276,27 @@ LL |     *((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
    = note: even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:117:5
+  --> $DIR/reference_casting.rs:115:5
+   |
+LL |     let value = num as *const i32 as *mut i32;
+   |                 ----------------------------- casting happend here
+LL |     *value = 1;
+   |     ^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
+
+error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
+  --> $DIR/reference_casting.rs:119:5
+   |
+LL |     let value = value as *mut i32;
+   |                 ----------------- casting happend here
+LL |     *value = 1;
+   |     ^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
+
+error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
+  --> $DIR/reference_casting.rs:122:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -286,7 +306,7 @@ LL |     *value = 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:120:5
+  --> $DIR/reference_casting.rs:125:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -297,7 +317,7 @@ LL |     *value_rebind = 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:122:5
+  --> $DIR/reference_casting.rs:127:5
    |
 LL |     *(num as *const i32).cast::<i32>().cast_mut() = 2;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -305,7 +325,7 @@ LL |     *(num as *const i32).cast::<i32>().cast_mut() = 2;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:124:5
+  --> $DIR/reference_casting.rs:129:5
    |
 LL |     *(num as *const _ as usize as *mut i32) = 2;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -313,7 +333,7 @@ LL |     *(num as *const _ as usize as *mut i32) = 2;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:126:5
+  --> $DIR/reference_casting.rs:131:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -324,7 +344,7 @@ LL |     std::ptr::write(value, 2);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:128:5
+  --> $DIR/reference_casting.rs:133:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -335,7 +355,7 @@ LL |     std::ptr::write_unaligned(value, 2);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:130:5
+  --> $DIR/reference_casting.rs:135:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -346,12 +366,12 @@ LL |     std::ptr::write_volatile(value, 2);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:134:9
+  --> $DIR/reference_casting.rs:139:9
    |
 LL |         *(this as *const _ as *mut _) = a;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
-error: aborting due to 40 previous errors
+error: aborting due to 42 previous errors
 
diff --git a/tests/ui/macros/issue-118786.rs b/tests/ui/macros/issue-118786.rs
new file mode 100644
index 00000000000..84af3a65113
--- /dev/null
+++ b/tests/ui/macros/issue-118786.rs
@@ -0,0 +1,16 @@
+// compile-flags: --crate-type lib -O -C debug-assertions=yes
+
+// Regression test for issue 118786
+
+macro_rules! make_macro {
+    ($macro_name:tt) => {
+        macro_rules! $macro_name {
+        //~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon
+        //~| ERROR macro expansion ignores token `{` and any following
+        //~| ERROR cannot find macro `macro_rules` in this scope
+            () => {}
+        }
+    }
+}
+
+make_macro!((meow));
diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr
new file mode 100644
index 00000000000..ca3a40f31c1
--- /dev/null
+++ b/tests/ui/macros/issue-118786.stderr
@@ -0,0 +1,47 @@
+error: macros that expand to items must be delimited with braces or followed by a semicolon
+  --> $DIR/issue-118786.rs:7:22
+   |
+LL |         macro_rules! $macro_name {
+   |                      ^^^^^^^^^^^
+   |
+help: change the delimiters to curly braces
+   |
+LL |         macro_rules! {} {
+   |                      ~          +
+help: add a semicolon
+   |
+LL |         macro_rules! $macro_name; {
+   |                                 +
+
+error: macro expansion ignores token `{` and any following
+  --> $DIR/issue-118786.rs:7:34
+   |
+LL |         macro_rules! $macro_name {
+   |                                  ^
+...
+LL | make_macro!((meow));
+   | ------------------- caused by the macro expansion here
+   |
+   = note: the usage of `make_macro!` is likely invalid in item context
+
+error: cannot find macro `macro_rules` in this scope
+  --> $DIR/issue-118786.rs:7:9
+   |
+LL |         macro_rules! $macro_name {
+   |         ^^^^^^^^^^^
+...
+LL | make_macro!((meow));
+   | ------------------- in this macro invocation
+   |
+note: maybe you have forgotten to define a name for this `macro_rules!`
+  --> $DIR/issue-118786.rs:7:9
+   |
+LL |         macro_rules! $macro_name {
+   |         ^^^^^^^^^^^
+...
+LL | make_macro!((meow));
+   | ------------------- in this macro invocation
+   = note: this error originates in the macro `make_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/panic-handler/panic-handler-duplicate.stderr b/tests/ui/panic-handler/panic-handler-duplicate.stderr
index 5c50b7016b2..299847474cd 100644
--- a/tests/ui/panic-handler/panic-handler-duplicate.stderr
+++ b/tests/ui/panic-handler/panic-handler-duplicate.stderr
@@ -1,14 +1,18 @@
 error[E0152]: found duplicate lang item `panic_impl`
   --> $DIR/panic-handler-duplicate.rs:15:1
    |
-LL | fn panic2(info: &PanicInfo) -> ! {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | / fn panic2(info: &PanicInfo) -> ! {
+LL | |     loop {}
+LL | | }
+   | |_^
    |
 note: the lang item is first defined here
   --> $DIR/panic-handler-duplicate.rs:10:1
    |
-LL | fn panic(info: &PanicInfo) -> ! {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | / fn panic(info: &PanicInfo) -> ! {
+LL | |     loop {}
+LL | | }
+   | |_^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/panic-handler/panic-handler-std.stderr b/tests/ui/panic-handler/panic-handler-std.stderr
index ad9addec8eb..48c216ce27e 100644
--- a/tests/ui/panic-handler/panic-handler-std.stderr
+++ b/tests/ui/panic-handler/panic-handler-std.stderr
@@ -1,8 +1,10 @@
 error[E0152]: found duplicate lang item `panic_impl`
   --> $DIR/panic-handler-std.rs:8:1
    |
-LL | fn panic(info: PanicInfo) -> ! {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | / fn panic(info: PanicInfo) -> ! {
+LL | |     loop {}
+LL | | }
+   | |_^
    |
    = note: the lang item is first defined in crate `std` (which `panic_handler_std` depends on)
    = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib
diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs
new file mode 100644
index 00000000000..7b927c89ba0
--- /dev/null
+++ b/tests/ui/parser/bad-let-else-statement.rs
@@ -0,0 +1,164 @@
+#![feature(inline_const)]
+#![feature(yeet_expr)]
+#![allow(incomplete_features)] // Necessary for now, while explicit_tail_calls is incomplete
+#![feature(explicit_tail_calls)]
+
+fn a() {
+    let foo = {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn b() {
+    let foo = for i in 1..2 {
+        break;
+    } else {
+        //~^ ERROR `for...else` loops are not supported
+        return;
+    };
+}
+
+fn c() {
+    let foo = if true {
+        1
+    } else {
+        0
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn d() {
+    let foo = loop {
+        break;
+    } else {
+        //~^ ERROR loop...else` loops are not supported
+        return;
+    };
+}
+
+fn e() {
+    let foo = match true {
+        true => 1,
+        false => 0
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+struct X {a: i32}
+fn f() {
+    let foo = X {
+        a: 1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn g() {
+    let foo = while false {
+        break;
+    } else {
+        //~^ ERROR `while...else` loops are not supported
+        return;
+    };
+}
+
+fn h() {
+    let foo = const {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn i() {
+    let foo = &{
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn j() {
+    let bar = 0;
+    let foo = bar = {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn k() {
+    let foo = 1 + {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn l() {
+    let foo = 1..{
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn m() {
+    let foo = return {
+        ()
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn n() {
+    let foo = -{
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn o() -> Result<(), ()> {
+    let foo = do yeet {
+        ()
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return Ok(());
+    };
+}
+
+fn p() {
+    let foo = become {
+        ()
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn q() {
+    let foo = |x: i32| {
+        x
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr
new file mode 100644
index 00000000000..7cbda25e417
--- /dev/null
+++ b/tests/ui/parser/bad-let-else-statement.stderr
@@ -0,0 +1,232 @@
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:9:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = ({
+LL |         1
+LL ~     }) else {
+   |
+
+error: `for...else` loops are not supported
+  --> $DIR/bad-let-else-statement.rs:18:7
+   |
+LL |       let foo = for i in 1..2 {
+   |                 --- `else` is attached to this loop
+LL |           break;
+LL |       } else {
+   |  _______^
+LL | |
+LL | |         return;
+LL | |     };
+   | |_____^
+   |
+   = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:29:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (if true {
+LL |         1
+LL |     } else {
+LL |         0
+LL ~     }) else {
+   |
+
+error: `loop...else` loops are not supported
+  --> $DIR/bad-let-else-statement.rs:38:7
+   |
+LL |       let foo = loop {
+   |                 ---- `else` is attached to this loop
+LL |           break;
+LL |       } else {
+   |  _______^
+LL | |
+LL | |         return;
+LL | |     };
+   | |_____^
+   |
+   = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:48:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (match true {
+LL |         true => 1,
+LL |         false => 0
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:58:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (X {
+LL |         a: 1
+LL ~     }) else {
+   |
+
+error: `while...else` loops are not supported
+  --> $DIR/bad-let-else-statement.rs:67:7
+   |
+LL |       let foo = while false {
+   |                 ----- `else` is attached to this loop
+LL |           break;
+LL |       } else {
+   |  _______^
+LL | |
+LL | |         return;
+LL | |     };
+   | |_____^
+   |
+   = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:76:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (const {
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:85:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = &({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:95:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = bar = ({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:104:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = 1 + ({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:113:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = 1..({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:122:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = return ({
+LL |         ()
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:131:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = -({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:140:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = do yeet ({
+LL |         ()
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:149:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = become ({
+LL |         ()
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:158:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = |x: i32| ({
+LL |         x
+LL ~     }) else {
+   |
+
+error: aborting due to 17 previous errors
+
diff --git a/tests/ui/parser/trait-item-with-defaultness-fail-semantic.rs b/tests/ui/parser/defaultness-invalid-places-fail-semantic.rs
index f2d97b7bac3..bff53f66e19 100644
--- a/tests/ui/parser/trait-item-with-defaultness-fail-semantic.rs
+++ b/tests/ui/parser/defaultness-invalid-places-fail-semantic.rs
@@ -10,3 +10,7 @@ trait X {
     default fn f1(); //~ ERROR `default` is only allowed on items in trait impls
     default fn f2() {} //~ ERROR `default` is only allowed on items in trait impls
 }
+
+default const E: u8 = 0; //~ ERROR `default` is only allowed on items in trait impls
+default type F = (); //~ ERROR `default` is only allowed on items in trait impls
+default fn h() {} //~ ERROR `default` is only allowed on items in trait impls
diff --git a/tests/ui/parser/trait-item-with-defaultness-fail-semantic.stderr b/tests/ui/parser/defaultness-invalid-places-fail-semantic.stderr
index be858cd651d..41fad3a5de3 100644
--- a/tests/ui/parser/trait-item-with-defaultness-fail-semantic.stderr
+++ b/tests/ui/parser/defaultness-invalid-places-fail-semantic.stderr
@@ -1,5 +1,5 @@
 error: `default` is only allowed on items in trait impls
-  --> $DIR/trait-item-with-defaultness-fail-semantic.rs:6:5
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:6:5
    |
 LL |     default const A: u8;
    |     -------^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL |     default const A: u8;
    |     `default` because of this
 
 error: `default` is only allowed on items in trait impls
-  --> $DIR/trait-item-with-defaultness-fail-semantic.rs:7:5
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:7:5
    |
 LL |     default const B: u8 = 0;
    |     -------^^^^^^^^^^^^^^^^^
@@ -15,7 +15,7 @@ LL |     default const B: u8 = 0;
    |     `default` because of this
 
 error: `default` is only allowed on items in trait impls
-  --> $DIR/trait-item-with-defaultness-fail-semantic.rs:8:5
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:8:5
    |
 LL |     default type D;
    |     -------^^^^^^^^
@@ -23,7 +23,7 @@ LL |     default type D;
    |     `default` because of this
 
 error: `default` is only allowed on items in trait impls
-  --> $DIR/trait-item-with-defaultness-fail-semantic.rs:9:5
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:9:5
    |
 LL |     default type C: Ord;
    |     -------^^^^^^^^^^^^^
@@ -31,7 +31,7 @@ LL |     default type C: Ord;
    |     `default` because of this
 
 error: `default` is only allowed on items in trait impls
-  --> $DIR/trait-item-with-defaultness-fail-semantic.rs:10:5
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:10:5
    |
 LL |     default fn f1();
    |     -------^^^^^^^^^
@@ -39,15 +39,39 @@ LL |     default fn f1();
    |     `default` because of this
 
 error: `default` is only allowed on items in trait impls
-  --> $DIR/trait-item-with-defaultness-fail-semantic.rs:11:5
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:11:5
    |
 LL |     default fn f2() {}
    |     -------^^^^^^^^
    |     |
    |     `default` because of this
 
+error: `default` is only allowed on items in trait impls
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:14:1
+   |
+LL | default const E: u8 = 0;
+   | -------^^^^^^^^^^^^^^^^^
+   | |
+   | `default` because of this
+
+error: `default` is only allowed on items in trait impls
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:15:1
+   |
+LL | default type F = ();
+   | -------^^^^^^^^^^^^^
+   | |
+   | `default` because of this
+
+error: `default` is only allowed on items in trait impls
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:16:1
+   |
+LL | default fn h() {}
+   | -------^^^^^^^
+   | |
+   | `default` because of this
+
 warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/trait-item-with-defaultness-fail-semantic.rs:1:12
+  --> $DIR/defaultness-invalid-places-fail-semantic.rs:1:12
    |
 LL | #![feature(specialization)]
    |            ^^^^^^^^^^^^^^
@@ -56,5 +80,5 @@ LL | #![feature(specialization)]
    = help: consider using `min_specialization` instead, which is more stable and complete
    = note: `#[warn(incomplete_features)]` on by default
 
-error: aborting due to 6 previous errors; 1 warning emitted
+error: aborting due to 9 previous errors; 1 warning emitted
 
diff --git a/tests/ui/proc-macro/auxiliary/env.rs b/tests/ui/proc-macro/auxiliary/env.rs
new file mode 100644
index 00000000000..58bcb08bf06
--- /dev/null
+++ b/tests/ui/proc-macro/auxiliary/env.rs
@@ -0,0 +1,28 @@
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(proc_macro_tracked_env)]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro::tracked_env::var;
+
+#[proc_macro]
+pub fn generate_const(input: TokenStream) -> TokenStream {
+    let the_const = match var("THE_CONST") {
+        Ok(x) if x == "12" => {
+            "const THE_CONST: u32 = 12;"
+        }
+        _ => {
+            "const THE_CONST: u32 = 0;"
+        }
+    };
+    let another = if var("ANOTHER").is_ok() {
+        "const ANOTHER: u32 = 1;"
+    } else {
+        "const ANOTHER: u32 = 2;"
+    };
+    format!("{the_const}{another}").parse().unwrap()
+}
diff --git a/tests/ui/proc-macro/env.rs b/tests/ui/proc-macro/env.rs
new file mode 100644
index 00000000000..1b1d1873eb3
--- /dev/null
+++ b/tests/ui/proc-macro/env.rs
@@ -0,0 +1,17 @@
+// aux-build:env.rs
+// run-pass
+// rustc-env: THE_CONST=1
+// compile-flags: -Zunstable-options --env THE_CONST=12 --env ANOTHER=4
+
+#![crate_name = "foo"]
+
+extern crate env;
+
+use env::generate_const;
+
+generate_const!();
+
+fn main() {
+    assert_eq!(THE_CONST, 12);
+    assert_eq!(ANOTHER, 1);
+}
diff --git a/tests/ui/regions/resolve-re-error-ice.rs b/tests/ui/regions/resolve-re-error-ice.rs
index f37b27a82b3..bf6defb9bae 100644
--- a/tests/ui/regions/resolve-re-error-ice.rs
+++ b/tests/ui/regions/resolve-re-error-ice.rs
@@ -1,8 +1,3 @@
-// check-pass
-
-// Allow this for now, can remove this UI test when this becomes a hard error.
-#![allow(implied_bounds_entailment)]
-
 use std::collections::hash_map::{Keys, HashMap};
 use std::marker::PhantomData;
 
@@ -15,6 +10,7 @@ struct Subject<'a, T, V, R>(PhantomData<(&'a T, V, R)>);
 impl<'a, K, V, R> MapAssertion<'a, K, V, R> for Subject<'a, HashMap<K, V>, (), R>
 {
     fn key_set(&self) -> Subject<'a, Keys<K, V>, (), R> {
+        //~^ ERROR cannot infer an appropriate lifetime for lifetime parameter '_ in generic type due to conflicting requirements
         todo!()
     }
 }
diff --git a/tests/ui/regions/resolve-re-error-ice.stderr b/tests/ui/regions/resolve-re-error-ice.stderr
index e7003e1c32f..41c5f0fa92e 100644
--- a/tests/ui/regions/resolve-re-error-ice.stderr
+++ b/tests/ui/regions/resolve-re-error-ice.stderr
@@ -1,15 +1,37 @@
-Future incompatibility report: Future breakage diagnostic:
-warning: impl method assumes more implied bounds than the corresponding trait method
-  --> $DIR/resolve-re-error-ice.rs:17:16
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter '_ in generic type due to conflicting requirements
+  --> $DIR/resolve-re-error-ice.rs:12:5
    |
 LL |     fn key_set(&self) -> Subject<'a, Keys<K, V>, (), R> {
-   |                ^^^^^     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `Subject<'_, std::collections::hash_map::Keys<'_, K, V>, (), R>`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
-note: the lint level is defined here
-  --> $DIR/resolve-re-error-ice.rs:4:10
+note: first, the lifetime cannot outlive the anonymous lifetime defined here...
+  --> $DIR/resolve-re-error-ice.rs:5:16
    |
-LL | #![allow(implied_bounds_entailment)]
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn key_set(&self) -> Subject<Keys<K, V>, (), R>;
+   |                ^^^^^
+note: ...so that the method type is compatible with trait
+  --> $DIR/resolve-re-error-ice.rs:12:5
+   |
+LL |     fn key_set(&self) -> Subject<'a, Keys<K, V>, (), R> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: expected `fn(&Subject<'_, _, _, _>) -> Subject<'_, std::collections::hash_map::Keys<'_, _, _>, _, _>`
+              found `fn(&Subject<'_, _, _, _>) -> Subject<'a, std::collections::hash_map::Keys<'_, _, _>, _, _>`
+note: but, the lifetime must be valid for the lifetime `'a` as defined here...
+  --> $DIR/resolve-re-error-ice.rs:10:6
+   |
+LL | impl<'a, K, V, R> MapAssertion<'a, K, V, R> for Subject<'a, HashMap<K, V>, (), R>
+   |      ^^
+note: ...so that the type `std::collections::hash_map::Keys<'_, K, V>` will meet its required lifetime bounds...
+  --> $DIR/resolve-re-error-ice.rs:12:5
+   |
+LL |     fn key_set(&self) -> Subject<'a, Keys<K, V>, (), R> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...that is required by this bound
+  --> $DIR/resolve-re-error-ice.rs:8:29
+   |
+LL | struct Subject<'a, T, V, R>(PhantomData<(&'a T, V, R)>);
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0495`.
diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr
index 1a26f34a52d..2c018ca875d 100644
--- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr
+++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr
@@ -1,28 +1,28 @@
-[$DIR/dbg-macro-expected-behavior.rs:22] Unit = Unit
-[$DIR/dbg-macro-expected-behavior.rs:23] a = Unit
-[$DIR/dbg-macro-expected-behavior.rs:29] Point { x: 42, y: 24 } = Point {
+[$DIR/dbg-macro-expected-behavior.rs:22:19] Unit = Unit
+[$DIR/dbg-macro-expected-behavior.rs:23:19] a = Unit
+[$DIR/dbg-macro-expected-behavior.rs:29:24] Point { x: 42, y: 24 } = Point {
     x: 42,
     y: 24,
 }
-[$DIR/dbg-macro-expected-behavior.rs:30] b = Point {
+[$DIR/dbg-macro-expected-behavior.rs:30:24] b = Point {
     x: 42,
     y: 24,
 }
-[$DIR/dbg-macro-expected-behavior.rs:38]
-[$DIR/dbg-macro-expected-behavior.rs:42] &a = NoCopy(
+[$DIR/dbg-macro-expected-behavior.rs:38:17]
+[$DIR/dbg-macro-expected-behavior.rs:42:27] &a = NoCopy(
     1337,
 )
-[$DIR/dbg-macro-expected-behavior.rs:42] dbg!(&a) = NoCopy(
+[$DIR/dbg-macro-expected-behavior.rs:42:22] dbg!(&a) = NoCopy(
     1337,
 )
-[$DIR/dbg-macro-expected-behavior.rs:47] f(&42) = 42
+[$DIR/dbg-macro-expected-behavior.rs:47:18] f(&42) = 42
 before
-[$DIR/dbg-macro-expected-behavior.rs:52] { foo += 1; eprintln!("before"); 7331 } = 7331
-[$DIR/dbg-macro-expected-behavior.rs:60] ("Yeah",) = (
+[$DIR/dbg-macro-expected-behavior.rs:52:22] { foo += 1; eprintln!("before"); 7331 } = 7331
+[$DIR/dbg-macro-expected-behavior.rs:60:27] ("Yeah",) = (
     "Yeah",
 )
-[$DIR/dbg-macro-expected-behavior.rs:63] 1 = 1
-[$DIR/dbg-macro-expected-behavior.rs:63] 2 = 2
-[$DIR/dbg-macro-expected-behavior.rs:67] 1u8 = 1
-[$DIR/dbg-macro-expected-behavior.rs:67] 2u32 = 2
-[$DIR/dbg-macro-expected-behavior.rs:67] "Yeah" = "Yeah"
+[$DIR/dbg-macro-expected-behavior.rs:63:29] 1 = 1
+[$DIR/dbg-macro-expected-behavior.rs:63:29] 2 = 2
+[$DIR/dbg-macro-expected-behavior.rs:67:37] 1u8 = 1
+[$DIR/dbg-macro-expected-behavior.rs:67:37] 2u32 = 2
+[$DIR/dbg-macro-expected-behavior.rs:67:37] "Yeah" = "Yeah"
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs
index 2a2e8cec3f0..59fb48e794c 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs
@@ -1,12 +1,13 @@
 // check-pass
 
 #![crate_type = "lib"]
-#![feature(no_core, lang_items, unboxed_closures, auto_traits, intrinsics, rustc_attrs)]
+#![feature(no_core, lang_items, unboxed_closures, auto_traits, intrinsics, rustc_attrs, staged_api)]
 #![feature(fundamental)]
 #![feature(const_trait_impl, effects, const_mut_refs)]
 #![allow(internal_features)]
 #![no_std]
 #![no_core]
+#![stable(feature = "minicore", since = "1.0.0")]
 
 #[lang = "sized"]
 trait Sized {}
@@ -82,6 +83,7 @@ trait FnMut<Args: Tuple>: ~const FnOnce<Args> {
 #[lang = "fn_once"]
 #[rustc_paren_sugar]
 trait FnOnce<Args: Tuple> {
+    #[lang = "fn_once_output"]
     type Output;
 
     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
@@ -93,7 +95,7 @@ struct ConstFnMutClosure<CapturedData, Function> {
 }
 
 #[lang = "tuple_trait"]
-pub trait Tuple {}
+trait Tuple {}
 
 macro_rules! impl_fn_mut_tuple {
     ($($var:ident)*) => {
@@ -509,3 +511,17 @@ trait StructuralPartialEq {}
 trait StructuralEq {}
 
 const fn drop<T: ~const Destruct>(_: T) {}
+
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
+    fn const_eval_select<ARG: Tuple, F, G, RET>(
+        arg: ARG,
+        called_in_const: F,
+        called_at_rt: G,
+    ) -> RET
+    /* where clauses enforced by built-in method confirmation:
+    where
+        F: const FnOnce<Arg, Output = RET>,
+        G: FnOnce<Arg, Output = RET>,
+     */;
+}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs
index bbe1194f7a3..5ecb75094f0 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs
@@ -52,4 +52,7 @@ trait Child1 where Self: ~const Trait {} //~ ERROR `~const` is not allowed
 // non-const impl
 impl<T: ~const Trait> Trait for T {} //~ ERROR `~const` is not allowed
 
+// inherent impl (regression test for issue #117004)
+impl<T: ~const Trait> Struct<T> {} //~ ERROR `~const` is not allowed
+
 fn main() {}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr
index c6e18924fd8..497ec5bcf84 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr
@@ -194,6 +194,18 @@ note: this impl is not `const`, so it cannot have `~const` trait bounds
 LL | impl<T: ~const Trait> Trait for T {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: `~const` is not allowed here
+  --> $DIR/tilde-const-invalid-places.rs:56:9
+   |
+LL | impl<T: ~const Trait> Struct<T> {}
+   |         ^^^^^^
+   |
+note: inherent impls cannot have `~const` trait bounds
+  --> $DIR/tilde-const-invalid-places.rs:56:1
+   |
+LL | impl<T: ~const Trait> Struct<T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error[E0658]: generic const items are experimental
   --> $DIR/tilde-const-invalid-places.rs:19:15
    |
@@ -239,6 +251,6 @@ LL |     type Type<T: ~const Trait> = ();
    = note: see issue #8995 <https://github.com/rust-lang/rust/issues/8995> for more information
    = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable
 
-error: aborting due to 26 previous errors
+error: aborting due to 27 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs
index fbdc3a4f370..bfd9fe42e67 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs
@@ -1,5 +1,4 @@
-// known-bug: #110395
-// FIXME check-pass
+// check-pass
 #![feature(const_trait_impl, effects)]
 
 #[const_trait]
@@ -9,8 +8,8 @@ trait Foo {
 
 struct Bar<T>(T);
 
-impl<T: ~const Foo> Bar<T> {
-    const fn foo(&self) {
+impl<T> Bar<T> {
+    const fn foo(&self) where T: ~const Foo {
         self.0.foo()
     }
 }
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.stderr
deleted file mode 100644
index 0925bfa7e57..00000000000
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/tilde_const_on_impl_bound.rs:14:9
-   |
-LL |         self.0.foo()
-   |         ^^^^^^^^^^^^ expected `host`, found `true`
-   |
-   = note: expected constant `host`
-              found constant `true`
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs
index 406e5c8441e..490d6a2f918 100644
--- a/tests/ui/structs-enums/type-sizes.rs
+++ b/tests/ui/structs-enums/type-sizes.rs
@@ -4,7 +4,6 @@
 #![allow(dead_code)]
 #![feature(never_type)]
 #![feature(pointer_is_aligned)]
-#![feature(ptr_from_ref)]
 #![feature(strict_provenance)]
 
 use std::mem::size_of;
diff --git a/tests/ui/traits/issue-102989.rs b/tests/ui/traits/issue-102989.rs
index 216cd78e56f..f1ecee0a552 100644
--- a/tests/ui/traits/issue-102989.rs
+++ b/tests/ui/traits/issue-102989.rs
@@ -7,6 +7,7 @@ trait Sized { } //~ ERROR found duplicate lang item `sized`
 fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
     //~^ ERROR `self` parameter is only allowed in associated functions
     //~| ERROR cannot find type `Struct` in this scope
+    //~| ERROR mismatched types
     let x = x << 1;
     //~^ ERROR cannot find value `x` in this scope
 }
diff --git a/tests/ui/traits/issue-102989.stderr b/tests/ui/traits/issue-102989.stderr
index 7d0098fe885..40e49df2b2d 100644
--- a/tests/ui/traits/issue-102989.stderr
+++ b/tests/ui/traits/issue-102989.stderr
@@ -13,7 +13,7 @@ LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
    |                      ^^^^^^ not found in this scope
 
 error[E0425]: cannot find value `x` in this scope
-  --> $DIR/issue-102989.rs:10:13
+  --> $DIR/issue-102989.rs:11:13
    |
 LL |     let x = x << 1;
    |             ^ help: a local variable with a similar name exists: `f`
@@ -22,13 +22,27 @@ error[E0152]: found duplicate lang item `sized`
   --> $DIR/issue-102989.rs:5:1
    |
 LL | trait Sized { }
-   | ^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^
    |
    = note: the lang item is first defined in crate `core` (which `std` depends on)
    = note: first definition in `core` loaded from SYSROOT/libcore-*.rlib
    = note: second definition in the local crate (`issue_102989`)
 
-error: aborting due to 4 previous errors
+error[E0308]: mismatched types
+  --> $DIR/issue-102989.rs:7:42
+   |
+LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
+   |    ----------                            ^^^^ expected `&u32`, found `()`
+   |    |
+   |    implicitly returns `()` as its body has no tail or `return` expression
+   |
+help: consider returning the local binding `f`
+   |
+LL ~     let x = x << 1;
+LL +     f
+   |
+
+error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0152, E0412, E0425.
+Some errors have detailed explanations: E0152, E0308, E0412, E0425.
 For more information about an error, try `rustc --explain E0152`.
diff --git a/tests/ui/traits/issue-117794.rs b/tests/ui/traits/issue-117794.rs
new file mode 100644
index 00000000000..a66d6eb10ed
--- /dev/null
+++ b/tests/ui/traits/issue-117794.rs
@@ -0,0 +1,10 @@
+trait Foo {}
+
+trait T {
+    fn a(&self) -> impl Foo {
+        self.b(|| 0)
+        //~^ ERROR no method named `b` found for reference `&Self` in the current scope
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/traits/issue-117794.stderr b/tests/ui/traits/issue-117794.stderr
new file mode 100644
index 00000000000..af63b47f07d
--- /dev/null
+++ b/tests/ui/traits/issue-117794.stderr
@@ -0,0 +1,9 @@
+error[E0599]: no method named `b` found for reference `&Self` in the current scope
+  --> $DIR/issue-117794.rs:5:14
+   |
+LL |         self.b(|| 0)
+   |              ^ help: there is a method with a similar name: `a`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.rs b/tests/ui/traits/next-solver/issue-118950-root-region.rs
new file mode 100644
index 00000000000..10fb7d525b7
--- /dev/null
+++ b/tests/ui/traits/next-solver/issue-118950-root-region.rs
@@ -0,0 +1,22 @@
+// compile-flags: -Znext-solver
+//
+// This is a gnarly test but I don't know how to minimize it, frankly.
+
+#![feature(lazy_type_alias)]
+//~^ WARN the feature `lazy_type_alias` is incomplete
+
+trait ToUnit<'a> {
+    type Unit;
+}
+
+trait Overlap<T> {}
+
+type Assoc<'a, T> = <*const T as ToUnit<'a>>::Unit;
+
+impl<T> Overlap<T> for T {}
+
+impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {}
+//~^ ERROR conflicting implementations of trait `Overlap<fn(_)>` for type `fn(_)`
+//~| ERROR cannot find type `Missing` in this scope
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr
new file mode 100644
index 00000000000..c16a48d5f15
--- /dev/null
+++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr
@@ -0,0 +1,36 @@
+error[E0412]: cannot find type `Missing` in this scope
+  --> $DIR/issue-118950-root-region.rs:18:55
+   |
+LL | impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {}
+   |                                                       ^^^^^^^ not found in this scope
+
+warning: the feature `lazy_type_alias` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-118950-root-region.rs:5:12
+   |
+LL | #![feature(lazy_type_alias)]
+   |            ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) })
+error[E0119]: conflicting implementations of trait `Overlap<fn(_)>` for type `fn(_)`
+  --> $DIR/issue-118950-root-region.rs:18:1
+   |
+LL | impl<T> Overlap<T> for T {}
+   | ------------------------ first implementation here
+LL |
+LL | impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `fn(_)`
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0119, E0412.
+For more information about an error, try `rustc --explain E0119`.
diff --git a/triagebot.toml b/triagebot.toml
index 30f049c79d6..e4b104cdb86 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -441,6 +441,14 @@ cc = ["@rust-lang/miri"]
 message = "Some changes occurred in need_type_info.rs"
 cc = ["@lcnr"]
 
+[mentions."compiler/rustc_middle/src/ty/relate.rs"]
+message = "Type relation code was changed"
+cc = ["@compiler-errors", "@lcnr"]
+
+[mentions."compiler/rustc_infer/src/infer/relate"]
+message = "Type relation code was changed"
+cc = ["@compiler-errors", "@lcnr"]
+
 [mentions."compiler/rustc_middle/src/mir/interpret"]
 message = "Some changes occurred to the CTFE / Miri engine"
 cc = ["@rust-lang/miri"]
@@ -555,15 +563,15 @@ cc = ["@oli-obk", "@RalfJung", "@JakobDegen", "@davidtwco", "@celinval", "@vakar
 
 [mentions."compiler/rustc_error_messages"]
 message = "`rustc_error_messages` was changed"
-cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
+cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"]
 
 [mentions."compiler/rustc_errors/src/translation.rs"]
 message = "`rustc_errors::translation` was changed"
-cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
+cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"]
 
 [mentions."compiler/rustc_macros/src/diagnostics"]
 message = "`rustc_macros::diagnostics` was changed"
-cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
+cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"]
 
 [mentions."compiler/stable_mir"]
 message = "This PR changes Stable MIR"
@@ -597,19 +605,19 @@ cc = ["@davidtwco", "@wesleywiser"]
 message = """
 This PR modifies `src/bootstrap/src/core/config`.
 
-If appropriate, please update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs` and `change-id` in `config.example.toml`.
+If appropriate, please update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`.
 """
 [mentions."src/bootstrap/defaults"]
 message = """
 This PR modifies `src/bootstrap/defaults`.
 
-If appropriate, please update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs` and `change-id` in `config.example.toml`.
+If appropriate, please update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`.
 """
 [mentions."config.example.toml"]
 message = """
 This PR modifies `config.example.toml`.
 
-If appropriate, please update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs` and `change-id` in `config.example.toml`.
+If appropriate, please update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`.
 """
 
 [mentions."src/bootstrap/defaults/config.compiler.toml"]